summaryrefslogtreecommitdiffstats
path: root/xbmc/settings
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/settings')
-rw-r--r--xbmc/settings/AdvancedSettings.cpp1559
-rw-r--r--xbmc/settings/AdvancedSettings.h391
-rw-r--r--xbmc/settings/CMakeLists.txt49
-rw-r--r--xbmc/settings/DiscSettings.cpp62
-rw-r--r--xbmc/settings/DiscSettings.h34
-rw-r--r--xbmc/settings/DisplaySettings.cpp948
-rw-r--r--xbmc/settings/DisplaySettings.h173
-rw-r--r--xbmc/settings/GameSettings.cpp63
-rw-r--r--xbmc/settings/GameSettings.h44
-rw-r--r--xbmc/settings/ISubSettings.h41
-rw-r--r--xbmc/settings/LibExportSettings.cpp115
-rw-r--r--xbmc/settings/LibExportSettings.h68
-rw-r--r--xbmc/settings/MediaSettings.cpp415
-rw-r--r--xbmc/settings/MediaSettings.h120
-rw-r--r--xbmc/settings/MediaSourceSettings.cpp500
-rw-r--r--xbmc/settings/MediaSourceSettings.h66
-rw-r--r--xbmc/settings/SettingAddon.cpp84
-rw-r--r--xbmc/settings/SettingAddon.h38
-rw-r--r--xbmc/settings/SettingConditions.cpp494
-rw-r--r--xbmc/settings/SettingConditions.h40
-rw-r--r--xbmc/settings/SettingControl.cpp378
-rw-r--r--xbmc/settings/SettingControl.h314
-rw-r--r--xbmc/settings/SettingCreator.cpp28
-rw-r--r--xbmc/settings/SettingCreator.h22
-rw-r--r--xbmc/settings/SettingDateTime.cpp88
-rw-r--r--xbmc/settings/SettingDateTime.h45
-rw-r--r--xbmc/settings/SettingPath.cpp145
-rw-r--r--xbmc/settings/SettingPath.h47
-rw-r--r--xbmc/settings/SettingUtils.cpp140
-rw-r--r--xbmc/settings/SettingUtils.h52
-rw-r--r--xbmc/settings/Settings.cpp1088
-rw-r--r--xbmc/settings/Settings.h598
-rw-r--r--xbmc/settings/SettingsBase.cpp273
-rw-r--r--xbmc/settings/SettingsBase.h279
-rw-r--r--xbmc/settings/SettingsComponent.cpp400
-rw-r--r--xbmc/settings/SettingsComponent.h89
-rw-r--r--xbmc/settings/SettingsValueFlatJsonSerializer.cpp139
-rw-r--r--xbmc/settings/SettingsValueFlatJsonSerializer.h40
-rw-r--r--xbmc/settings/SettingsValueXmlSerializer.cpp101
-rw-r--r--xbmc/settings/SettingsValueXmlSerializer.h36
-rw-r--r--xbmc/settings/SkinSettings.cpp186
-rw-r--r--xbmc/settings/SkinSettings.h62
-rw-r--r--xbmc/settings/SubtitlesSettings.cpp192
-rw-r--r--xbmc/settings/SubtitlesSettings.h211
-rw-r--r--xbmc/settings/dialogs/CMakeLists.txt13
-rw-r--r--xbmc/settings/dialogs/GUIDialogContentSettings.cpp428
-rw-r--r--xbmc/settings/dialogs/GUIDialogContentSettings.h91
-rw-r--r--xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp452
-rw-r--r--xbmc/settings/dialogs/GUIDialogLibExportSettings.h60
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsBase.cpp972
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsBase.h189
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsManagerBase.cpp64
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsManagerBase.h34
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsManualBase.cpp1617
-rw-r--r--xbmc/settings/dialogs/GUIDialogSettingsManualBase.h650
-rw-r--r--xbmc/settings/lib/CMakeLists.txt31
-rw-r--r--xbmc/settings/lib/ISetting.cpp76
-rw-r--r--xbmc/settings/lib/ISetting.h139
-rw-r--r--xbmc/settings/lib/ISettingCallback.h88
-rw-r--r--xbmc/settings/lib/ISettingControl.cpp56
-rw-r--r--xbmc/settings/lib/ISettingControl.h36
-rw-r--r--xbmc/settings/lib/ISettingControlCreator.h32
-rw-r--r--xbmc/settings/lib/ISettingCreator.h35
-rw-r--r--xbmc/settings/lib/ISettingsHandler.h58
-rw-r--r--xbmc/settings/lib/ISettingsValueSerializer.h21
-rw-r--r--xbmc/settings/lib/Setting.cpp1690
-rw-r--r--xbmc/settings/lib/Setting.h536
-rw-r--r--xbmc/settings/lib/SettingCategoryAccess.cpp41
-rw-r--r--xbmc/settings/lib/SettingCategoryAccess.h47
-rw-r--r--xbmc/settings/lib/SettingConditions.cpp153
-rw-r--r--xbmc/settings/lib/SettingConditions.h105
-rw-r--r--xbmc/settings/lib/SettingDefinitions.h138
-rw-r--r--xbmc/settings/lib/SettingDependency.cpp421
-rw-r--r--xbmc/settings/lib/SettingDependency.h135
-rw-r--r--xbmc/settings/lib/SettingLevel.h21
-rw-r--r--xbmc/settings/lib/SettingRequirement.cpp37
-rw-r--r--xbmc/settings/lib/SettingRequirement.h47
-rw-r--r--xbmc/settings/lib/SettingSection.cpp359
-rw-r--r--xbmc/settings/lib/SettingSection.h186
-rw-r--r--xbmc/settings/lib/SettingType.h23
-rw-r--r--xbmc/settings/lib/SettingUpdate.cpp65
-rw-r--r--xbmc/settings/lib/SettingUpdate.h46
-rw-r--r--xbmc/settings/lib/SettingsManager.cpp1424
-rw-r--r--xbmc/settings/lib/SettingsManager.h544
-rw-r--r--xbmc/settings/windows/CMakeLists.txt11
-rw-r--r--xbmc/settings/windows/GUIControlSettings.cpp1798
-rw-r--r--xbmc/settings/windows/GUIControlSettings.h366
-rw-r--r--xbmc/settings/windows/GUIWindowSettings.cpp19
-rw-r--r--xbmc/settings/windows/GUIWindowSettings.h19
-rw-r--r--xbmc/settings/windows/GUIWindowSettingsCategory.cpp244
-rw-r--r--xbmc/settings/windows/GUIWindowSettingsCategory.h51
-rw-r--r--xbmc/settings/windows/GUIWindowSettingsScreenCalibration.cpp630
-rw-r--r--xbmc/settings/windows/GUIWindowSettingsScreenCalibration.h47
93 files changed, 24602 insertions, 0 deletions
diff --git a/xbmc/settings/AdvancedSettings.cpp b/xbmc/settings/AdvancedSettings.cpp
new file mode 100644
index 0000000..4fd93d0
--- /dev/null
+++ b/xbmc/settings/AdvancedSettings.cpp
@@ -0,0 +1,1559 @@
+/*
+ * 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 "AdvancedSettings.h"
+
+#include "LangInfo.h"
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "application/AppParams.h"
+#include "filesystem/SpecialProtocol.h"
+#include "network/DNSNameCache.h"
+#include "profiles/ProfileManager.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/FileUtils.h"
+#include "utils/LangCodeExpander.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <climits>
+#include <regex>
+#include <string>
+#include <vector>
+
+using namespace ADDON;
+
+CAdvancedSettings::CAdvancedSettings()
+{
+ m_initialized = false;
+ m_fullScreen = false;
+}
+
+void CAdvancedSettings::OnSettingsLoaded()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ // load advanced settings
+ Load(*profileManager);
+
+ // default players?
+ CLog::Log(LOGINFO, "Default Video Player: {}", m_videoDefaultPlayer);
+ CLog::Log(LOGINFO, "Default Audio Player: {}", m_audioDefaultPlayer);
+
+ // setup any logging...
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (settings->GetBool(CSettings::SETTING_DEBUG_SHOWLOGINFO))
+ {
+ m_logLevel = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
+ CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting ({})", m_logLevel);
+ }
+ else
+ {
+ m_logLevel = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
+ CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level {}.", m_logLevel);
+ }
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
+}
+
+void CAdvancedSettings::OnSettingsUnloaded()
+{
+ m_initialized = false;
+}
+
+void CAdvancedSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_DEBUG_SHOWLOGINFO)
+ SetDebugMode(std::static_pointer_cast<const CSettingBool>(setting)->GetValue());
+}
+
+void CAdvancedSettings::Initialize(CSettingsManager& settingsMgr)
+{
+ Initialize();
+
+ const auto params = CServiceBroker::GetAppParams();
+
+ if (params->GetLogLevel() == LOG_LEVEL_DEBUG)
+ {
+ m_logLevel = LOG_LEVEL_DEBUG;
+ m_logLevelHint = LOG_LEVEL_DEBUG;
+ CServiceBroker::GetLogging().SetLogLevel(LOG_LEVEL_DEBUG);
+ }
+
+ const std::string& settingsFile = params->GetSettingsFile();
+ if (!settingsFile.empty())
+ AddSettingsFile(settingsFile);
+
+ if (params->IsStartFullScreen())
+ m_startFullScreen = true;
+
+ if (params->IsStandAlone())
+ m_handleMounting = true;
+
+ settingsMgr.RegisterSettingsHandler(this, true);
+ std::set<std::string> settingSet;
+ settingSet.insert(CSettings::SETTING_DEBUG_SHOWLOGINFO);
+ settingsMgr.RegisterCallback(this, settingSet);
+}
+
+void CAdvancedSettings::Uninitialize(CSettingsManager& settingsMgr)
+{
+ settingsMgr.UnregisterCallback(this);
+ settingsMgr.UnregisterSettingsHandler(this);
+ settingsMgr.UnregisterSettingOptionsFiller("loggingcomponents");
+
+ Clear();
+
+ m_initialized = false;
+}
+
+void CAdvancedSettings::Initialize()
+{
+ if (m_initialized)
+ return;
+
+ m_audioApplyDrc = -1.0f;
+ m_VideoPlayerIgnoreDTSinWAV = false;
+
+ //default hold time of 25 ms, this allows a 20 hertz sine to pass undistorted
+ m_limiterHold = 0.025f;
+ m_limiterRelease = 0.1f;
+
+ m_seekSteps = { 10, 30, 60, 180, 300, 600, 1800 };
+
+ m_audioDefaultPlayer = "paplayer";
+ m_audioPlayCountMinimumPercent = 90.0f;
+
+ m_videoSubsDelayRange = 60;
+ m_videoAudioDelayRange = 10;
+ m_videoUseTimeSeeking = true;
+ m_videoTimeSeekForward = 30;
+ m_videoTimeSeekBackward = -30;
+ m_videoTimeSeekForwardBig = 600;
+ m_videoTimeSeekBackwardBig = -600;
+ m_videoPercentSeekForward = 2;
+ m_videoPercentSeekBackward = -2;
+ m_videoPercentSeekForwardBig = 10;
+ m_videoPercentSeekBackwardBig = -10;
+
+ m_videoPPFFmpegPostProc = "ha:128:7,va,dr";
+ m_videoDefaultPlayer = "VideoPlayer";
+ m_videoIgnoreSecondsAtStart = 3*60;
+ m_videoIgnorePercentAtEnd = 8.0f;
+ m_videoPlayCountMinimumPercent = 90.0f;
+ m_videoVDPAUScaling = -1;
+ m_videoNonLinStretchRatio = 0.5f;
+ m_videoAutoScaleMaxFps = 30.0f;
+ m_videoCaptureUseOcclusionQuery = -1; //-1 is auto detect
+ m_videoVDPAUtelecine = false;
+ m_videoVDPAUdeintSkipChromaHD = false;
+ m_DXVACheckCompatibility = false;
+ m_DXVACheckCompatibilityPresent = false;
+ m_videoFpsDetect = 1;
+ m_maxTempo = 1.55f;
+ m_videoPreferStereoStream = false;
+
+ m_videoDefaultLatency = 0.0;
+
+ m_musicUseTimeSeeking = true;
+ m_musicTimeSeekForward = 10;
+ m_musicTimeSeekBackward = -10;
+ m_musicTimeSeekForwardBig = 60;
+ m_musicTimeSeekBackwardBig = -60;
+ m_musicPercentSeekForward = 1;
+ m_musicPercentSeekBackward = -1;
+ m_musicPercentSeekForwardBig = 10;
+ m_musicPercentSeekBackwardBig = -10;
+
+ m_slideshowPanAmount = 2.5f;
+ m_slideshowZoomAmount = 5.0f;
+ m_slideshowBlackBarCompensation = 20.0f;
+
+ m_songInfoDuration = 10;
+
+ m_cddbAddress = "gnudb.gnudb.org";
+ m_addSourceOnTop = false;
+
+ m_handleMounting = CServiceBroker::GetAppParams()->IsStandAlone();
+
+ m_fullScreenOnMovieStart = true;
+ m_cachePath = "special://temp/";
+
+ m_videoCleanDateTimeRegExp = "(.*[^ _\\,\\.\\(\\)\\[\\]\\-])[ _\\.\\(\\)\\[\\]\\-]+(19[0-9][0-9]|20[0-9][0-9])([ _\\,\\.\\(\\)\\[\\]\\-]|[^0-9]$)?";
+
+ m_videoCleanStringRegExps.clear();
+ m_videoCleanStringRegExps.emplace_back(
+ "[ "
+ "_\\,\\.\\(\\)\\[\\]\\-](10bit|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|3d|aac|ac3|"
+ "aka|atmos|avi|bd5|bdrip|bluray|brrip|cam|cd[1-9]|custom|dc|ddp|divx|divx5|dolbydigital|"
+ "dolbyvision|dsr|dsrip|dts|dts-hdma|dts-hra|dts-x|dv|dvd|dvd5|dvd9|dvdivx|dvdrip|dvdscr|"
+ "dvdscreener|extended|fragment|fs|h264|h265|hdr|hdr10|hevc|hddvd|hdrip|hdtv|hdtvrip|hrhd|"
+ "hrhdtv|internal|limited|multisubs|nfofix|ntsc|ogg|ogm|pal|pdtv|proper|r3|r5|read.nfo|"
+ "remastered|remux|repack|rerip|retail|screener|se|svcd|tc|telecine|telesync|truehd|ts|uhd|"
+ "unrated|ws|x264|x265|xvid|xvidvd|xxx|web-dl|webrip|www.www|\\[.*\\])([ "
+ "_\\,\\.\\(\\)\\[\\]\\-]|$)");
+ m_videoCleanStringRegExps.emplace_back("(\\[.*\\])");
+
+ // this vector will be inserted at the end to
+ // m_moviesExcludeFromScanRegExps, m_tvshowExcludeFromScanRegExps and
+ // m_audioExcludeFromScanRegExps
+ m_allExcludeFromScanRegExps.clear();
+ m_allExcludeFromScanRegExps.emplace_back("[\\/].+\\.ite[\\/]"); // ignore itunes extras dir
+ m_allExcludeFromScanRegExps.emplace_back("[\\/]\\.\\_");
+ m_allExcludeFromScanRegExps.emplace_back("\\.DS_Store");
+ m_allExcludeFromScanRegExps.emplace_back("\\.AppleDouble");
+
+ m_moviesExcludeFromScanRegExps.clear();
+ m_moviesExcludeFromScanRegExps.emplace_back("-trailer");
+ m_moviesExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
+ m_moviesExcludeFromScanRegExps.emplace_back("[\\/](proof|subs)[\\/]");
+ m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+
+ m_tvshowExcludeFromScanRegExps.clear();
+ m_tvshowExcludeFromScanRegExps.emplace_back("[!-._ \\\\/]sample[-._ \\\\/]");
+ m_tvshowExcludeFromScanRegExps.insert(m_tvshowExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+
+ m_audioExcludeFromScanRegExps.clear();
+ m_audioExcludeFromScanRegExps.insert(m_audioExcludeFromScanRegExps.end(),
+ m_allExcludeFromScanRegExps.begin(),
+ m_allExcludeFromScanRegExps.end());
+
+ m_folderStackRegExps.clear();
+ m_folderStackRegExps.emplace_back("((cd|dvd|dis[ck])[0-9]+)$");
+
+ m_videoStackRegExps.clear();
+ m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[0-9]+)(.*?)(\\.[^.]+)$");
+ m_videoStackRegExps.emplace_back("(.*?)([ _.-]*(?:cd|dvd|p(?:(?:ar)?t)|dis[ck])[ _.-]*[a-d])(.*?)(\\.[^.]+)$");
+ m_videoStackRegExps.emplace_back("(.*?)([ ._-]*[a-d])(.*?)(\\.[^.]+)$");
+ // This one is a bit too greedy to enable by default. It will stack sequels
+ // in a flat dir structure, but is perfectly safe in a dir-per-vid one.
+ //m_videoStackRegExps.push_back("(.*?)([ ._-]*[0-9])(.*?)(\\.[^.]+)$");
+
+ m_tvshowEnumRegExps.clear();
+ // foo.s01.e01, foo.s01_e01, S01E02 foo, S01 - E02, S01xE02
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"s([0-9]+)[ ._x-]*e([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // foo.ep01, foo.EP_01, foo.E01
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(
+ false, "[\\._ -]?()e(?:p[ ._-]?)?([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // foo.yyyy.mm.dd.* (byDate=true)
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{4})[\\.-]([0-9]{2})[\\.-]([0-9]{2})"));
+ // foo.mm.dd.yyyy.* (byDate=true)
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(true,"([0-9]{2})[\\.-]([0-9]{2})[\\.-]([0-9]{4})"));
+ // foo.1x09* or just /1x09*
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$"));
+ // Part I, Pt.VI, Part 1
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\/._ -]p(?:ar)?t[_. -]()([ivx]+|[0-9]+)([._ -][^\\/]*)$"));
+ // This regexp is for matching special episodes by their title, e.g. foo.special.mp4
+ m_tvshowEnumRegExps.push_back(
+ TVShowRegexp(false, "[\\\\/]([^\\\\/]+)\\.special\\.[a-z0-9]+$", 0, true));
+
+ // foo.103*, 103 foo
+ // XXX: This regex is greedy and will match years in show names. It should always be last.
+ m_tvshowEnumRegExps.push_back(TVShowRegexp(false,"[\\\\/\\._ -]([0-9]+)([0-9][0-9](?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([\\._ -][^\\\\/]*)$"));
+
+ m_tvshowMultiPartEnumRegExp = "^[-_ex]+([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)";
+
+ m_remoteDelay = 3;
+ m_bScanIRServer = true;
+
+ m_playlistAsFolders = true;
+ m_detectAsUdf = false;
+
+ m_fanartRes = 1080;
+ m_imageRes = 720;
+ m_imageScalingAlgorithm = CPictureScalingAlgorithm::Default;
+ m_imageQualityJpeg = 4;
+
+ m_sambaclienttimeout = 30;
+ m_sambadoscodepage = "";
+ m_sambastatfiles = true;
+
+ m_bHTTPDirectoryStatFilesize = false;
+
+ m_bFTPThumbs = false;
+
+ m_bShoutcastArt = true;
+
+ m_musicThumbs = "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG";
+ m_musicArtistExtraArt = {};
+ m_musicAlbumExtraArt = {};
+
+ m_bMusicLibraryAllItemsOnBottom = false;
+ m_bMusicLibraryCleanOnUpdate = false;
+ m_bMusicLibraryArtistSortOnUpdate = false;
+ m_iMusicLibraryRecentlyAddedItems = 25;
+ m_strMusicLibraryAlbumFormat = "";
+ m_prioritiseAPEv2tags = false;
+ m_musicItemSeparator = " / ";
+ m_musicArtistSeparators = { ";", " feat. ", " ft. " };
+ m_videoItemSeparator = " / ";
+ m_iMusicLibraryDateAdded = 1; // prefer mtime over ctime and current time
+ m_bMusicLibraryUseISODates = false;
+
+ m_bVideoLibraryAllItemsOnBottom = false;
+ m_iVideoLibraryRecentlyAddedItems = 25;
+ m_bVideoLibraryCleanOnUpdate = false;
+ m_bVideoLibraryUseFastHash = true;
+ m_bVideoScannerIgnoreErrors = false;
+ m_iVideoLibraryDateAdded = 1; // prefer mtime over ctime and current time
+
+ m_videoEpisodeExtraArt = {};
+ m_videoTvShowExtraArt = {};
+ m_videoTvSeasonExtraArt = {};
+ m_videoMovieExtraArt = {};
+ m_videoMovieSetExtraArt = {};
+ m_videoMusicVideoExtraArt = {};
+
+ m_iEpgUpdateCheckInterval = 300; /* Check every X seconds, if EPG data need to be updated. This does not mean that
+ every X seconds an EPG update is actually triggered, it's just the interval how
+ often to check whether an update should be triggered. If this value is greater
+ than GUI setting 'epg.epgupdate' value, then EPG updates will done with the value
+ specified for 'updatecheckinterval', effectively overriding the GUI setting's value. */
+ m_iEpgCleanupInterval = 900; /* Remove old entries from the EPG every X seconds */
+ m_iEpgActiveTagCheckInterval = 60; /* Check for updated active tags every X seconds */
+ m_iEpgRetryInterruptedUpdateInterval = 30; /* Retry an interrupted EPG update after X seconds */
+ m_iEpgUpdateEmptyTagsInterval = 7200; /* If a TV channel has no EPG data, try to obtain data for that channel every
+ X seconds. This overrides the GUI setting 'epg.epgupdate' value, but only
+ for channels without EPG data. If this value is less than 'updatecheckinterval'
+ value, then data update will be done with the interval specified by
+ 'updatecheckinterval'.
+ Example 1: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
+ updateemptytagsinterval = 60 => trigger an EPG update for every
+ channel without EPG data every 5 minutes and trigger an EPG update
+ for every channel with EPG data every 2 hours.
+ Example 2: epg.epgupdate = 120 (minutes!), updatecheckinterval = 300,
+ updateemptytagsinterval = 3600 => trigger an EPG update for every
+ channel without EPG data every 2 hours and trigger an EPG update
+ for every channel with EPG data every 1 hour. */
+ m_bEpgDisplayUpdatePopup = true; /* Display a progress popup while updating EPG data from clients */
+ m_bEpgDisplayIncrementalUpdatePopup = false; /* Display a progress popup while doing incremental EPG updates, but
+ only if 'displayupdatepopup' is also enabled. */
+
+ m_bEdlMergeShortCommBreaks = false; // Off by default
+ m_EdlDisplayCommbreakNotifications = true; // On by default
+ m_iEdlMaxCommBreakLength = 8 * 30 + 10; // Just over 8 * 30 second commercial break.
+ m_iEdlMinCommBreakLength = 3 * 30; // 3 * 30 second commercial breaks.
+ m_iEdlMaxCommBreakGap = 4 * 30; // 4 * 30 second commercial breaks.
+ m_iEdlMaxStartGap = 5 * 60; // 5 minutes.
+ m_iEdlCommBreakAutowait = 0; // Off by default
+ m_iEdlCommBreakAutowind = 0; // Off by default
+
+ m_curlconnecttimeout = 30;
+ m_curllowspeedtime = 20;
+ m_curlretries = 2;
+ m_curlKeepAliveInterval = 30;
+ m_curlDisableIPV6 = false; //Certain hardware/OS combinations have trouble
+ //with ipv6.
+ m_curlDisableHTTP2 = false;
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ m_minimizeToTray = false;
+#endif
+#if defined(TARGET_DARWIN_EMBEDDED)
+ m_startFullScreen = true;
+#else
+ m_startFullScreen = false;
+#endif
+ m_showExitButton = true;
+ m_splashImage = true;
+
+ m_playlistRetries = 100;
+ m_playlistTimeout = 20; // 20 seconds timeout
+ m_GLRectangleHack = false;
+ m_iSkipLoopFilter = 0;
+ m_bVirtualShares = true;
+
+ m_cpuTempCmd = "";
+ m_gpuTempCmd = "";
+#if defined(TARGET_DARWIN)
+ // default for osx is fullscreen always on top
+ m_alwaysOnTop = true;
+#else
+ // default for windows is not always on top
+ m_alwaysOnTop = false;
+#endif
+
+ m_iPVRTimeCorrection = 0;
+ m_iPVRInfoToggleInterval = 5000;
+ m_bPVRChannelIconsAutoScan = true;
+ m_bPVRAutoScanIconsUserSet = false;
+ m_iPVRNumericChannelSwitchTimeout = 2000;
+ m_iPVRTimeshiftThreshold = 10;
+ m_bPVRTimeshiftSimpleOSD = true;
+ m_PVRDefaultSortOrder.sortBy = SortByDate;
+ m_PVRDefaultSortOrder.sortOrder = SortOrderDescending;
+
+ m_cacheMemSize = 1024 * 1024 * 20; // 20 MiB
+ m_cacheBufferMode = CACHE_BUFFER_MODE_NETWORK; // Default (buffer all network filesystems)
+ m_cacheChunkSize = 128 * 1024; // 128 KiB
+
+ // the following setting determines the readRate of a player data
+ // as multiply of the default data read rate
+ m_cacheReadFactor = 4.0f;
+
+ m_addonPackageFolderSize = 200;
+
+ m_jsonOutputCompact = true;
+ m_jsonTcpPort = 9090;
+
+ m_enableMultimediaKeys = false;
+
+ m_canWindowed = true;
+ m_guiVisualizeDirtyRegions = false;
+ m_guiAlgorithmDirtyRegions = 3;
+ m_guiSmartRedraw = false;
+ m_airTunesPort = 36666;
+ m_airPlayPort = 36667;
+
+ m_databaseMusic.Reset();
+ m_databaseVideo.Reset();
+
+ m_useLocaleCollation = true;
+
+ m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.rss|.webp|.jp2|.apng";
+ m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.gdm|.imf|.m15|.sfx|.uni|.ac3|.dts|.cue|.aif|.aiff|.wpl|.xspf|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.wv|.dsp|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.wtv|.mka|.tak|.opus|.dff|.dsf|.m4b|.dtshd";
+ m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.mpd|.m3u|.m3u8|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.udf|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.mk3d|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.001|.wpl|.xspf|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.mpl|.webm|.bdmv|.bdm|.wtv|.trp|.f4v";
+ m_subtitlesExtensions = ".utf|.utf8|.utf-8|.sub|.srt|.smi|.rt|.txt|.ssa|.text|.ssa|.aqt|.jss|"
+ ".ass|.vtt|.idx|.ifo|.zip|.sup";
+ m_discStubExtensions = ".disc";
+ // internal music extensions
+ m_musicExtensions += "|.cdda";
+ // internal video extensions
+ m_videoExtensions += "|.pvr";
+
+ m_stereoscopicregex_3d = "[-. _]3d[-. _]";
+ m_stereoscopicregex_sbs = "[-. _]h?sbs[-. _]";
+ m_stereoscopicregex_tab = "[-. _]h?tab[-. _]";
+
+ m_logLevelHint = m_logLevel = LOG_LEVEL_NORMAL;
+
+ m_openGlDebugging = false;
+
+ m_userAgent = g_sysinfo.GetUserAgent();
+
+ m_nfsTimeout = 30;
+ m_nfsRetries = -1;
+
+ m_initialized = true;
+}
+
+bool CAdvancedSettings::Load(const CProfileManager &profileManager)
+{
+ // NOTE: This routine should NOT set the default of any of these parameters
+ // it should instead use the versions of GetString/Integer/Float that
+ // don't take defaults in. Defaults are set in the constructor above
+ Initialize(); // In case of profile switch.
+ ParseSettingsFile("special://xbmc/system/advancedsettings.xml");
+ for (unsigned int i = 0; i < m_settingsFiles.size(); i++)
+ ParseSettingsFile(m_settingsFiles[i]);
+
+ ParseSettingsFile(profileManager.GetUserDataItem("advancedsettings.xml"));
+
+ // Add the list of disc stub extensions (if any) to the list of video extensions
+ if (!m_discStubExtensions.empty())
+ m_videoExtensions += "|" + m_discStubExtensions;
+
+ return true;
+}
+
+void CAdvancedSettings::ParseSettingsFile(const std::string &file)
+{
+ CXBMCTinyXML advancedXML;
+ if (!CFileUtils::Exists(file))
+ {
+ CLog::Log(LOGINFO, "No settings file to load ({})", file);
+ return;
+ }
+
+ if (!advancedXML.LoadFile(file))
+ {
+ CLog::Log(LOGERROR, "Error loading {}, Line {}\n{}", file, advancedXML.ErrorRow(),
+ advancedXML.ErrorDesc());
+ return;
+ }
+
+ TiXmlElement *pRootElement = advancedXML.RootElement();
+ if (!pRootElement || StringUtils::CompareNoCase(pRootElement->Value(), "advancedsettings") != 0)
+ {
+ CLog::Log(LOGERROR, "Error loading {}, no <advancedsettings> node", file);
+ return;
+ }
+
+ // succeeded - tell the user it worked
+ CLog::Log(LOGINFO, "Loaded settings file from {}", file);
+
+ //Make a copy of the AS.xml and hide advancedsettings passwords
+ CXBMCTinyXML advancedXMLCopy(advancedXML);
+ TiXmlNode *pRootElementCopy = advancedXMLCopy.RootElement();
+ for (const auto& dbname : { "videodatabase", "musicdatabase", "tvdatabase", "epgdatabase" })
+ {
+ TiXmlNode *db = pRootElementCopy->FirstChild(dbname);
+ if (db)
+ {
+ TiXmlNode *passTag = db->FirstChild("pass");
+ if (passTag)
+ {
+ TiXmlNode *pass = passTag->FirstChild();
+ if (pass)
+ {
+ passTag->RemoveChild(pass);
+ passTag->LinkEndChild(new TiXmlText("*****"));
+ }
+ }
+ }
+ }
+ TiXmlNode *network = pRootElementCopy->FirstChild("network");
+ if (network)
+ {
+ TiXmlNode *passTag = network->FirstChild("httpproxypassword");
+ if (passTag)
+ {
+ TiXmlNode *pass = passTag->FirstChild();
+ if (pass)
+ {
+ passTag->RemoveChild(pass);
+ passTag->LinkEndChild(new TiXmlText("*****"));
+ }
+ }
+ if (network->FirstChildElement("nfstimeout"))
+ {
+#ifdef HAS_NFS_SET_TIMEOUT
+ XMLUtils::GetUInt(network, "nfstimeout", m_nfsTimeout, 0, 3600);
+#else
+ CLog::Log(LOGWARNING, "nfstimeout unsupported");
+#endif
+ }
+ if (network->FirstChildElement("nfsretries"))
+ {
+ XMLUtils::GetInt(network, "nfsretries", m_nfsRetries, -1, 30);
+ }
+ }
+
+ // Dump contents of copied AS.xml to debug log
+ TiXmlPrinter printer;
+ printer.SetLineBreak("\n");
+ printer.SetIndent(" ");
+ advancedXMLCopy.Accept(&printer);
+ // redact User/pass in URLs
+ std::regex redactRe("(\\w+://)\\S+:\\S+@");
+ CLog::Log(LOGINFO, "Contents of {} are...\n{}", file,
+ std::regex_replace(printer.CStr(), redactRe, "$1USERNAME:PASSWORD@"));
+
+ TiXmlElement *pElement = pRootElement->FirstChildElement("audio");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "defaultplayer", m_audioDefaultPlayer);
+ // 101 on purpose - can be used to never automark as watched
+ XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_audioPlayCountMinimumPercent, 0.0f, 101.0f);
+
+ XMLUtils::GetBoolean(pElement, "usetimeseeking", m_musicUseTimeSeeking);
+ XMLUtils::GetInt(pElement, "timeseekforward", m_musicTimeSeekForward, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackward", m_musicTimeSeekBackward, -6000, 0);
+ XMLUtils::GetInt(pElement, "timeseekforwardbig", m_musicTimeSeekForwardBig, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_musicTimeSeekBackwardBig, -6000, 0);
+
+ XMLUtils::GetInt(pElement, "percentseekforward", m_musicPercentSeekForward, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackward", m_musicPercentSeekBackward, -100, 0);
+ XMLUtils::GetInt(pElement, "percentseekforwardbig", m_musicPercentSeekForwardBig, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_musicPercentSeekBackwardBig, -100, 0);
+
+ TiXmlElement* pAudioExcludes = pElement->FirstChildElement("excludefromlisting");
+ if (pAudioExcludes)
+ GetCustomRegexps(pAudioExcludes, m_audioExcludeFromListingRegExps);
+
+ pAudioExcludes = pElement->FirstChildElement("excludefromscan");
+ if (pAudioExcludes)
+ GetCustomRegexps(pAudioExcludes, m_audioExcludeFromScanRegExps);
+
+ XMLUtils::GetFloat(pElement, "applydrc", m_audioApplyDrc);
+ XMLUtils::GetBoolean(pElement, "VideoPlayerignoredtsinwav", m_VideoPlayerIgnoreDTSinWAV);
+
+ XMLUtils::GetFloat(pElement, "limiterhold", m_limiterHold, 0.0f, 100.0f);
+ XMLUtils::GetFloat(pElement, "limiterrelease", m_limiterRelease, 0.001f, 100.0f);
+ XMLUtils::GetUInt(pElement, "maxpassthroughoffsyncduration", m_maxPassthroughOffSyncDuration,
+ 10, 100);
+ }
+
+ pElement = pRootElement->FirstChildElement("x11");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "omlsync", m_omlSync);
+ }
+
+ pElement = pRootElement->FirstChildElement("video");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "stereoscopicregex3d", m_stereoscopicregex_3d);
+ XMLUtils::GetString(pElement, "stereoscopicregexsbs", m_stereoscopicregex_sbs);
+ XMLUtils::GetString(pElement, "stereoscopicregextab", m_stereoscopicregex_tab);
+ XMLUtils::GetFloat(pElement, "subsdelayrange", m_videoSubsDelayRange, 10, 600);
+ XMLUtils::GetFloat(pElement, "audiodelayrange", m_videoAudioDelayRange, 10, 600);
+ XMLUtils::GetString(pElement, "defaultplayer", m_videoDefaultPlayer);
+ XMLUtils::GetBoolean(pElement, "fullscreenonmoviestart", m_fullScreenOnMovieStart);
+ // 101 on purpose - can be used to never automark as watched
+ XMLUtils::GetFloat(pElement, "playcountminimumpercent", m_videoPlayCountMinimumPercent, 0.0f, 101.0f);
+ XMLUtils::GetInt(pElement, "ignoresecondsatstart", m_videoIgnoreSecondsAtStart, 0, 900);
+ XMLUtils::GetFloat(pElement, "ignorepercentatend", m_videoIgnorePercentAtEnd, 0, 100.0f);
+
+ XMLUtils::GetBoolean(pElement, "usetimeseeking", m_videoUseTimeSeeking);
+ XMLUtils::GetInt(pElement, "timeseekforward", m_videoTimeSeekForward, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackward", m_videoTimeSeekBackward, -6000, 0);
+ XMLUtils::GetInt(pElement, "timeseekforwardbig", m_videoTimeSeekForwardBig, 0, 6000);
+ XMLUtils::GetInt(pElement, "timeseekbackwardbig", m_videoTimeSeekBackwardBig, -6000, 0);
+
+ XMLUtils::GetInt(pElement, "percentseekforward", m_videoPercentSeekForward, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackward", m_videoPercentSeekBackward, -100, 0);
+ XMLUtils::GetInt(pElement, "percentseekforwardbig", m_videoPercentSeekForwardBig, 0, 100);
+ XMLUtils::GetInt(pElement, "percentseekbackwardbig", m_videoPercentSeekBackwardBig, -100, 0);
+
+ TiXmlElement* pVideoExcludes = pElement->FirstChildElement("excludefromlisting");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_videoExcludeFromListingRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("excludefromscan");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_moviesExcludeFromScanRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("excludetvshowsfromscan");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_tvshowExcludeFromScanRegExps);
+
+ pVideoExcludes = pElement->FirstChildElement("cleanstrings");
+ if (pVideoExcludes)
+ GetCustomRegexps(pVideoExcludes, m_videoCleanStringRegExps);
+
+ XMLUtils::GetString(pElement,"cleandatetime", m_videoCleanDateTimeRegExp);
+ XMLUtils::GetString(pElement,"ppffmpegpostprocessing",m_videoPPFFmpegPostProc);
+ XMLUtils::GetInt(pElement,"vdpauscaling",m_videoVDPAUScaling);
+ XMLUtils::GetFloat(pElement, "nonlinearstretchratio", m_videoNonLinStretchRatio, 0.01f, 1.0f);
+ XMLUtils::GetFloat(pElement,"autoscalemaxfps",m_videoAutoScaleMaxFps, 0.0f, 1000.0f);
+ XMLUtils::GetInt(pElement, "useocclusionquery", m_videoCaptureUseOcclusionQuery, -1, 1);
+ XMLUtils::GetBoolean(pElement,"vdpauInvTelecine",m_videoVDPAUtelecine);
+ XMLUtils::GetBoolean(pElement,"vdpauHDdeintSkipChroma",m_videoVDPAUdeintSkipChromaHD);
+
+ TiXmlElement* pAdjustRefreshrate = pElement->FirstChildElement("adjustrefreshrate");
+ if (pAdjustRefreshrate)
+ {
+ TiXmlElement* pRefreshOverride = pAdjustRefreshrate->FirstChildElement("override");
+ while (pRefreshOverride)
+ {
+ RefreshOverride override = {};
+
+ float fps;
+ if (XMLUtils::GetFloat(pRefreshOverride, "fps", fps))
+ {
+ override.fpsmin = fps - 0.01f;
+ override.fpsmax = fps + 0.01f;
+ }
+
+ float fpsmin, fpsmax;
+ if (XMLUtils::GetFloat(pRefreshOverride, "fpsmin", fpsmin) &&
+ XMLUtils::GetFloat(pRefreshOverride, "fpsmax", fpsmax))
+ {
+ override.fpsmin = fpsmin;
+ override.fpsmax = fpsmax;
+ }
+
+ float refresh;
+ if (XMLUtils::GetFloat(pRefreshOverride, "refresh", refresh))
+ {
+ override.refreshmin = refresh - 0.01f;
+ override.refreshmax = refresh + 0.01f;
+ }
+
+ float refreshmin, refreshmax;
+ if (XMLUtils::GetFloat(pRefreshOverride, "refreshmin", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshOverride, "refreshmax", refreshmax))
+ {
+ override.refreshmin = refreshmin;
+ override.refreshmax = refreshmax;
+ }
+
+ bool fpsCorrect = (override.fpsmin > 0.0f && override.fpsmax >= override.fpsmin);
+ bool refreshCorrect = (override.refreshmin > 0.0f && override.refreshmax >= override.refreshmin);
+
+ if (fpsCorrect && refreshCorrect)
+ m_videoAdjustRefreshOverrides.push_back(override);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed refreshrate override, fpsmin:{:f} fpsmax:{:f} "
+ "refreshmin:{:f} refreshmax:{:f}",
+ override.fpsmin, override.fpsmax, override.refreshmin, override.refreshmax);
+
+ pRefreshOverride = pRefreshOverride->NextSiblingElement("override");
+ }
+
+ TiXmlElement* pRefreshFallback = pAdjustRefreshrate->FirstChildElement("fallback");
+ while (pRefreshFallback)
+ {
+ RefreshOverride fallback = {};
+ fallback.fallback = true;
+
+ float refresh;
+ if (XMLUtils::GetFloat(pRefreshFallback, "refresh", refresh))
+ {
+ fallback.refreshmin = refresh - 0.01f;
+ fallback.refreshmax = refresh + 0.01f;
+ }
+
+ float refreshmin, refreshmax;
+ if (XMLUtils::GetFloat(pRefreshFallback, "refreshmin", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshFallback, "refreshmax", refreshmax))
+ {
+ fallback.refreshmin = refreshmin;
+ fallback.refreshmax = refreshmax;
+ }
+
+ if (fallback.refreshmin > 0.0f && fallback.refreshmax >= fallback.refreshmin)
+ m_videoAdjustRefreshOverrides.push_back(fallback);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed refreshrate fallback, fpsmin:{:f} fpsmax:{:f} "
+ "refreshmin:{:f} refreshmax:{:f}",
+ fallback.fpsmin, fallback.fpsmax, fallback.refreshmin, fallback.refreshmax);
+
+ pRefreshFallback = pRefreshFallback->NextSiblingElement("fallback");
+ }
+ }
+
+ m_DXVACheckCompatibilityPresent = XMLUtils::GetBoolean(pElement,"checkdxvacompatibility", m_DXVACheckCompatibility);
+
+ //0 = disable fps detect, 1 = only detect on timestamps with uniform spacing, 2 detect on all timestamps
+ XMLUtils::GetInt(pElement, "fpsdetect", m_videoFpsDetect, 0, 2);
+ XMLUtils::GetFloat(pElement, "maxtempo", m_maxTempo, 1.5, 2.1);
+ XMLUtils::GetBoolean(pElement, "preferstereostream", m_videoPreferStereoStream);
+
+ // Store global display latency settings
+ TiXmlElement* pVideoLatency = pElement->FirstChildElement("latency");
+ if (pVideoLatency)
+ {
+ float refresh, refreshmin, refreshmax, delay;
+ TiXmlElement* pRefreshVideoLatency = pVideoLatency->FirstChildElement("refresh");
+
+ while (pRefreshVideoLatency)
+ {
+ RefreshVideoLatency videolatency = {};
+
+ if (XMLUtils::GetFloat(pRefreshVideoLatency, "rate", refresh))
+ {
+ videolatency.refreshmin = refresh - 0.01f;
+ videolatency.refreshmax = refresh + 0.01f;
+ }
+ else if (XMLUtils::GetFloat(pRefreshVideoLatency, "min", refreshmin) &&
+ XMLUtils::GetFloat(pRefreshVideoLatency, "max", refreshmax))
+ {
+ videolatency.refreshmin = refreshmin;
+ videolatency.refreshmax = refreshmax;
+ }
+ if (XMLUtils::GetFloat(pRefreshVideoLatency, "delay", delay, -600.0f, 600.0f))
+ videolatency.delay = delay;
+
+ if (videolatency.refreshmin > 0.0f && videolatency.refreshmax >= videolatency.refreshmin)
+ m_videoRefreshLatency.push_back(videolatency);
+ else
+ CLog::Log(LOGWARNING,
+ "Ignoring malformed display latency <refresh> entry, min:{:f} max:{:f}",
+ videolatency.refreshmin, videolatency.refreshmax);
+
+ pRefreshVideoLatency = pRefreshVideoLatency->NextSiblingElement("refresh");
+ }
+
+ // Get default global display latency
+ XMLUtils::GetFloat(pVideoLatency, "delay", m_videoDefaultLatency, -600.0f, 600.0f);
+ }
+ }
+
+ pElement = pRootElement->FirstChildElement("musiclibrary");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iMusicLibraryRecentlyAddedItems, 1, INT_MAX);
+ XMLUtils::GetBoolean(pElement, "prioritiseapetags", m_prioritiseAPEv2tags);
+ XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bMusicLibraryAllItemsOnBottom);
+ XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bMusicLibraryCleanOnUpdate);
+ XMLUtils::GetBoolean(pElement, "artistsortonupdate", m_bMusicLibraryArtistSortOnUpdate);
+ XMLUtils::GetString(pElement, "albumformat", m_strMusicLibraryAlbumFormat);
+ XMLUtils::GetString(pElement, "itemseparator", m_musicItemSeparator);
+ XMLUtils::GetInt(pElement, "dateadded", m_iMusicLibraryDateAdded);
+ XMLUtils::GetBoolean(pElement, "useisodates", m_bMusicLibraryUseISODates);
+ //Music artist name separators
+ TiXmlElement* separators = pElement->FirstChildElement("artistseparators");
+ if (separators)
+ {
+ m_musicArtistSeparators.clear();
+ TiXmlNode* separator = separators->FirstChild("separator");
+ while (separator)
+ {
+ if (separator->FirstChild())
+ m_musicArtistSeparators.push_back(separator->FirstChild()->ValueStr());
+ separator = separator->NextSibling("separator");
+ }
+ }
+
+ SetExtraArtwork(pElement->FirstChildElement("artistextraart"), m_musicArtistExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("albumextraart"), m_musicAlbumExtraArt);
+ }
+
+ pElement = pRootElement->FirstChildElement("videolibrary");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "allitemsonbottom", m_bVideoLibraryAllItemsOnBottom);
+ XMLUtils::GetInt(pElement, "recentlyaddeditems", m_iVideoLibraryRecentlyAddedItems, 1, INT_MAX);
+ XMLUtils::GetBoolean(pElement, "cleanonupdate", m_bVideoLibraryCleanOnUpdate);
+ XMLUtils::GetBoolean(pElement, "usefasthash", m_bVideoLibraryUseFastHash);
+ XMLUtils::GetString(pElement, "itemseparator", m_videoItemSeparator);
+ XMLUtils::GetBoolean(pElement, "importwatchedstate", m_bVideoLibraryImportWatchedState);
+ XMLUtils::GetBoolean(pElement, "importresumepoint", m_bVideoLibraryImportResumePoint);
+ XMLUtils::GetInt(pElement, "dateadded", m_iVideoLibraryDateAdded);
+
+ SetExtraArtwork(pElement->FirstChildElement("episodeextraart"), m_videoEpisodeExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("tvshowextraart"), m_videoTvShowExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("tvseasonextraart"), m_videoTvSeasonExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("movieextraart"), m_videoMovieExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("moviesetextraart"), m_videoMovieSetExtraArt);
+ SetExtraArtwork(pElement->FirstChildElement("musicvideoextraart"), m_videoMusicVideoExtraArt);
+ }
+
+ pElement = pRootElement->FirstChildElement("videoscanner");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "ignoreerrors", m_bVideoScannerIgnoreErrors);
+ }
+
+ // Backward-compatibility of ExternalPlayer config
+ pElement = pRootElement->FirstChildElement("externalplayer");
+ if (pElement)
+ {
+ CLog::Log(LOGWARNING, "External player configuration has been removed from advancedsettings.xml. It can now be configured in userdata/playercorefactory.xml");
+ }
+ pElement = pRootElement->FirstChildElement("slideshow");
+ if (pElement)
+ {
+ XMLUtils::GetFloat(pElement, "panamount", m_slideshowPanAmount, 0.0f, 20.0f);
+ XMLUtils::GetFloat(pElement, "zoomamount", m_slideshowZoomAmount, 0.0f, 20.0f);
+ XMLUtils::GetFloat(pElement, "blackbarcompensation", m_slideshowBlackBarCompensation, 0.0f, 50.0f);
+ }
+
+ pElement = pRootElement->FirstChildElement("network");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "curlclienttimeout", m_curlconnecttimeout, 1, 1000);
+ XMLUtils::GetInt(pElement, "curllowspeedtime", m_curllowspeedtime, 1, 1000);
+ XMLUtils::GetInt(pElement, "curlretries", m_curlretries, 0, 10);
+ XMLUtils::GetInt(pElement, "curlkeepaliveinterval", m_curlKeepAliveInterval, 0, 300);
+ XMLUtils::GetBoolean(pElement, "disableipv6", m_curlDisableIPV6);
+ XMLUtils::GetBoolean(pElement, "disablehttp2", m_curlDisableHTTP2);
+ XMLUtils::GetString(pElement, "catrustfile", m_caTrustFile);
+ }
+
+ pElement = pRootElement->FirstChildElement("cache");
+ if (pElement)
+ {
+ XMLUtils::GetUInt(pElement, "memorysize", m_cacheMemSize);
+ XMLUtils::GetUInt(pElement, "buffermode", m_cacheBufferMode, 0, 4);
+ XMLUtils::GetUInt(pElement, "chunksize", m_cacheChunkSize, 256, 1024 * 1024);
+ XMLUtils::GetFloat(pElement, "readfactor", m_cacheReadFactor);
+ }
+
+ pElement = pRootElement->FirstChildElement("jsonrpc");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "compactoutput", m_jsonOutputCompact);
+ XMLUtils::GetUInt(pElement, "tcpport", m_jsonTcpPort);
+ }
+
+ pElement = pRootElement->FirstChildElement("samba");
+ if (pElement)
+ {
+ XMLUtils::GetString(pElement, "doscodepage", m_sambadoscodepage);
+ XMLUtils::GetInt(pElement, "clienttimeout", m_sambaclienttimeout, 5, 100);
+ XMLUtils::GetBoolean(pElement, "statfiles", m_sambastatfiles);
+ }
+
+ pElement = pRootElement->FirstChildElement("httpdirectory");
+ if (pElement)
+ XMLUtils::GetBoolean(pElement, "statfilesize", m_bHTTPDirectoryStatFilesize);
+
+ pElement = pRootElement->FirstChildElement("ftp");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "remotethumbs", m_bFTPThumbs);
+ }
+
+ pElement = pRootElement->FirstChildElement("loglevel");
+ if (pElement)
+ { // read the loglevel setting, so set the setting advanced to hide it in GUI
+ // as altering it will do nothing - we don't write to advancedsettings.xml
+ XMLUtils::GetInt(pRootElement, "loglevel", m_logLevelHint, LOG_LEVEL_NONE, LOG_LEVEL_MAX);
+ const char* hide = pElement->Attribute("hide");
+ if (hide == NULL || StringUtils::CompareNoCase("false", hide, 5) != 0)
+ {
+ SettingPtr setting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_DEBUG_SHOWLOGINFO);
+ if (setting != NULL)
+ setting->SetVisible(false);
+ }
+ m_logLevel = std::max(m_logLevel, m_logLevelHint);
+ CServiceBroker::GetLogging().SetLogLevel(m_logLevel);
+ }
+
+ XMLUtils::GetString(pRootElement, "cddbaddress", m_cddbAddress);
+ XMLUtils::GetBoolean(pRootElement, "addsourceontop", m_addSourceOnTop);
+
+ //airtunes + airplay
+ XMLUtils::GetInt(pRootElement, "airtunesport", m_airTunesPort);
+ XMLUtils::GetInt(pRootElement, "airplayport", m_airPlayPort);
+
+ XMLUtils::GetBoolean(pRootElement, "handlemounting", m_handleMounting);
+ XMLUtils::GetBoolean(pRootElement, "automountopticalmedia", m_autoMountOpticalMedia);
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ XMLUtils::GetBoolean(pRootElement, "minimizetotray", m_minimizeToTray);
+#endif
+#if defined(TARGET_DARWIN_OSX) || defined(TARGET_WINDOWS)
+ XMLUtils::GetBoolean(pRootElement, "fullscreen", m_startFullScreen);
+#endif
+ XMLUtils::GetBoolean(pRootElement, "splash", m_splashImage);
+ XMLUtils::GetBoolean(pRootElement, "showexitbutton", m_showExitButton);
+ XMLUtils::GetBoolean(pRootElement, "canwindowed", m_canWindowed);
+
+ XMLUtils::GetInt(pRootElement, "songinfoduration", m_songInfoDuration, 0, INT_MAX);
+ XMLUtils::GetInt(pRootElement, "playlistretries", m_playlistRetries, -1, 5000);
+ XMLUtils::GetInt(pRootElement, "playlisttimeout", m_playlistTimeout, 0, 5000);
+
+ XMLUtils::GetBoolean(pRootElement,"glrectanglehack", m_GLRectangleHack);
+ XMLUtils::GetInt(pRootElement,"skiploopfilter", m_iSkipLoopFilter, -16, 48);
+
+ XMLUtils::GetBoolean(pRootElement,"virtualshares", m_bVirtualShares);
+ XMLUtils::GetUInt(pRootElement, "packagefoldersize", m_addonPackageFolderSize);
+
+ // EPG
+ pElement = pRootElement->FirstChildElement("epg");
+ if (pElement)
+ {
+ XMLUtils::GetInt(pElement, "updatecheckinterval", m_iEpgUpdateCheckInterval);
+ XMLUtils::GetInt(pElement, "cleanupinterval", m_iEpgCleanupInterval);
+ XMLUtils::GetInt(pElement, "activetagcheckinterval", m_iEpgActiveTagCheckInterval);
+ XMLUtils::GetInt(pElement, "retryinterruptedupdateinterval", m_iEpgRetryInterruptedUpdateInterval);
+ XMLUtils::GetInt(pElement, "updateemptytagsinterval", m_iEpgUpdateEmptyTagsInterval);
+ XMLUtils::GetBoolean(pElement, "displayupdatepopup", m_bEpgDisplayUpdatePopup);
+ XMLUtils::GetBoolean(pElement, "displayincrementalupdatepopup", m_bEpgDisplayIncrementalUpdatePopup);
+ }
+
+ // EDL commercial break handling
+ pElement = pRootElement->FirstChildElement("edl");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "mergeshortcommbreaks", m_bEdlMergeShortCommBreaks);
+ XMLUtils::GetBoolean(pElement, "displaycommbreaknotifications",
+ m_EdlDisplayCommbreakNotifications);
+ XMLUtils::GetInt(pElement, "maxcommbreaklength", m_iEdlMaxCommBreakLength, 0, 10 * 60); // Between 0 and 10 minutes
+ XMLUtils::GetInt(pElement, "mincommbreaklength", m_iEdlMinCommBreakLength, 0, 5 * 60); // Between 0 and 5 minutes
+ XMLUtils::GetInt(pElement, "maxcommbreakgap", m_iEdlMaxCommBreakGap, 0, 5 * 60); // Between 0 and 5 minutes.
+ XMLUtils::GetInt(pElement, "maxstartgap", m_iEdlMaxStartGap, 0, 10 * 60); // Between 0 and 10 minutes
+ XMLUtils::GetInt(pElement, "commbreakautowait", m_iEdlCommBreakAutowait, -60, 60); // Between -60 and 60 seconds
+ XMLUtils::GetInt(pElement, "commbreakautowind", m_iEdlCommBreakAutowind, -60, 60); // Between -60 and 60 seconds
+ }
+
+ // picture exclude regexps
+ TiXmlElement* pPictureExcludes = pRootElement->FirstChildElement("pictureexcludes");
+ if (pPictureExcludes)
+ GetCustomRegexps(pPictureExcludes, m_pictureExcludeFromListingRegExps);
+
+ // picture extensions
+ TiXmlElement* pExts = pRootElement->FirstChildElement("pictureextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_pictureExtensions);
+
+ // music extensions
+ pExts = pRootElement->FirstChildElement("musicextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_musicExtensions);
+
+ // video extensions
+ pExts = pRootElement->FirstChildElement("videoextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_videoExtensions);
+
+ // stub extensions
+ pExts = pRootElement->FirstChildElement("discstubextensions");
+ if (pExts)
+ GetCustomExtensions(pExts, m_discStubExtensions);
+
+ m_vecTokens.clear();
+ CLangInfo::LoadTokens(pRootElement->FirstChild("sorttokens"),m_vecTokens);
+
+ //! @todo Should cache path be given in terms of our predefined paths??
+ //! Are we even going to have predefined paths??
+ std::string tmp;
+ if (XMLUtils::GetPath(pRootElement, "cachepath", tmp))
+ m_cachePath = tmp;
+ URIUtils::AddSlashAtEnd(m_cachePath);
+
+ g_LangCodeExpander.LoadUserCodes(pRootElement->FirstChildElement("languagecodes"));
+
+ // trailer matching regexps
+ TiXmlElement* pTrailerMatching = pRootElement->FirstChildElement("trailermatching");
+ if (pTrailerMatching)
+ GetCustomRegexps(pTrailerMatching, m_trailerMatchRegExps);
+
+ //everything that's a trailer is not a movie
+ m_moviesExcludeFromScanRegExps.insert(m_moviesExcludeFromScanRegExps.end(),
+ m_trailerMatchRegExps.begin(),
+ m_trailerMatchRegExps.end());
+
+ // video stacking regexps
+ TiXmlElement* pVideoStacking = pRootElement->FirstChildElement("moviestacking");
+ if (pVideoStacking)
+ GetCustomRegexps(pVideoStacking, m_videoStackRegExps);
+
+ // folder stacking regexps
+ TiXmlElement* pFolderStacking = pRootElement->FirstChildElement("folderstacking");
+ if (pFolderStacking)
+ GetCustomRegexps(pFolderStacking, m_folderStackRegExps);
+
+ //tv stacking regexps
+ TiXmlElement* pTVStacking = pRootElement->FirstChildElement("tvshowmatching");
+ if (pTVStacking)
+ GetCustomTVRegexps(pTVStacking, m_tvshowEnumRegExps);
+
+ //tv multipart enumeration regexp
+ XMLUtils::GetString(pRootElement, "tvmultipartmatching", m_tvshowMultiPartEnumRegExp);
+
+ // path substitutions
+ TiXmlElement* pPathSubstitution = pRootElement->FirstChildElement("pathsubstitution");
+ if (pPathSubstitution)
+ {
+ m_pathSubstitutions.clear();
+ CLog::Log(LOGDEBUG,"Configuring path substitutions");
+ TiXmlNode* pSubstitute = pPathSubstitution->FirstChildElement("substitute");
+ while (pSubstitute)
+ {
+ std::string strFrom, strTo;
+ TiXmlNode* pFrom = pSubstitute->FirstChild("from");
+ if (pFrom && !pFrom->NoChildren())
+ strFrom = CSpecialProtocol::TranslatePath(pFrom->FirstChild()->Value()).c_str();
+ TiXmlNode* pTo = pSubstitute->FirstChild("to");
+ if (pTo && !pTo->NoChildren())
+ strTo = pTo->FirstChild()->Value();
+
+ if (!strFrom.empty() && !strTo.empty())
+ {
+ CLog::Log(LOGDEBUG," Registering substitution pair:");
+ CLog::Log(LOGDEBUG, " From: [{}]", CURL::GetRedacted(strFrom));
+ CLog::Log(LOGDEBUG, " To: [{}]", CURL::GetRedacted(strTo));
+ m_pathSubstitutions.push_back(std::make_pair(strFrom,strTo));
+ }
+ else
+ {
+ // error message about missing tag
+ if (strFrom.empty())
+ CLog::Log(LOGERROR," Missing <from> tag");
+ else
+ CLog::Log(LOGERROR," Missing <to> tag");
+ }
+
+ // get next one
+ pSubstitute = pSubstitute->NextSiblingElement("substitute");
+ }
+ }
+
+ XMLUtils::GetInt(pRootElement, "remotedelay", m_remoteDelay, 0, 20);
+ XMLUtils::GetBoolean(pRootElement, "scanirserver", m_bScanIRServer);
+
+ XMLUtils::GetUInt(pRootElement, "fanartres", m_fanartRes, 0, 9999);
+ XMLUtils::GetUInt(pRootElement, "imageres", m_imageRes, 0, 9999);
+ if (XMLUtils::GetString(pRootElement, "imagescalingalgorithm", tmp))
+ m_imageScalingAlgorithm = CPictureScalingAlgorithm::FromString(tmp);
+ XMLUtils::GetUInt(pRootElement, "imagequalityjpeg", m_imageQualityJpeg, 0, 21);
+ XMLUtils::GetBoolean(pRootElement, "playlistasfolders", m_playlistAsFolders);
+ XMLUtils::GetBoolean(pRootElement, "uselocalecollation", m_useLocaleCollation);
+ XMLUtils::GetBoolean(pRootElement, "detectasudf", m_detectAsUdf);
+
+ // music thumbs
+ TiXmlElement* pThumbs = pRootElement->FirstChildElement("musicthumbs");
+ if (pThumbs)
+ GetCustomExtensions(pThumbs,m_musicThumbs);
+
+ // show art for shoutcast v2 streams (set to false for devices with limited storage)
+ XMLUtils::GetBoolean(pRootElement, "shoutcastart", m_bShoutcastArt);
+ // music filename->tag filters
+ TiXmlElement* filters = pRootElement->FirstChildElement("musicfilenamefilters");
+ if (filters)
+ {
+ TiXmlNode* filter = filters->FirstChild("filter");
+ while (filter)
+ {
+ if (filter->FirstChild())
+ m_musicTagsFromFileFilters.push_back(filter->FirstChild()->ValueStr());
+ filter = filter->NextSibling("filter");
+ }
+ }
+
+ TiXmlElement* pHostEntries = pRootElement->FirstChildElement("hosts");
+ if (pHostEntries)
+ {
+ TiXmlElement* element = pHostEntries->FirstChildElement("entry");
+ while(element)
+ {
+ if(!element->NoChildren())
+ {
+ std::string name = XMLUtils::GetAttribute(element, "name");
+ std::string value = element->FirstChild()->ValueStr();
+ if (!name.empty())
+ CDNSNameCache::Add(name, value);
+ }
+ element = element->NextSiblingElement("entry");
+ }
+ }
+
+ XMLUtils::GetString(pRootElement, "cputempcommand", m_cpuTempCmd);
+ XMLUtils::GetString(pRootElement, "gputempcommand", m_gpuTempCmd);
+
+ XMLUtils::GetBoolean(pRootElement, "alwaysontop", m_alwaysOnTop);
+
+ TiXmlElement *pPVR = pRootElement->FirstChildElement("pvr");
+ if (pPVR)
+ {
+ XMLUtils::GetInt(pPVR, "timecorrection", m_iPVRTimeCorrection, 0, 1440);
+ XMLUtils::GetInt(pPVR, "infotoggleinterval", m_iPVRInfoToggleInterval, 0, 30000);
+ XMLUtils::GetBoolean(pPVR, "channeliconsautoscan", m_bPVRChannelIconsAutoScan);
+ XMLUtils::GetBoolean(pPVR, "autoscaniconsuserset", m_bPVRAutoScanIconsUserSet);
+ XMLUtils::GetInt(pPVR, "numericchannelswitchtimeout", m_iPVRNumericChannelSwitchTimeout, 50, 60000);
+ XMLUtils::GetInt(pPVR, "timeshiftthreshold", m_iPVRTimeshiftThreshold, 0, 60);
+ XMLUtils::GetBoolean(pPVR, "timeshiftsimpleosd", m_bPVRTimeshiftSimpleOSD);
+ TiXmlElement* pSortDecription = pPVR->FirstChildElement("pvrrecordings");
+ if (pSortDecription)
+ {
+ const char* XML_SORTMETHOD = "sortmethod";
+ const char* XML_SORTORDER = "sortorder";
+ int sortMethod;
+ // ignore SortByTime for duration defaults
+ if (XMLUtils::GetInt(pSortDecription, XML_SORTMETHOD, sortMethod, SortByLabel, SortByFile))
+ {
+ int sortOrder;
+ if (XMLUtils::GetInt(pSortDecription, XML_SORTORDER, sortOrder, SortOrderAscending,
+ SortOrderDescending))
+ {
+ m_PVRDefaultSortOrder.sortBy = (SortBy)sortMethod;
+ m_PVRDefaultSortOrder.sortOrder = (SortOrder)sortOrder;
+ }
+ }
+ }
+ }
+
+ TiXmlElement* pDatabase = pRootElement->FirstChildElement("videodatabase");
+ if (pDatabase)
+ {
+ CLog::Log(LOGWARNING, "VIDEO database configuration is experimental.");
+ XMLUtils::GetString(pDatabase, "type", m_databaseVideo.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseVideo.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseVideo.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseVideo.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseVideo.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseVideo.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseVideo.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseVideo.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseVideo.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseVideo.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseVideo.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseVideo.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("musicdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseMusic.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseMusic.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseMusic.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseMusic.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseMusic.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseMusic.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseMusic.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseMusic.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseMusic.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseMusic.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseMusic.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseMusic.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("tvdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseTV.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseTV.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseTV.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseTV.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseTV.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseTV.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseTV.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseTV.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseTV.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseTV.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseTV.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseTV.compression);
+ }
+
+ pDatabase = pRootElement->FirstChildElement("epgdatabase");
+ if (pDatabase)
+ {
+ XMLUtils::GetString(pDatabase, "type", m_databaseEpg.type);
+ XMLUtils::GetString(pDatabase, "host", m_databaseEpg.host);
+ XMLUtils::GetString(pDatabase, "port", m_databaseEpg.port);
+ XMLUtils::GetString(pDatabase, "user", m_databaseEpg.user);
+ XMLUtils::GetString(pDatabase, "pass", m_databaseEpg.pass);
+ XMLUtils::GetString(pDatabase, "name", m_databaseEpg.name);
+ XMLUtils::GetString(pDatabase, "key", m_databaseEpg.key);
+ XMLUtils::GetString(pDatabase, "cert", m_databaseEpg.cert);
+ XMLUtils::GetString(pDatabase, "ca", m_databaseEpg.ca);
+ XMLUtils::GetString(pDatabase, "capath", m_databaseEpg.capath);
+ XMLUtils::GetString(pDatabase, "ciphers", m_databaseEpg.ciphers);
+ XMLUtils::GetBoolean(pDatabase, "compression", m_databaseEpg.compression);
+ }
+
+ pElement = pRootElement->FirstChildElement("enablemultimediakeys");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pRootElement, "enablemultimediakeys", m_enableMultimediaKeys);
+ }
+
+ pElement = pRootElement->FirstChildElement("gui");
+ if (pElement)
+ {
+ XMLUtils::GetBoolean(pElement, "visualizedirtyregions", m_guiVisualizeDirtyRegions);
+ XMLUtils::GetInt(pElement, "algorithmdirtyregions", m_guiAlgorithmDirtyRegions);
+ XMLUtils::GetBoolean(pElement, "smartredraw", m_guiSmartRedraw);
+ }
+
+ std::string seekSteps;
+ XMLUtils::GetString(pRootElement, "seeksteps", seekSteps);
+ if (!seekSteps.empty())
+ {
+ m_seekSteps.clear();
+ std::vector<std::string> steps = StringUtils::Split(seekSteps, ',');
+ for(std::vector<std::string>::iterator it = steps.begin(); it != steps.end(); ++it)
+ m_seekSteps.push_back(atoi((*it).c_str()));
+ }
+
+ XMLUtils::GetBoolean(pRootElement, "opengldebugging", m_openGlDebugging);
+
+ // load in the settings overrides
+ CServiceBroker::GetSettingsComponent()->GetSettings()->LoadHidden(pRootElement);
+
+ // Migration of old style art options from advanced setting to GUI setting
+ MigrateOldArtSettings();
+}
+
+void CAdvancedSettings::Clear()
+{
+ m_videoCleanStringRegExps.clear();
+ m_moviesExcludeFromScanRegExps.clear();
+ m_tvshowExcludeFromScanRegExps.clear();
+ m_videoExcludeFromListingRegExps.clear();
+ m_videoStackRegExps.clear();
+ m_folderStackRegExps.clear();
+ m_allExcludeFromScanRegExps.clear();
+ m_audioExcludeFromScanRegExps.clear();
+ m_audioExcludeFromListingRegExps.clear();
+ m_pictureExcludeFromListingRegExps.clear();
+
+ m_pictureExtensions.clear();
+ m_musicExtensions.clear();
+ m_videoExtensions.clear();
+ m_discStubExtensions.clear();
+
+ m_userAgent.clear();
+}
+
+void CAdvancedSettings::GetCustomTVRegexps(TiXmlElement *pRootElement, SETTINGS_TVSHOWLIST& settings)
+{
+ TiXmlElement *pElement = pRootElement;
+ while (pElement)
+ {
+ int iAction = 0; // overwrite
+ // for backward compatibility
+ const char* szAppend = pElement->Attribute("append");
+ if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
+ iAction = 1;
+ // action takes precedence if both attributes exist
+ const char* szAction = pElement->Attribute("action");
+ if (szAction)
+ {
+ iAction = 0; // overwrite
+ if (StringUtils::CompareNoCase(szAction, "append") == 0)
+ iAction = 1; // append
+ else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
+ iAction = 2; // prepend
+ }
+ if (iAction == 0)
+ settings.clear();
+ TiXmlNode* pRegExp = pElement->FirstChild("regexp");
+ int i = 0;
+ while (pRegExp)
+ {
+ if (pRegExp->FirstChild())
+ {
+ bool bByDate = false;
+ bool byTitle = false;
+ int iDefaultSeason = 1;
+ if (pRegExp->ToElement())
+ {
+ std::string byDate = XMLUtils::GetAttribute(pRegExp->ToElement(), "bydate");
+ if (byDate == "true")
+ {
+ bByDate = true;
+ }
+ std::string byTitleAttr = XMLUtils::GetAttribute(pRegExp->ToElement(), "bytitle");
+ byTitle = (byTitleAttr == "true");
+ std::string defaultSeason = XMLUtils::GetAttribute(pRegExp->ToElement(), "defaultseason");
+ if(!defaultSeason.empty())
+ {
+ iDefaultSeason = atoi(defaultSeason.c_str());
+ }
+ }
+ std::string regExp = pRegExp->FirstChild()->Value();
+ if (iAction == 2)
+ {
+ settings.insert(settings.begin() + i++, 1,
+ TVShowRegexp(bByDate, regExp, iDefaultSeason, byTitle));
+ }
+ else
+ {
+ settings.push_back(TVShowRegexp(bByDate, regExp, iDefaultSeason, byTitle));
+ }
+ }
+ pRegExp = pRegExp->NextSibling("regexp");
+ }
+
+ pElement = pElement->NextSiblingElement(pRootElement->Value());
+ }
+}
+
+void CAdvancedSettings::GetCustomRegexps(TiXmlElement *pRootElement, std::vector<std::string>& settings)
+{
+ TiXmlElement *pElement = pRootElement;
+ while (pElement)
+ {
+ int iAction = 0; // overwrite
+ // for backward compatibility
+ const char* szAppend = pElement->Attribute("append");
+ if ((szAppend && StringUtils::CompareNoCase(szAppend, "yes") == 0))
+ iAction = 1;
+ // action takes precedence if both attributes exist
+ const char* szAction = pElement->Attribute("action");
+ if (szAction)
+ {
+ iAction = 0; // overwrite
+ if (StringUtils::CompareNoCase(szAction, "append") == 0)
+ iAction = 1; // append
+ else if (StringUtils::CompareNoCase(szAction, "prepend") == 0)
+ iAction = 2; // prepend
+ }
+ if (iAction == 0)
+ settings.clear();
+ TiXmlNode* pRegExp = pElement->FirstChild("regexp");
+ int i = 0;
+ while (pRegExp)
+ {
+ if (pRegExp->FirstChild())
+ {
+ std::string regExp = pRegExp->FirstChild()->Value();
+ if (iAction == 2)
+ settings.insert(settings.begin() + i++, 1, regExp);
+ else
+ settings.push_back(regExp);
+ }
+ pRegExp = pRegExp->NextSibling("regexp");
+ }
+
+ pElement = pElement->NextSiblingElement(pRootElement->Value());
+ }
+}
+
+void CAdvancedSettings::GetCustomExtensions(TiXmlElement *pRootElement, std::string& extensions)
+{
+ std::string extraExtensions;
+ if (XMLUtils::GetString(pRootElement, "add", extraExtensions) && !extraExtensions.empty())
+ extensions += "|" + extraExtensions;
+ if (XMLUtils::GetString(pRootElement, "remove", extraExtensions) && !extraExtensions.empty())
+ {
+ std::vector<std::string> exts = StringUtils::Split(extraExtensions, '|');
+ for (std::vector<std::string>::const_iterator i = exts.begin(); i != exts.end(); ++i)
+ {
+ size_t iPos = extensions.find(*i);
+ if (iPos != std::string::npos)
+ extensions.erase(iPos,i->size()+1);
+ }
+ }
+}
+
+void CAdvancedSettings::AddSettingsFile(const std::string &filename)
+{
+ m_settingsFiles.push_back(filename);
+}
+
+float CAdvancedSettings::GetLatencyTweak(float refreshrate)
+{
+ float delay = m_videoDefaultLatency;
+ for (int i = 0; i < (int) m_videoRefreshLatency.size(); i++)
+ {
+ RefreshVideoLatency& videolatency = m_videoRefreshLatency[i];
+ if (refreshrate >= videolatency.refreshmin && refreshrate <= videolatency.refreshmax)
+ delay = videolatency.delay;
+ }
+
+ return delay; // in milliseconds
+}
+
+void CAdvancedSettings::SetDebugMode(bool debug)
+{
+ if (debug)
+ {
+ int level = std::max(m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
+ m_logLevel = level;
+ CServiceBroker::GetLogging().SetLogLevel(level);
+ CLog::Log(LOGINFO, "Enabled debug logging due to GUI setting. Level {}.", level);
+ }
+ else
+ {
+ int level = std::min(m_logLevelHint, LOG_LEVEL_DEBUG/*LOG_LEVEL_NORMAL*/);
+ CLog::Log(LOGINFO, "Disabled debug logging due to GUI setting. Level {}.", level);
+ m_logLevel = level;
+ CServiceBroker::GetLogging().SetLogLevel(level);
+ }
+}
+
+void CAdvancedSettings::SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap)
+{
+ if (!arttypes)
+ return;
+ artworkMap.clear();
+ const TiXmlNode* arttype = arttypes->FirstChild("arttype");
+ while (arttype)
+ {
+ if (arttype->FirstChild())
+ artworkMap.push_back(arttype->FirstChild()->ValueStr());
+ arttype = arttype->NextSibling("arttype");
+ }
+}
+
+void ConvertToWhitelist(const std::vector<std::string>& oldlist, std::vector<CVariant>& whitelist)
+{
+ for (auto& it : oldlist)
+ {
+ size_t last_index = it.find_last_not_of("0123456789");
+ std::string strFamilyType = it.substr(0, last_index + 1); // "fanart" of "fanart16"
+ if (std::find(whitelist.begin(), whitelist.end(), strFamilyType) == whitelist.end())
+ whitelist.emplace_back(strFamilyType);
+ }
+}
+
+void CAdvancedSettings::MigrateOldArtSettings()
+{
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ if (!settings->GetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED))
+ {
+ CLog::Log(LOGINFO, "Migrating old music library artwork settings to new GUI settings");
+ // Convert numeric art type variants into simple art type family entry
+ // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
+ if (!m_musicArtistExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_musicArtistExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_ARTISTART_WHITELIST, whitelist);
+ }
+ if (!m_musicAlbumExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_musicAlbumExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_ALBUMART_WHITELIST, whitelist);
+ }
+
+ // Convert value like "folder.jpg|Folder.jpg|folder.JPG|Folder.JPG|cover.jpg|Cover.jpg|
+ // cover.jpeg|thumb.jpg|Thumb.jpg|thumb.JPG|Thumb.JPG" into case-insensitive unique elements
+ // e.g. {"folder.jpg", "cover.jpg", "cover.jpeg", "thumb.jpg"}
+ if (!m_musicThumbs.empty())
+ {
+ std::vector<std::string> thumbs1 = StringUtils::Split(m_musicThumbs, "|");
+ std::vector<std::string> thumbs2;
+ for (auto& it : thumbs1)
+ {
+ StringUtils::ToLower(it);
+ if (std::find(thumbs2.begin(), thumbs2.end(), it) == thumbs2.end())
+ thumbs2.emplace_back(it);
+ }
+ std::vector<CVariant> thumbs;
+ thumbs.reserve(thumbs2.size());
+ for (const auto& it : thumbs2)
+ thumbs.emplace_back(it);
+ settings->SetList(CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS, thumbs);
+ }
+
+ // Whitelists configured, set artwork level to custom
+ if (!m_musicAlbumExtraArt.empty() || !m_musicArtistExtraArt.empty())
+ settings->SetInt(CSettings::SETTING_MUSICLIBRARY_ARTWORKLEVEL, 2);
+
+ // Flag migration of settings so not done again
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED, true);
+ }
+
+ if (!settings->GetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED))
+ {
+ CLog::Log(LOGINFO, "Migrating old video library artwork settings to new GUI settings");
+ // Convert numeric art type variants into simple art type family entry
+ // e.g. {"banner", "fanart1", "fanart2", "fanart3"... } into { "banner", "fanart"}
+ if (!m_videoEpisodeExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoEpisodeExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST, whitelist);
+ }
+ if (!m_videoTvShowExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoTvShowExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST, whitelist);
+ }
+ if (!m_videoMovieExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoMovieExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST, whitelist);
+ }
+ if (!m_videoMusicVideoExtraArt.empty())
+ {
+ std::vector<CVariant> whitelist;
+ ConvertToWhitelist(m_videoMusicVideoExtraArt, whitelist);
+ settings->SetList(CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST, whitelist);
+ }
+
+ // Whitelists configured, set artwork level to custom
+ if (!m_videoEpisodeExtraArt.empty() || !m_videoTvShowExtraArt.empty()
+ || !m_videoMovieExtraArt.empty() || !m_videoMusicVideoExtraArt.empty())
+ settings->SetInt(CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL,
+ CSettings::MUSICLIBRARY_ARTWORK_LEVEL_CUSTOM);
+
+ // Flag migration of settings so not done again
+ settings->SetBool(CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED, true);
+ }
+}
diff --git a/xbmc/settings/AdvancedSettings.h b/xbmc/settings/AdvancedSettings.h
new file mode 100644
index 0000000..4ba4e7e
--- /dev/null
+++ b/xbmc/settings/AdvancedSettings.h
@@ -0,0 +1,391 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "pictures/PictureScalingAlgorithm.h"
+#include "settings/lib/ISettingCallback.h"
+#include "settings/lib/ISettingsHandler.h"
+#include "utils/SortUtils.h"
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#define CACHE_BUFFER_MODE_INTERNET 0
+#define CACHE_BUFFER_MODE_ALL 1
+#define CACHE_BUFFER_MODE_TRUE_INTERNET 2
+#define CACHE_BUFFER_MODE_NONE 3
+#define CACHE_BUFFER_MODE_NETWORK 4
+
+class CProfileManager;
+class CSettingsManager;
+class CVariant;
+struct IntegerSettingOption;
+
+class TiXmlElement;
+namespace ADDON
+{
+ class IAddon;
+}
+
+class DatabaseSettings
+{
+public:
+ DatabaseSettings() { Reset(); }
+ void Reset()
+ {
+ type.clear();
+ host.clear();
+ port.clear();
+ user.clear();
+ pass.clear();
+ name.clear();
+ key.clear();
+ cert.clear();
+ ca.clear();
+ capath.clear();
+ ciphers.clear();
+ compression = false;
+ };
+ std::string type;
+ std::string host;
+ std::string port;
+ std::string user;
+ std::string pass;
+ std::string name;
+ std::string key;
+ std::string cert;
+ std::string ca;
+ std::string capath;
+ std::string ciphers;
+ bool compression;
+};
+
+struct TVShowRegexp
+{
+ bool byDate;
+ bool byTitle;
+ std::string regexp;
+ int defaultSeason;
+ TVShowRegexp(bool d, const std::string& r, int s = 1, bool t = false) : regexp(r)
+ {
+ byDate = d;
+ defaultSeason = s;
+ byTitle = t;
+ }
+};
+
+struct RefreshOverride
+{
+ float fpsmin;
+ float fpsmax;
+
+ float refreshmin;
+ float refreshmax;
+
+ bool fallback;
+};
+
+
+struct RefreshVideoLatency
+{
+ float refreshmin;
+ float refreshmax;
+
+ float delay;
+};
+
+typedef std::vector<TVShowRegexp> SETTINGS_TVSHOWLIST;
+
+class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
+{
+ public:
+ CAdvancedSettings();
+
+ void OnSettingsLoaded() override;
+ void OnSettingsUnloaded() override;
+
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ void Initialize(CSettingsManager& settingsMgr);
+ void Uninitialize(CSettingsManager& settingsMgr);
+ bool Initialized() const { return m_initialized; }
+ void AddSettingsFile(const std::string &filename);
+ bool Load(const CProfileManager &profileManager);
+
+ static void GetCustomTVRegexps(TiXmlElement *pRootElement, SETTINGS_TVSHOWLIST& settings);
+ static void GetCustomRegexps(TiXmlElement *pRootElement, std::vector<std::string> &settings);
+ static void GetCustomExtensions(TiXmlElement *pRootElement, std::string& extensions);
+
+ std::string m_audioDefaultPlayer;
+ float m_audioPlayCountMinimumPercent;
+ bool m_VideoPlayerIgnoreDTSinWAV;
+ float m_limiterHold;
+ float m_limiterRelease;
+
+ bool m_omlSync = true;
+
+ float m_videoSubsDelayRange;
+ float m_videoAudioDelayRange;
+ bool m_videoUseTimeSeeking;
+ int m_videoTimeSeekForward;
+ int m_videoTimeSeekBackward;
+ int m_videoTimeSeekForwardBig;
+ int m_videoTimeSeekBackwardBig;
+ int m_videoPercentSeekForward;
+ int m_videoPercentSeekBackward;
+ int m_videoPercentSeekForwardBig;
+ int m_videoPercentSeekBackwardBig;
+ std::vector<int> m_seekSteps;
+ std::string m_videoPPFFmpegPostProc;
+ bool m_videoVDPAUtelecine;
+ bool m_videoVDPAUdeintSkipChromaHD;
+ bool m_musicUseTimeSeeking;
+ int m_musicTimeSeekForward;
+ int m_musicTimeSeekBackward;
+ int m_musicTimeSeekForwardBig;
+ int m_musicTimeSeekBackwardBig;
+ int m_musicPercentSeekForward;
+ int m_musicPercentSeekBackward;
+ int m_musicPercentSeekForwardBig;
+ int m_musicPercentSeekBackwardBig;
+ int m_videoIgnoreSecondsAtStart;
+ float m_videoIgnorePercentAtEnd;
+ float m_audioApplyDrc;
+ unsigned int m_maxPassthroughOffSyncDuration = 10; // when 10 ms off adjust
+
+ int m_videoVDPAUScaling;
+ float m_videoNonLinStretchRatio;
+ float m_videoAutoScaleMaxFps;
+ std::vector<RefreshOverride> m_videoAdjustRefreshOverrides;
+ std::vector<RefreshVideoLatency> m_videoRefreshLatency;
+ float m_videoDefaultLatency;
+ int m_videoCaptureUseOcclusionQuery;
+ bool m_DXVACheckCompatibility;
+ bool m_DXVACheckCompatibilityPresent;
+ int m_videoFpsDetect;
+ float m_maxTempo;
+ bool m_videoPreferStereoStream = false;
+
+ std::string m_videoDefaultPlayer;
+ float m_videoPlayCountMinimumPercent;
+
+ float m_slideshowBlackBarCompensation;
+ float m_slideshowZoomAmount;
+ float m_slideshowPanAmount;
+
+ int m_songInfoDuration;
+ int m_logLevel;
+ int m_logLevelHint;
+ std::string m_cddbAddress;
+ bool m_addSourceOnTop; //!< True to put 'add source' buttons on top
+
+ //airtunes + airplay
+ int m_airTunesPort;
+ int m_airPlayPort;
+
+ /*! \brief Only used in linux for the udisks and udisks2 providers
+ * defines if kodi should automount media drives
+ * @note if kodi is running standalone (--standalone option) it will
+ * be set to tue
+ */
+ bool m_handleMounting;
+ /*! \brief Only used in linux for the udisks and udisks2 providers
+ * defines if kodi should automount optical discs
+ */
+ bool m_autoMountOpticalMedia{true};
+
+ bool m_fullScreenOnMovieStart;
+ std::string m_cachePath;
+ std::string m_videoCleanDateTimeRegExp;
+ std::vector<std::string> m_videoCleanStringRegExps;
+ std::vector<std::string> m_videoExcludeFromListingRegExps;
+ std::vector<std::string> m_allExcludeFromScanRegExps;
+ std::vector<std::string> m_moviesExcludeFromScanRegExps;
+ std::vector<std::string> m_tvshowExcludeFromScanRegExps;
+ std::vector<std::string> m_audioExcludeFromListingRegExps;
+ std::vector<std::string> m_audioExcludeFromScanRegExps;
+ std::vector<std::string> m_pictureExcludeFromListingRegExps;
+ std::vector<std::string> m_videoStackRegExps;
+ std::vector<std::string> m_folderStackRegExps;
+ std::vector<std::string> m_trailerMatchRegExps;
+ SETTINGS_TVSHOWLIST m_tvshowEnumRegExps;
+ std::string m_tvshowMultiPartEnumRegExp;
+ typedef std::vector< std::pair<std::string, std::string> > StringMapping;
+ StringMapping m_pathSubstitutions;
+ int m_remoteDelay; ///< \brief number of remote messages to ignore before repeating
+ bool m_bScanIRServer;
+
+ bool m_playlistAsFolders;
+ bool m_detectAsUdf;
+
+ unsigned int m_fanartRes; ///< \brief the maximal resolution to cache fanart at (assumes 16x9)
+ unsigned int m_imageRes; ///< \brief the maximal resolution to cache images at (assumes 16x9)
+ CPictureScalingAlgorithm::Algorithm m_imageScalingAlgorithm;
+ unsigned int
+ m_imageQualityJpeg; ///< \brief the stored jpeg quality the lower the better (default: 4)
+
+ int m_sambaclienttimeout;
+ std::string m_sambadoscodepage;
+ bool m_sambastatfiles;
+
+ bool m_bHTTPDirectoryStatFilesize;
+
+ bool m_bFTPThumbs;
+ bool m_bShoutcastArt;
+
+ std::string m_musicThumbs;
+ std::vector<std::string> m_musicArtistExtraArt;
+ std::vector<std::string> m_musicAlbumExtraArt;
+
+ int m_iMusicLibraryRecentlyAddedItems;
+ int m_iMusicLibraryDateAdded;
+ bool m_bMusicLibraryAllItemsOnBottom;
+ bool m_bMusicLibraryCleanOnUpdate;
+ bool m_bMusicLibraryArtistSortOnUpdate;
+ bool m_bMusicLibraryUseISODates;
+ std::string m_strMusicLibraryAlbumFormat;
+ bool m_prioritiseAPEv2tags;
+ std::string m_musicItemSeparator;
+ std::vector<std::string> m_musicArtistSeparators;
+ std::string m_videoItemSeparator;
+ std::vector<std::string> m_musicTagsFromFileFilters;
+
+ bool m_bVideoLibraryAllItemsOnBottom;
+ int m_iVideoLibraryRecentlyAddedItems;
+ bool m_bVideoLibraryCleanOnUpdate;
+ bool m_bVideoLibraryUseFastHash;
+ bool m_bVideoLibraryImportWatchedState{true};
+ bool m_bVideoLibraryImportResumePoint{true};
+ std::vector<std::string> m_videoEpisodeExtraArt;
+ std::vector<std::string> m_videoTvShowExtraArt;
+ std::vector<std::string> m_videoTvSeasonExtraArt;
+ std::vector<std::string> m_videoMovieExtraArt;
+ std::vector<std::string> m_videoMovieSetExtraArt;
+ std::vector<std::string> m_videoMusicVideoExtraArt;
+
+ bool m_bVideoScannerIgnoreErrors;
+ int m_iVideoLibraryDateAdded;
+
+ std::set<std::string> m_vecTokens;
+
+ int m_iEpgUpdateCheckInterval; // seconds
+ int m_iEpgCleanupInterval; // seconds
+ int m_iEpgActiveTagCheckInterval; // seconds
+ int m_iEpgRetryInterruptedUpdateInterval; // seconds
+ int m_iEpgUpdateEmptyTagsInterval; // seconds
+ bool m_bEpgDisplayUpdatePopup;
+ bool m_bEpgDisplayIncrementalUpdatePopup;
+
+ // EDL Commercial Break
+ bool m_bEdlMergeShortCommBreaks;
+ /*!< @brief If GUI notifications should be shown when reaching the start of commercial breaks */
+ bool m_EdlDisplayCommbreakNotifications;
+ int m_iEdlMaxCommBreakLength; // seconds
+ int m_iEdlMinCommBreakLength; // seconds
+ int m_iEdlMaxCommBreakGap; // seconds
+ int m_iEdlMaxStartGap; // seconds
+ int m_iEdlCommBreakAutowait; // seconds
+ int m_iEdlCommBreakAutowind; // seconds
+
+ int m_curlconnecttimeout;
+ int m_curllowspeedtime;
+ int m_curlretries;
+ int m_curlKeepAliveInterval; // seconds
+ bool m_curlDisableIPV6;
+ bool m_curlDisableHTTP2;
+
+ std::string m_caTrustFile;
+
+ bool m_minimizeToTray; /* win32 only */
+ bool m_fullScreen;
+ bool m_startFullScreen;
+ bool m_showExitButton; /* Ideal for appliances to hide a 'useless' button */
+ bool m_canWindowed;
+ bool m_splashImage;
+ bool m_alwaysOnTop; /* makes xbmc to run always on top .. osx/win32 only .. */
+ int m_playlistRetries;
+ int m_playlistTimeout;
+ bool m_GLRectangleHack;
+ int m_iSkipLoopFilter;
+
+ bool m_bVirtualShares;
+
+ std::string m_cpuTempCmd;
+ std::string m_gpuTempCmd;
+
+ /* PVR/TV related advanced settings */
+ int m_iPVRTimeCorrection; /*!< @brief correct all times (epg tags, timer tags, recording tags) by this amount of minutes. defaults to 0. */
+ int m_iPVRInfoToggleInterval; /*!< @brief if there are more than 1 pvr gui info item available (e.g. multiple recordings active at the same time), use this toggle delay in milliseconds. defaults to 3000. */
+ bool m_bPVRChannelIconsAutoScan; /*!< @brief automatically scan user defined folder for channel icons when loading internal channel groups */
+ bool m_bPVRAutoScanIconsUserSet; /*!< @brief mark channel icons populated by auto scan as "user set" */
+ int m_iPVRNumericChannelSwitchTimeout; /*!< @brief time in msecs after that a channel switch occurs after entering a channel number, if confirmchannelswitch is disabled */
+ int m_iPVRTimeshiftThreshold; /*!< @brief time diff between current playing time and timeshift buffer end, in seconds, before a playing stream is displayed as timeshifting. */
+ bool m_bPVRTimeshiftSimpleOSD; /*!< @brief use simple timeshift OSD (with progress only for the playing event instead of progress for the whole ts buffer). */
+ SortDescription m_PVRDefaultSortOrder; /*!< @brief SortDecription used to store default recording sort type and sort order */
+
+ DatabaseSettings m_databaseMusic; // advanced music database setup
+ DatabaseSettings m_databaseVideo; // advanced video database setup
+ DatabaseSettings m_databaseTV; // advanced tv database setup
+ DatabaseSettings m_databaseEpg; /*!< advanced EPG database setup */
+
+ bool m_useLocaleCollation;
+
+ bool m_guiVisualizeDirtyRegions;
+ int m_guiAlgorithmDirtyRegions;
+ bool m_guiSmartRedraw;
+ unsigned int m_addonPackageFolderSize;
+
+ unsigned int m_cacheMemSize;
+ unsigned int m_cacheBufferMode;
+ unsigned int m_cacheChunkSize;
+ float m_cacheReadFactor;
+
+ bool m_jsonOutputCompact;
+ unsigned int m_jsonTcpPort;
+
+ bool m_enableMultimediaKeys;
+ std::vector<std::string> m_settingsFiles;
+ void ParseSettingsFile(const std::string &file);
+
+ float GetLatencyTweak(float refreshrate);
+ bool m_initialized;
+
+ void SetDebugMode(bool debug);
+
+ //! \brief Toggles dirty-region visualization
+ void ToggleDirtyRegionVisualization()
+ {
+ m_guiVisualizeDirtyRegions = !m_guiVisualizeDirtyRegions;
+ }
+
+ // runtime settings which cannot be set from advancedsettings.xml
+ std::string m_videoExtensions;
+ std::string m_discStubExtensions;
+ std::string m_subtitlesExtensions;
+ std::string m_musicExtensions;
+ std::string m_pictureExtensions;
+
+ std::string m_stereoscopicregex_3d;
+ std::string m_stereoscopicregex_sbs;
+ std::string m_stereoscopicregex_tab;
+
+ bool m_openGlDebugging;
+
+ std::string m_userAgent;
+ uint32_t m_nfsTimeout;
+ int m_nfsRetries;
+
+ private:
+ void Initialize();
+ void Clear();
+ void SetExtraArtwork(const TiXmlElement* arttypes, std::vector<std::string>& artworkMap);
+ void MigrateOldArtSettings();
+};
diff --git a/xbmc/settings/CMakeLists.txt b/xbmc/settings/CMakeLists.txt
new file mode 100644
index 0000000..f53d12b
--- /dev/null
+++ b/xbmc/settings/CMakeLists.txt
@@ -0,0 +1,49 @@
+set(SOURCES AdvancedSettings.cpp
+ DisplaySettings.cpp
+ GameSettings.cpp
+ LibExportSettings.cpp
+ MediaSettings.cpp
+ MediaSourceSettings.cpp
+ SettingAddon.cpp
+ SettingConditions.cpp
+ SettingControl.cpp
+ SettingCreator.cpp
+ SettingDateTime.cpp
+ SettingPath.cpp
+ Settings.cpp
+ SettingsBase.cpp
+ SettingsValueFlatJsonSerializer.cpp
+ SettingsValueXmlSerializer.cpp
+ SettingUtils.cpp
+ SkinSettings.cpp
+ SettingsComponent.cpp
+ SubtitlesSettings.cpp)
+
+set(HEADERS AdvancedSettings.h
+ DiscSettings.h
+ DisplaySettings.h
+ GameSettings.h
+ ISubSettings.h
+ LibExportSettings.h
+ MediaSettings.h
+ MediaSourceSettings.h
+ SettingAddon.h
+ SettingConditions.h
+ SettingControl.h
+ SettingCreator.h
+ SettingDateTime.h
+ SettingPath.h
+ Settings.h
+ SettingsBase.h
+ SettingsValueFlatJsonSerializer.h
+ SettingsValueXmlSerializer.h
+ SettingUtils.h
+ SkinSettings.h
+ SettingsComponent.h
+ SubtitlesSettings.h)
+
+if(BLURAY_FOUND)
+ list(APPEND SOURCES DiscSettings.cpp)
+endif()
+
+core_add_library(settings)
diff --git a/xbmc/settings/DiscSettings.cpp b/xbmc/settings/DiscSettings.cpp
new file mode 100644
index 0000000..de8bb33
--- /dev/null
+++ b/xbmc/settings/DiscSettings.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017-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 "DiscSettings.h"
+
+#include "Settings.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "lib/Setting.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <string>
+
+#include <libbluray/bluray-version.h>
+#include <libbluray/bluray.h>
+
+using namespace KODI::MESSAGING;
+
+CDiscSettings& CDiscSettings::GetInstance()
+{
+ static CDiscSettings sDiscSettings;
+ return sDiscSettings;
+}
+
+void CDiscSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+#if (BLURAY_VERSION >= BLURAY_VERSION_CODE(1,0,1))
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_DISC_PLAYBACK)
+ {
+ int mode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ if (mode == BD_PLAYBACK_DISC_MENU)
+ {
+ bool bdjWorking = false;
+ BLURAY* bd = bd_init();
+ const BLURAY_DISC_INFO* info = bd_get_disc_info(bd);
+
+ if (!info->libjvm_detected)
+ CLog::Log(LOGDEBUG, "DiscSettings - Could not load the java vm.");
+ else if (!info->bdj_handled)
+ CLog::Log(LOGDEBUG, "DiscSettings - Could not load the libbluray.jar.");
+ else
+ bdjWorking = true;
+
+ bd_close(bd);
+
+ if (!bdjWorking)
+ HELPERS::ShowOKDialogText(CVariant{ 29803 }, CVariant{ 29804 });
+ }
+ }
+#endif
+}
diff --git a/xbmc/settings/DiscSettings.h b/xbmc/settings/DiscSettings.h
new file mode 100644
index 0000000..9d62eea
--- /dev/null
+++ b/xbmc/settings/DiscSettings.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+/**
+* Playback settings
+*/
+enum BDPlaybackMode
+{
+ BD_PLAYBACK_SIMPLE_MENU = 0,
+ BD_PLAYBACK_DISC_MENU,
+ BD_PLAYBACK_MAIN_TITLE,
+};
+
+#include "settings/lib/ISettingCallback.h"
+
+class CDiscSettings : public ISettingCallback
+{
+public:
+ /* ISettingCallback*/
+
+ static CDiscSettings& GetInstance();
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+private:
+ CDiscSettings() = default;
+ ~CDiscSettings() override = default;
+};
diff --git a/xbmc/settings/DisplaySettings.cpp b/xbmc/settings/DisplaySettings.cpp
new file mode 100644
index 0000000..25f230a
--- /dev/null
+++ b/xbmc/settings/DisplaySettings.cpp
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2013-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 "DisplaySettings.h"
+
+#include "ServiceBroker.h"
+#include "cores/VideoPlayer/VideoRenderers/ColorManager.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/LocalizeStrings.h"
+#include "guilib/StereoscopicsManager.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "rendering/RenderSystem.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+#include "windowing/GraphicContext.h"
+#include "windowing/WinSystem.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <float.h>
+#include <mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+#ifdef TARGET_WINDOWS
+#include "rendering/dx/DeviceResources.h"
+#endif
+
+using namespace KODI::MESSAGING;
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+// 0.1 second increments
+#define MAX_REFRESH_CHANGE_DELAY 200
+
+static RESOLUTION_INFO EmptyResolution;
+static RESOLUTION_INFO EmptyModifiableResolution;
+
+float square_error(float x, float y)
+{
+ float yonx = (x > 0) ? y / x : 0;
+ float xony = (y > 0) ? x / y : 0;
+ return std::max(yonx, xony);
+}
+
+static std::string ModeFlagsToString(unsigned int flags, bool identifier)
+{
+ std::string res;
+ if(flags & D3DPRESENTFLAG_INTERLACED)
+ res += "i";
+ else
+ res += "p";
+
+ if(!identifier)
+ res += " ";
+
+ if(flags & D3DPRESENTFLAG_MODE3DSBS)
+ res += "sbs";
+ else if(flags & D3DPRESENTFLAG_MODE3DTB)
+ res += "tab";
+ else if(identifier)
+ res += "std";
+ return res;
+}
+
+CDisplaySettings::CDisplaySettings()
+{
+ m_resolutions.resize(RES_CUSTOM);
+
+ m_zoomAmount = 1.0f;
+ m_pixelRatio = 1.0f;
+ m_verticalShift = 0.0f;
+ m_nonLinearStretched = false;
+ m_resolutionChangeAborted = false;
+}
+
+CDisplaySettings::~CDisplaySettings() = default;
+
+CDisplaySettings& CDisplaySettings::GetInstance()
+{
+ static CDisplaySettings sDisplaySettings;
+ return sDisplaySettings;
+}
+
+bool CDisplaySettings::Load(const TiXmlNode *settings)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+
+ if (settings == NULL)
+ return false;
+
+ const TiXmlElement *pElement = settings->FirstChildElement("resolutions");
+ if (!pElement)
+ {
+ CLog::Log(LOGERROR, "CDisplaySettings: settings file doesn't contain <resolutions>");
+ return false;
+ }
+
+ const TiXmlElement *pResolution = pElement->FirstChildElement("resolution");
+ while (pResolution)
+ {
+ // get the data for this calibration
+ RESOLUTION_INFO cal;
+
+ XMLUtils::GetString(pResolution, "description", cal.strMode);
+ XMLUtils::GetInt(pResolution, "subtitles", cal.iSubtitles);
+ XMLUtils::GetFloat(pResolution, "pixelratio", cal.fPixelRatio);
+#ifdef HAVE_X11
+ XMLUtils::GetFloat(pResolution, "refreshrate", cal.fRefreshRate);
+ XMLUtils::GetString(pResolution, "output", cal.strOutput);
+ XMLUtils::GetString(pResolution, "xrandrid", cal.strId);
+#endif
+
+ const TiXmlElement *pOverscan = pResolution->FirstChildElement("overscan");
+ if (pOverscan)
+ {
+ XMLUtils::GetInt(pOverscan, "left", cal.Overscan.left);
+ XMLUtils::GetInt(pOverscan, "top", cal.Overscan.top);
+ XMLUtils::GetInt(pOverscan, "right", cal.Overscan.right);
+ XMLUtils::GetInt(pOverscan, "bottom", cal.Overscan.bottom);
+ }
+
+ // mark calibration as not updated
+ // we must not delete those, resolution just might not be available
+ cal.iWidth = cal.iHeight = 0;
+
+ // store calibration, avoid adding duplicates
+ bool found = false;
+ for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
+ {
+ if (StringUtils::EqualsNoCase(it->strMode, cal.strMode))
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ m_calibrations.push_back(cal);
+
+ // iterate around
+ pResolution = pResolution->NextSiblingElement("resolution");
+ }
+
+ ApplyCalibrations();
+ return true;
+}
+
+bool CDisplaySettings::Save(TiXmlNode *settings) const
+{
+ if (settings == NULL)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ TiXmlElement xmlRootElement("resolutions");
+ TiXmlNode *pRoot = settings->InsertEndChild(xmlRootElement);
+ if (pRoot == NULL)
+ return false;
+
+ // save calibrations
+ for (ResolutionInfos::const_iterator it = m_calibrations.begin(); it != m_calibrations.end(); ++it)
+ {
+ // Write the resolution tag
+ TiXmlElement resElement("resolution");
+ TiXmlNode *pNode = pRoot->InsertEndChild(resElement);
+ if (pNode == NULL)
+ return false;
+
+ // Now write each of the pieces of information we need...
+ XMLUtils::SetString(pNode, "description", it->strMode);
+ XMLUtils::SetInt(pNode, "subtitles", it->iSubtitles);
+ XMLUtils::SetFloat(pNode, "pixelratio", it->fPixelRatio);
+#ifdef HAVE_X11
+ XMLUtils::SetFloat(pNode, "refreshrate", it->fRefreshRate);
+ XMLUtils::SetString(pNode, "output", it->strOutput);
+ XMLUtils::SetString(pNode, "xrandrid", it->strId);
+#endif
+
+ // create the overscan child
+ TiXmlElement overscanElement("overscan");
+ TiXmlNode *pOverscanNode = pNode->InsertEndChild(overscanElement);
+ if (pOverscanNode == NULL)
+ return false;
+
+ XMLUtils::SetInt(pOverscanNode, "left", it->Overscan.left);
+ XMLUtils::SetInt(pOverscanNode, "top", it->Overscan.top);
+ XMLUtils::SetInt(pOverscanNode, "right", it->Overscan.right);
+ XMLUtils::SetInt(pOverscanNode, "bottom", it->Overscan.bottom);
+ }
+
+ return true;
+}
+
+void CDisplaySettings::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+ m_resolutions.clear();
+ m_resolutions.resize(RES_CUSTOM);
+
+ m_zoomAmount = 1.0f;
+ m_pixelRatio = 1.0f;
+ m_verticalShift = 0.0f;
+ m_nonLinearStretched = false;
+}
+
+void CDisplaySettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == "videoscreen.cms3dlut")
+ {
+ std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".3dlut", g_localizeStrings.Get(36580), path))
+ {
+ std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
+ }
+ }
+ else if (settingId == "videoscreen.displayprofile")
+ {
+ std::string path = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, ".icc|.icm", g_localizeStrings.Get(36581), path))
+ {
+ std::static_pointer_cast<CSettingString>(std::const_pointer_cast<CSetting>(setting))->SetValue(path);
+ }
+ }
+}
+
+bool CDisplaySettings::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION ||
+ settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
+ {
+ RESOLUTION newRes = RES_DESKTOP;
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_RESOLUTION)
+ newRes = (RESOLUTION)std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREEN)
+ {
+ int screen = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+
+ // if triggered by a change of screenmode, screen may not have changed
+ if (screen == GetCurrentDisplayMode())
+ return true;
+
+ // get desktop resolution for screen
+ newRes = GetResolutionForScreen();
+ }
+
+ std::string screenmode = GetStringFromResolution(newRes);
+ if (!CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE, screenmode))
+ return false;
+ }
+
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
+ {
+ RESOLUTION oldRes = GetCurrentResolution();
+ RESOLUTION newRes = GetResolutionFromString(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
+
+ SetCurrentResolution(newRes, false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(newRes, false);
+
+ // check if the old or the new resolution was/is windowed
+ // in which case we don't show any prompt to the user
+ if (oldRes != RES_WINDOW && newRes != RES_WINDOW && oldRes != newRes)
+ {
+ if (!m_resolutionChangeAborted)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""},
+ CVariant{""}, 15000) != DialogResponse::CHOICE_YES)
+ {
+ m_resolutionChangeAborted = true;
+ return false;
+ }
+ }
+ else
+ m_resolutionChangeAborted = false;
+ }
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_MONITOR)
+ {
+ CServiceBroker::GetWinSystem()->UpdateResolutions();
+ RESOLUTION newRes = GetResolutionForScreen();
+
+ SetCurrentResolution(newRes, false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(newRes, true);
+
+ if (!m_resolutionChangeAborted)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{13110}, CVariant{13111}, CVariant{""}, CVariant{""},
+ 10000) != DialogResponse::CHOICE_YES)
+ {
+ m_resolutionChangeAborted = true;
+ return false;
+ }
+ }
+ else
+ m_resolutionChangeAborted = false;
+
+ return true;
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_10BITSURFACES)
+ {
+#ifdef TARGET_WINDOWS
+ DX::DeviceResources::Get()->ApplyDisplaySettings();
+ return true;
+#endif
+ }
+#if defined(HAVE_X11) || defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS)
+ {
+ auto winSystem = CServiceBroker::GetWinSystem();
+#if defined(HAVE_X11)
+ winSystem->UpdateResolutions();
+#elif defined(TARGET_WINDOWS_DESKTOP) || defined(TARGET_DARWIN_OSX)
+ CGraphicContext& gfxContext = winSystem->GetGfxContext();
+ gfxContext.SetVideoResolution(gfxContext.GetVideoResolution(), true);
+#endif
+ }
+#endif
+
+ return true;
+}
+
+bool CDisplaySettings::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ if (setting == NULL)
+ return false;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_SCREENMODE)
+ {
+ std::shared_ptr<CSettingString> screenmodeSetting = std::static_pointer_cast<CSettingString>(setting);
+ std::string screenmode = screenmodeSetting->GetValue();
+ // in Eden there was no character ("i" or "p") indicating interlaced/progressive
+ // at the end so we just add a "p" and assume progressive
+ // no 3d mode existed before, so just assume std modes
+ if (screenmode.size() == 20)
+ return screenmodeSetting->SetValue(screenmode + "pstd");
+ if (screenmode.size() == 21)
+ return screenmodeSetting->SetValue(screenmode + "std");
+ }
+ else if (settingId == CSettings::SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE)
+ {
+ std::shared_ptr<CSettingInt> stereomodeSetting = std::static_pointer_cast<CSettingInt>(setting);
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ STEREOSCOPIC_PLAYBACK_MODE playbackMode = (STEREOSCOPIC_PLAYBACK_MODE) settings->GetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE);
+ if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_OFF)
+ {
+ // if preferred playback mode was OFF, update playback mode to ignore
+ if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
+ settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_IGNORE);
+ return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
+ }
+ else if (stereomodeSetting->GetValue() == RENDER_STEREO_MODE_MONO)
+ {
+ // if preferred playback mode was MONO, update playback mode
+ if (playbackMode == STEREOSCOPIC_PLAYBACK_MODE_PREFERRED)
+ settings->SetInt(CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE, STEREOSCOPIC_PLAYBACK_MODE_MONO);
+ return stereomodeSetting->SetValue(RENDER_STEREO_MODE_AUTO);
+ }
+ }
+
+ return false;
+}
+
+void CDisplaySettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (!setting)
+ return;
+
+ const std::string& settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_VIDEOSCREEN_WHITELIST)
+ CResolutionUtils::PrintWhitelist();
+}
+
+void CDisplaySettings::SetMonitor(const std::string& monitor)
+{
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ const std::string curMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ if (curMonitor != monitor)
+ {
+ m_resolutionChangeAborted = true;
+ settings->SetString(CSettings::SETTING_VIDEOSCREEN_MONITOR, monitor);
+ }
+}
+
+void CDisplaySettings::SetCurrentResolution(RESOLUTION resolution, bool save /* = false */)
+{
+ if (resolution == RES_WINDOW && !CServiceBroker::GetWinSystem()->CanDoWindowed())
+ resolution = RES_DESKTOP;
+
+ if (save)
+ {
+ // Save videoscreen.screenmode setting
+ std::string mode = GetStringFromResolution(resolution);
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE, mode.c_str());
+
+ // Check if videoscreen.screen setting also needs to be saved
+ // e.g. if ToggleFullscreen is called
+ int currentDisplayMode = GetCurrentDisplayMode();
+ int currentDisplayModeSetting = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
+ if (currentDisplayMode != currentDisplayModeSetting)
+ {
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN, currentDisplayMode);
+ }
+ }
+ else if (resolution != m_currentResolution)
+ {
+ m_currentResolution = resolution;
+ SetChanged();
+ }
+}
+
+RESOLUTION CDisplaySettings::GetDisplayResolution() const
+{
+ return GetResolutionFromString(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_VIDEOSCREEN_SCREENMODE));
+}
+
+const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index >= m_resolutions.size())
+ return EmptyResolution;
+
+ return m_resolutions[index];
+}
+
+const RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution) const
+{
+ if (resolution <= RES_INVALID)
+ return EmptyResolution;
+
+ return GetResolutionInfo((size_t)resolution);
+}
+
+RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(size_t index)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index >= m_resolutions.size())
+ {
+ EmptyModifiableResolution = RESOLUTION_INFO();
+ return EmptyModifiableResolution;
+ }
+
+ return m_resolutions[index];
+}
+
+RESOLUTION_INFO& CDisplaySettings::GetResolutionInfo(RESOLUTION resolution)
+{
+ if (resolution <= RES_INVALID)
+ {
+ EmptyModifiableResolution = RESOLUTION_INFO();
+ return EmptyModifiableResolution;
+ }
+
+ return GetResolutionInfo((size_t)resolution);
+}
+
+void CDisplaySettings::AddResolutionInfo(const RESOLUTION_INFO &resolution)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ RESOLUTION_INFO res(resolution);
+
+ if((res.dwFlags & D3DPRESENTFLAG_MODE3DTB) == 0)
+ {
+ /* add corrections for some special case modes frame packing modes */
+
+ if(res.iScreenWidth == 1920
+ && res.iScreenHeight == 2205)
+ {
+ res.iBlanking = 45;
+ res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+ }
+
+ if(res.iScreenWidth == 1280
+ && res.iScreenHeight == 1470)
+ {
+ res.iBlanking = 30;
+ res.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
+ }
+ }
+ m_resolutions.push_back(res);
+}
+
+void CDisplaySettings::ApplyCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // apply all calibrations to the resolutions
+ for (ResolutionInfos::const_iterator itCal = m_calibrations.begin(); itCal != m_calibrations.end(); ++itCal)
+ {
+ // find resolutions
+ for (size_t res = RES_DESKTOP; res < m_resolutions.size(); ++res)
+ {
+ if (StringUtils::EqualsNoCase(itCal->strMode, m_resolutions[res].strMode))
+ {
+ // overscan
+ m_resolutions[res].Overscan.left = itCal->Overscan.left;
+ if (m_resolutions[res].Overscan.left < -m_resolutions[res].iWidth/4)
+ m_resolutions[res].Overscan.left = -m_resolutions[res].iWidth/4;
+ if (m_resolutions[res].Overscan.left > m_resolutions[res].iWidth/4)
+ m_resolutions[res].Overscan.left = m_resolutions[res].iWidth/4;
+
+ m_resolutions[res].Overscan.top = itCal->Overscan.top;
+ if (m_resolutions[res].Overscan.top < -m_resolutions[res].iHeight/4)
+ m_resolutions[res].Overscan.top = -m_resolutions[res].iHeight/4;
+ if (m_resolutions[res].Overscan.top > m_resolutions[res].iHeight/4)
+ m_resolutions[res].Overscan.top = m_resolutions[res].iHeight/4;
+
+ m_resolutions[res].Overscan.right = itCal->Overscan.right;
+ if (m_resolutions[res].Overscan.right < m_resolutions[res].iWidth / 2)
+ m_resolutions[res].Overscan.right = m_resolutions[res].iWidth / 2;
+ if (m_resolutions[res].Overscan.right > m_resolutions[res].iWidth * 3/2)
+ m_resolutions[res].Overscan.right = m_resolutions[res].iWidth *3/2;
+
+ m_resolutions[res].Overscan.bottom = itCal->Overscan.bottom;
+ if (m_resolutions[res].Overscan.bottom < m_resolutions[res].iHeight / 2)
+ m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight / 2;
+ if (m_resolutions[res].Overscan.bottom > m_resolutions[res].iHeight * 3/2)
+ m_resolutions[res].Overscan.bottom = m_resolutions[res].iHeight * 3/2;
+
+ m_resolutions[res].iSubtitles = itCal->iSubtitles;
+ if (m_resolutions[res].iSubtitles < 0)
+ m_resolutions[res].iSubtitles = 0;
+ if (m_resolutions[res].iSubtitles > m_resolutions[res].iHeight * 3 / 2)
+ m_resolutions[res].iSubtitles = m_resolutions[res].iHeight * 3 / 2;
+
+ m_resolutions[res].fPixelRatio = itCal->fPixelRatio;
+ if (m_resolutions[res].fPixelRatio < 0.5f)
+ m_resolutions[res].fPixelRatio = 0.5f;
+ if (m_resolutions[res].fPixelRatio > 2.0f)
+ m_resolutions[res].fPixelRatio = 2.0f;
+ break;
+ }
+ }
+ }
+}
+
+void CDisplaySettings::UpdateCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ if (m_resolutions.size() <= RES_DESKTOP)
+ return;
+
+ // Add new (unique) resolutions
+ for (ResolutionInfos::const_iterator res(m_resolutions.cbegin() + RES_CUSTOM); res != m_resolutions.cend(); ++res)
+ if (std::find_if(m_calibrations.cbegin(), m_calibrations.cend(),
+ [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(res->strMode, info.strMode); }) == m_calibrations.cend())
+ m_calibrations.push_back(*res);
+
+ for (auto &cal : m_calibrations)
+ {
+ ResolutionInfos::const_iterator res(std::find_if(m_resolutions.cbegin() + RES_DESKTOP, m_resolutions.cend(),
+ [&](const RESOLUTION_INFO& info) { return StringUtils::EqualsNoCase(cal.strMode, info.strMode); }));
+
+ if (res != m_resolutions.cend())
+ {
+ //! @todo erase calibrations with default values
+ cal = *res;
+ }
+ }
+}
+
+void CDisplaySettings::ClearCalibrations()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_calibrations.clear();
+}
+
+DisplayMode CDisplaySettings::GetCurrentDisplayMode() const
+{
+ if (GetCurrentResolution() == RES_WINDOW)
+ return DM_WINDOWED;
+
+ return DM_FULLSCREEN;
+}
+
+RESOLUTION CDisplaySettings::FindBestMatchingResolution(const std::map<RESOLUTION, RESOLUTION_INFO> &resolutionInfos, int width, int height, float refreshrate, unsigned flags)
+{
+ // find the closest match to these in our res vector. If we have the screen, we score the res
+ RESOLUTION bestRes = RES_DESKTOP;
+ float bestScore = FLT_MAX;
+ flags &= D3DPRESENTFLAG_MODEMASK;
+
+ for (std::map<RESOLUTION, RESOLUTION_INFO>::const_iterator it = resolutionInfos.begin(); it != resolutionInfos.end(); ++it)
+ {
+ const RESOLUTION_INFO &info = it->second;
+
+ if ((info.dwFlags & D3DPRESENTFLAG_MODEMASK) != flags)
+ continue;
+
+ float score = 10 * (square_error((float)info.iScreenWidth, (float)width) +
+ square_error((float)info.iScreenHeight, (float)height) +
+ square_error(info.fRefreshRate, refreshrate));
+ if (score < bestScore)
+ {
+ bestScore = score;
+ bestRes = it->first;
+ }
+ }
+
+ return bestRes;
+}
+
+RESOLUTION CDisplaySettings::GetResolutionFromString(const std::string &strResolution)
+{
+
+ if (strResolution == "DESKTOP")
+ return RES_DESKTOP;
+ else if (strResolution == "WINDOW")
+ return RES_WINDOW;
+ else if (strResolution.size() >= 20)
+ {
+ // format: WWWWWHHHHHRRR.RRRRRP333, where W = width, H = height, R = refresh, P = interlace, 3 = stereo mode
+ int width = std::strtol(StringUtils::Mid(strResolution, 0,5).c_str(), NULL, 10);
+ int height = std::strtol(StringUtils::Mid(strResolution, 5,5).c_str(), NULL, 10);
+ float refresh = (float)std::strtod(StringUtils::Mid(strResolution, 10,9).c_str(), NULL);
+ unsigned flags = 0;
+
+ // look for 'i' and treat everything else as progressive,
+ if(StringUtils::Mid(strResolution, 19,1) == "i")
+ flags |= D3DPRESENTFLAG_INTERLACED;
+
+ if(StringUtils::Mid(strResolution, 20,3) == "sbs")
+ flags |= D3DPRESENTFLAG_MODE3DSBS;
+ else if(StringUtils::Mid(strResolution, 20,3) == "tab")
+ flags |= D3DPRESENTFLAG_MODE3DTB;
+
+ std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
+ for (size_t resolution = RES_DESKTOP; resolution < CDisplaySettings::GetInstance().ResolutionInfoSize(); resolution++)
+ resolutionInfos.insert(std::make_pair((RESOLUTION)resolution, CDisplaySettings::GetInstance().GetResolutionInfo(resolution)));
+
+ return FindBestMatchingResolution(resolutionInfos, width, height, refresh, flags);
+ }
+
+ return RES_DESKTOP;
+}
+
+std::string CDisplaySettings::GetStringFromResolution(RESOLUTION resolution, float refreshrate /* = 0.0f */)
+{
+ if (resolution == RES_WINDOW)
+ return "WINDOW";
+
+ if (resolution >= RES_DESKTOP && resolution < (RESOLUTION)CDisplaySettings::GetInstance().ResolutionInfoSize())
+ {
+ const RESOLUTION_INFO &info = CDisplaySettings::GetInstance().GetResolutionInfo(resolution);
+ // also handle RES_DESKTOP resolutions with non-default refresh rates
+ if (resolution != RES_DESKTOP || (refreshrate > 0.0f && refreshrate != info.fRefreshRate))
+ {
+ return StringUtils::Format("{:05}{:05}{:09.5f}{}", info.iScreenWidth, info.iScreenHeight,
+ refreshrate > 0.0f ? refreshrate : info.fRefreshRate,
+ ModeFlagsToString(info.dwFlags, true));
+ }
+ }
+
+ return "DESKTOP";
+}
+
+RESOLUTION CDisplaySettings::GetResolutionForScreen()
+{
+ DisplayMode mode = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOSCREEN_SCREEN);
+ if (mode == DM_WINDOWED)
+ return RES_WINDOW;
+
+ return RES_DESKTOP;
+}
+
+static inline bool ModeSort(const StringSettingOption& i, const StringSettingOption& j)
+{
+ return (i.value > j.value);
+}
+
+void CDisplaySettings::SettingOptionsModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ for (auto index = (unsigned int)RES_CUSTOM; index < CDisplaySettings::GetInstance().ResolutionInfoSize(); ++index)
+ {
+ const auto mode = CDisplaySettings::GetInstance().GetResolutionInfo(index);
+
+ if (mode.dwFlags ^ D3DPRESENTFLAG_INTERLACED)
+ {
+ auto setting = GetStringFromResolution((RESOLUTION)index, mode.fRefreshRate);
+
+ list.emplace_back(
+ StringUtils::Format("{}x{}{} {:0.2f}Hz", mode.iScreenWidth, mode.iScreenHeight,
+ ModeFlagsToString(mode.dwFlags, false), mode.fRefreshRate),
+ setting);
+ }
+ }
+
+ std::sort(list.begin(), list.end(), ModeSort);
+}
+
+void CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(13551), 0);
+
+ for (int i = 1; i <= MAX_REFRESH_CHANGE_DELAY; i++)
+ list.emplace_back(
+ StringUtils::Format(g_localizeStrings.Get(13553), static_cast<double>(i) / 10.0), i);
+}
+
+void CDisplaySettings::SettingOptionsRefreshRatesFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ // get the proper resolution
+ RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
+ if (res < RES_WINDOW)
+ return;
+
+ // only add "Windowed" if in windowed mode
+ if (res == RES_WINDOW)
+ {
+ current = "WINDOW";
+ list.emplace_back(g_localizeStrings.Get(242), current);
+ return;
+ }
+
+ RESOLUTION_INFO resInfo = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ // The only meaningful parts of res here are iScreenWidth, iScreenHeight
+ std::vector<REFRESHRATE> refreshrates = CServiceBroker::GetWinSystem()->RefreshRates(resInfo.iScreenWidth, resInfo.iScreenHeight, resInfo.dwFlags);
+
+ bool match = false;
+ for (std::vector<REFRESHRATE>::const_iterator refreshrate = refreshrates.begin(); refreshrate != refreshrates.end(); ++refreshrate)
+ {
+ std::string screenmode = GetStringFromResolution((RESOLUTION)refreshrate->ResInfo_Index, refreshrate->RefreshRate);
+ if (!match && StringUtils::EqualsNoCase(std::static_pointer_cast<const CSettingString>(setting)->GetValue(), screenmode))
+ match = true;
+ list.emplace_back(StringUtils::Format("{:.2f}", refreshrate->RefreshRate), screenmode);
+ }
+
+ if (!match)
+ current = GetStringFromResolution(res, CServiceBroker::GetWinSystem()->DefaultRefreshRate(refreshrates).RefreshRate);
+}
+
+void CDisplaySettings::SettingOptionsResolutionsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ RESOLUTION res = CDisplaySettings::GetInstance().GetDisplayResolution();
+ RESOLUTION_INFO info = CDisplaySettings::GetInstance().GetResolutionInfo(res);
+ if (res == RES_WINDOW)
+ {
+ current = res;
+ list.emplace_back(g_localizeStrings.Get(242), res);
+ }
+ else
+ {
+ std::map<RESOLUTION, RESOLUTION_INFO> resolutionInfos;
+ std::vector<RESOLUTION_WHR> resolutions = CServiceBroker::GetWinSystem()->ScreenResolutions(info.fRefreshRate);
+ for (std::vector<RESOLUTION_WHR>::const_iterator resolution = resolutions.begin(); resolution != resolutions.end(); ++resolution)
+ {
+ list.emplace_back(StringUtils::Format("{}x{}{}", resolution->width, resolution->height,
+ ModeFlagsToString(resolution->flags, false)),
+ resolution->ResInfo_Index);
+
+ resolutionInfos.insert(std::make_pair((RESOLUTION)resolution->ResInfo_Index, CDisplaySettings::GetInstance().GetResolutionInfo(resolution->ResInfo_Index)));
+ }
+
+ current = FindBestMatchingResolution(resolutionInfos,
+ info.iScreenWidth, info.iScreenHeight,
+ info.fRefreshRate, info.dwFlags);
+ }
+}
+
+void CDisplaySettings::SettingOptionsDispModeFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ // The user should only be able to disable windowed modes with the canwindowed
+ // setting. When the user sets canwindowed to true but the windowing system
+ // does not support windowed modes, we would just shoot ourselves in the foot
+ // by offering the option.
+ if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_canWindowed && CServiceBroker::GetWinSystem()->CanDoWindowed())
+ list.emplace_back(g_localizeStrings.Get(242), DM_WINDOWED);
+
+ list.emplace_back(g_localizeStrings.Get(244), DM_FULLSCREEN);
+}
+
+void CDisplaySettings::SettingOptionsStereoscopicModesFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui != nullptr)
+ {
+ const CStereoscopicsManager &stereoscopicsManager = gui->GetStereoscopicsManager();
+
+ for (int i = RENDER_STEREO_MODE_OFF; i < RENDER_STEREO_MODE_COUNT; i++)
+ {
+ RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
+ if (CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
+ }
+ }
+}
+
+void CDisplaySettings::SettingOptionsPreferredStereoscopicViewModesFiller(
+ const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ const CStereoscopicsManager &stereoscopicsManager = CServiceBroker::GetGUI()->GetStereoscopicsManager();
+
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(RENDER_STEREO_MODE_AUTO),
+ RENDER_STEREO_MODE_AUTO); // option for autodetect
+ // don't add "off" to the list of preferred modes as this doesn't make sense
+ for (int i = RENDER_STEREO_MODE_OFF +1; i < RENDER_STEREO_MODE_COUNT; i++)
+ {
+ RENDER_STEREO_MODE mode = (RENDER_STEREO_MODE) i;
+ // also skip "mono" mode which is no real stereoscopic mode
+ if (mode != RENDER_STEREO_MODE_MONO && CServiceBroker::GetRenderSystem()->SupportsStereo(mode))
+ list.emplace_back(stereoscopicsManager.GetLabelForStereoMode(mode), mode);
+ }
+}
+
+void CDisplaySettings::SettingOptionsMonitorsFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ auto winSystem = CServiceBroker::GetWinSystem();
+ if (!winSystem)
+ return;
+
+ auto settingsComponent = CServiceBroker::GetSettingsComponent();
+ if (!settingsComponent)
+ return;
+
+ auto settings = settingsComponent->GetSettings();
+ if (!settings)
+ return;
+
+ const std::vector<std::string> monitors = winSystem->GetConnectedOutputs();
+ std::string currentMonitor = settings->GetString(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+
+ bool foundMonitor = false;
+ for (auto const& monitor : monitors)
+ {
+ if(monitor == currentMonitor)
+ {
+ foundMonitor = true;
+ }
+ list.emplace_back(monitor, monitor);
+ }
+
+ if (!foundMonitor && !current.empty())
+ {
+ // Add current value so no monitor change is triggered when entering the settings screen and
+ // the preferred monitor is preserved
+ list.emplace_back(current, current);
+ }
+}
+
+void CDisplaySettings::ClearCustomResolutions()
+{
+ if (m_resolutions.size() > RES_CUSTOM)
+ {
+ std::vector<RESOLUTION_INFO>::iterator firstCustom = m_resolutions.begin()+RES_CUSTOM;
+ m_resolutions.erase(firstCustom, m_resolutions.end());
+ }
+}
+
+void CDisplaySettings::SettingOptionsCmsModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36580), CMS_MODE_3DLUT);
+#ifdef HAVE_LCMS2
+ list.emplace_back(g_localizeStrings.Get(36581), CMS_MODE_PROFILE);
+#endif
+}
+
+void CDisplaySettings::SettingOptionsCmsWhitepointsFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36586), CMS_WHITEPOINT_D65);
+ list.emplace_back(g_localizeStrings.Get(36587), CMS_WHITEPOINT_D93);
+}
+
+void CDisplaySettings::SettingOptionsCmsPrimariesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36588), CMS_PRIMARIES_AUTO);
+ list.emplace_back(g_localizeStrings.Get(36589), CMS_PRIMARIES_BT709);
+ list.emplace_back(g_localizeStrings.Get(36579), CMS_PRIMARIES_BT2020);
+ list.emplace_back(g_localizeStrings.Get(36590), CMS_PRIMARIES_170M);
+ list.emplace_back(g_localizeStrings.Get(36591), CMS_PRIMARIES_BT470M);
+ list.emplace_back(g_localizeStrings.Get(36592), CMS_PRIMARIES_BT470BG);
+ list.emplace_back(g_localizeStrings.Get(36593), CMS_PRIMARIES_240M);
+}
+
+void CDisplaySettings::SettingOptionsCmsGammaModesFiller(const SettingConstPtr& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data)
+{
+ list.emplace_back(g_localizeStrings.Get(36582), CMS_TRC_BT1886);
+ list.emplace_back(g_localizeStrings.Get(36583), CMS_TRC_INPUT_OFFSET);
+ list.emplace_back(g_localizeStrings.Get(36584), CMS_TRC_OUTPUT_OFFSET);
+ list.emplace_back(g_localizeStrings.Get(36585), CMS_TRC_ABSOLUTE);
+}
+
diff --git a/xbmc/settings/DisplaySettings.h b/xbmc/settings/DisplaySettings.h
new file mode 100644
index 0000000..e02e80c
--- /dev/null
+++ b/xbmc/settings/DisplaySettings.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "settings/ISubSettings.h"
+#include "settings/lib/ISettingCallback.h"
+#include "threads/CriticalSection.h"
+#include "utils/Observer.h"
+#include "windowing/Resolution.h"
+
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+class TiXmlNode;
+struct IntegerSettingOption;
+struct StringSettingOption;
+
+class CDisplaySettings : public ISettingCallback, public ISubSettings,
+ public Observable
+{
+public:
+ static CDisplaySettings& GetInstance();
+
+ bool Load(const TiXmlNode *settings) override;
+ bool Save(TiXmlNode *settings) const override;
+ void Clear() override;
+
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ /*!
+ \brief Returns the currently active resolution
+
+ This resolution might differ from the display resolution which is based on
+ the user's settings.
+
+ \sa SetCurrentResolution
+ \sa GetResolutionInfo
+ \sa GetDisplayResolution
+ */
+ RESOLUTION GetCurrentResolution() const { return m_currentResolution; }
+ void SetCurrentResolution(RESOLUTION resolution, bool save = false);
+ /*!
+ \brief Returns the best-matching resolution of the videoscreen.screenmode setting value
+
+ This resolution might differ from the current resolution which is based on
+ the properties of the operating system and the attached displays.
+
+ \sa GetCurrentResolution
+ */
+ RESOLUTION GetDisplayResolution() const;
+
+ const RESOLUTION_INFO& GetResolutionInfo(size_t index) const;
+ const RESOLUTION_INFO& GetResolutionInfo(RESOLUTION resolution) const;
+ RESOLUTION_INFO& GetResolutionInfo(size_t index);
+ RESOLUTION_INFO& GetResolutionInfo(RESOLUTION resolution);
+ size_t ResolutionInfoSize() const { return m_resolutions.size(); }
+ void AddResolutionInfo(const RESOLUTION_INFO &resolution);
+
+ const RESOLUTION_INFO& GetCurrentResolutionInfo() const { return GetResolutionInfo(m_currentResolution); }
+ RESOLUTION_INFO& GetCurrentResolutionInfo() { return GetResolutionInfo(m_currentResolution); }
+ RESOLUTION GetResFromString(const std::string &strResolution) { return GetResolutionFromString(strResolution); }
+ std::string GetStringFromRes(const RESOLUTION resolution, float refreshrate = 0.0f) { return GetStringFromResolution(resolution, refreshrate); }
+
+ void ApplyCalibrations();
+ void UpdateCalibrations();
+ void ClearCalibrations();
+ void ClearCustomResolutions();
+
+ float GetZoomAmount() const { return m_zoomAmount; }
+ void SetZoomAmount(float zoomAmount) { m_zoomAmount = zoomAmount; }
+ float GetPixelRatio() const { return m_pixelRatio; }
+ void SetPixelRatio(float pixelRatio) { m_pixelRatio = pixelRatio; }
+ float GetVerticalShift() const { return m_verticalShift; }
+ void SetVerticalShift(float verticalShift) { m_verticalShift = verticalShift; }
+ bool IsNonLinearStretched() const { return m_nonLinearStretched; }
+ void SetNonLinearStretched(bool nonLinearStretch) { m_nonLinearStretched = nonLinearStretch; }
+ void SetMonitor(const std::string& monitor);
+
+ static void SettingOptionsModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+ static void SettingOptionsRefreshChangeDelaysFiller(
+ const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsRefreshRatesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+ static void SettingOptionsResolutionsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsDispModeFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsStereoscopicModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsPreferredStereoscopicViewModesFiller(
+ const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsMonitorsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+ static void SettingOptionsCmsModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsCmsWhitepointsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsCmsPrimariesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+ static void SettingOptionsCmsGammaModesFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<IntegerSettingOption>& list,
+ int& current,
+ void* data);
+
+
+protected:
+ CDisplaySettings();
+ CDisplaySettings(const CDisplaySettings&) = delete;
+ CDisplaySettings& operator=(CDisplaySettings const&) = delete;
+ ~CDisplaySettings() override;
+
+ DisplayMode GetCurrentDisplayMode() const;
+
+ static RESOLUTION GetResolutionFromString(const std::string &strResolution);
+ static std::string GetStringFromResolution(RESOLUTION resolution, float refreshrate = 0.0f);
+ static RESOLUTION GetResolutionForScreen();
+
+ static RESOLUTION FindBestMatchingResolution(const std::map<RESOLUTION, RESOLUTION_INFO> &resolutionInfos, int width, int height, float refreshrate, unsigned int flags);
+
+private:
+ // holds the real gui resolution
+ RESOLUTION m_currentResolution;
+
+ typedef std::vector<RESOLUTION_INFO> ResolutionInfos;
+ ResolutionInfos m_resolutions;
+ ResolutionInfos m_calibrations;
+
+ float m_zoomAmount; // current zoom amount
+ float m_pixelRatio; // current pixel ratio
+ float m_verticalShift; // current vertical shift
+ bool m_nonLinearStretched; // current non-linear stretch
+
+ bool m_resolutionChangeAborted;
+ mutable CCriticalSection m_critical;
+};
diff --git a/xbmc/settings/GameSettings.cpp b/xbmc/settings/GameSettings.cpp
new file mode 100644
index 0000000..77d58ef
--- /dev/null
+++ b/xbmc/settings/GameSettings.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017-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 "GameSettings.h"
+
+using namespace KODI;
+
+CGameSettings &CGameSettings::operator=(const CGameSettings &rhs)
+{
+ if (this != &rhs)
+ {
+ m_videoFilter = rhs.m_videoFilter;
+ m_stretchMode = rhs.m_stretchMode;
+ m_rotationDegCCW = rhs.m_rotationDegCCW;
+ }
+ return *this;
+}
+
+void CGameSettings::Reset()
+{
+ m_videoFilter.clear();
+ m_stretchMode = RETRO::STRETCHMODE::Normal;
+ m_rotationDegCCW = 0;
+}
+
+bool CGameSettings::operator==(const CGameSettings &rhs) const
+{
+ return m_videoFilter == rhs.m_videoFilter &&
+ m_stretchMode == rhs.m_stretchMode &&
+ m_rotationDegCCW == rhs.m_rotationDegCCW;
+}
+
+void CGameSettings::SetVideoFilter(const std::string &videoFilter)
+{
+ if (videoFilter != m_videoFilter)
+ {
+ m_videoFilter = videoFilter;
+ SetChanged();
+ }
+}
+
+void CGameSettings::SetStretchMode(RETRO::STRETCHMODE stretchMode)
+{
+ if (stretchMode != m_stretchMode)
+ {
+ m_stretchMode = stretchMode;
+ SetChanged();
+ }
+}
+
+void CGameSettings::SetRotationDegCCW(unsigned int rotation)
+{
+ if (rotation != m_rotationDegCCW)
+ {
+ m_rotationDegCCW = rotation;
+ SetChanged();
+ }
+}
diff --git a/xbmc/settings/GameSettings.h b/xbmc/settings/GameSettings.h
new file mode 100644
index 0000000..f4c6424
--- /dev/null
+++ b/xbmc/settings/GameSettings.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+#include "cores/GameSettings.h"
+#include "utils/Observer.h"
+
+#include <string>
+
+class CGameSettings : public Observable
+{
+public:
+ CGameSettings() { Reset(); }
+ CGameSettings(const CGameSettings &other) { *this = other; }
+
+ CGameSettings &operator=(const CGameSettings &rhs);
+
+ // Restore game settings to default
+ void Reset();
+
+ bool operator==(const CGameSettings &rhs) const;
+ bool operator!=(const CGameSettings &rhs) const { return !(*this == rhs); }
+
+ const std::string &VideoFilter() const { return m_videoFilter; }
+ void SetVideoFilter(const std::string &videoFilter);
+
+ KODI::RETRO::STRETCHMODE StretchMode() const { return m_stretchMode; }
+ void SetStretchMode(KODI::RETRO::STRETCHMODE stretchMode);
+
+ unsigned int RotationDegCCW() const { return m_rotationDegCCW; }
+ void SetRotationDegCCW(unsigned int rotation);
+
+private:
+ // Video settings
+ std::string m_videoFilter;
+ KODI::RETRO::STRETCHMODE m_stretchMode;
+ unsigned int m_rotationDegCCW;
+};
diff --git a/xbmc/settings/ISubSettings.h b/xbmc/settings/ISubSettings.h
new file mode 100644
index 0000000..4cc2456
--- /dev/null
+++ b/xbmc/settings/ISubSettings.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+class TiXmlNode;
+
+/*!
+ \ingroup settings
+ \brief Interface defining methods to load additional setting values from an
+ XML file being loaded by the settings system.
+ */
+class ISubSettings
+{
+public:
+ virtual ~ISubSettings() = default;
+
+ /*!
+ \brief Load settings from the given XML node.
+
+ \param settings XML node containing setting values
+ \return True if loading the settings was successful, false otherwise.
+ */
+ virtual bool Load(const TiXmlNode *settings) { return true; }
+ /*!
+ \brief Save settings to the given XML node.
+
+ \param settings XML node in which the settings will be saved
+ \return True if saving the settings was successful, false otherwise.
+ */
+ virtual bool Save(TiXmlNode *settings) const { return true; }
+ /*!
+ \brief Clear any loaded setting values.
+ */
+ virtual void Clear() { }
+};
diff --git a/xbmc/settings/LibExportSettings.cpp b/xbmc/settings/LibExportSettings.cpp
new file mode 100644
index 0000000..15be272
--- /dev/null
+++ b/xbmc/settings/LibExportSettings.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+// LibExportSettings.cpp: implementation of the CLibExportSettings class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "LibExportSettings.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CLibExportSettings::CLibExportSettings()
+{
+ m_exporttype = ELIBEXPORT_SINGLEFILE;
+ m_itemstoexport = ELIBEXPORT_ALBUMS + ELIBEXPORT_ALBUMARTISTS;
+ m_overwrite = false;
+ m_artwork = false;
+ m_unscraped = false;
+ m_skipnfo = false;
+}
+
+bool CLibExportSettings::operator!=(const CLibExportSettings &right) const
+{
+ if (m_exporttype != right.m_exporttype)
+ return true;
+ if (m_strPath != right.m_strPath)
+ return true;
+ if (m_overwrite != right.m_overwrite)
+ return true;
+ if (m_itemstoexport != right.m_itemstoexport)
+ return true;
+
+ if (m_artwork != right.m_artwork)
+ return true;
+ if (m_unscraped != right.m_unscraped)
+ return true;
+ if (m_skipnfo != right.m_skipnfo)
+ return true;
+
+ return false;
+}
+
+bool CLibExportSettings::IsItemExported(ELIBEXPORTOPTIONS item) const
+{
+ return (m_itemstoexport & item);
+}
+
+bool CLibExportSettings::IsArtists() const
+{
+ return (m_itemstoexport & ELIBEXPORT_ALBUMARTISTS) ||
+ (m_itemstoexport & ELIBEXPORT_SONGARTISTS) ||
+ (m_itemstoexport & ELIBEXPORT_OTHERARTISTS);
+}
+
+std::vector<int> CLibExportSettings::GetExportItems() const
+{
+ std::vector<int> values;
+ if (IsItemExported(ELIBEXPORT_ALBUMS))
+ values.emplace_back(ELIBEXPORT_ALBUMS);
+ if (IsItemExported(ELIBEXPORT_ALBUMARTISTS))
+ values.emplace_back(ELIBEXPORT_ALBUMARTISTS);
+ if (IsItemExported(ELIBEXPORT_SONGARTISTS))
+ values.emplace_back(ELIBEXPORT_SONGARTISTS);
+ if (IsItemExported(ELIBEXPORT_OTHERARTISTS))
+ values.emplace_back(ELIBEXPORT_OTHERARTISTS);
+ if (IsItemExported(ELIBEXPORT_ACTORTHUMBS))
+ values.emplace_back(ELIBEXPORT_ACTORTHUMBS);
+ if (IsItemExported(ELIBEXPORT_SONGS))
+ values.emplace_back(ELIBEXPORT_SONGS);
+ return values;
+}
+
+std::vector<int> CLibExportSettings::GetLimitedItems(int items) const
+{
+ std::vector<int> values;
+ if (IsItemExported(ELIBEXPORT_ALBUMS) && (items & ELIBEXPORT_ALBUMS))
+ values.emplace_back(ELIBEXPORT_ALBUMS);
+ if (IsItemExported(ELIBEXPORT_ALBUMARTISTS) && (items & ELIBEXPORT_ALBUMARTISTS))
+ values.emplace_back(ELIBEXPORT_ALBUMARTISTS);
+ if (IsItemExported(ELIBEXPORT_SONGARTISTS) && (items & ELIBEXPORT_SONGARTISTS))
+ values.emplace_back(ELIBEXPORT_SONGARTISTS);
+ if (IsItemExported(ELIBEXPORT_OTHERARTISTS) && (items & ELIBEXPORT_OTHERARTISTS))
+ values.emplace_back(ELIBEXPORT_OTHERARTISTS);
+ if (IsItemExported(ELIBEXPORT_ACTORTHUMBS) && (items & ELIBEXPORT_ACTORTHUMBS))
+ values.emplace_back(ELIBEXPORT_ACTORTHUMBS);
+ if (IsItemExported(ELIBEXPORT_SONGS) && (items & ELIBEXPORT_SONGS))
+ values.emplace_back(ELIBEXPORT_SONGS);
+ return values;
+}
+
+bool CLibExportSettings::IsSingleFile() const
+{
+ return (m_exporttype == ELIBEXPORT_SINGLEFILE);
+}
+
+bool CLibExportSettings::IsSeparateFiles() const
+{
+ return (m_exporttype == ELIBEXPORT_SEPARATEFILES);
+}
+
+bool CLibExportSettings::IsToLibFolders() const
+{
+ return (m_exporttype == ELIBEXPORT_TOLIBRARYFOLDER);
+}
+
+bool CLibExportSettings::IsArtistFoldersOnly() const
+{
+ return (m_exporttype == ELIBEXPORT_ARTISTFOLDERS);
+}
diff --git a/xbmc/settings/LibExportSettings.h b/xbmc/settings/LibExportSettings.h
new file mode 100644
index 0000000..c7395fb
--- /dev/null
+++ b/xbmc/settings/LibExportSettings.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+// LibExportSettings.h: interface for the CLibExportSettings class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "settings/lib/Setting.h"
+
+#include <string>
+
+// Enumeration of library export options (possibly OR'd together)
+enum ELIBEXPORTOPTIONS
+{
+ ELIBEXPORT_SINGLEFILE = 0x0000,
+ ELIBEXPORT_SEPARATEFILES = 0x0001,
+ ELIBEXPORT_TOLIBRARYFOLDER = 0x0002,
+ ELIBEXPORT_OVERWRITE = 0x0004,
+ ELIBEXPORT_UNSCRAPED = 0x0008,
+ ELIBEXPORT_ALBUMS = 0x0010,
+ ELIBEXPORT_ALBUMARTISTS = 0x0020,
+ ELIBEXPORT_SONGARTISTS = 0x0040,
+ ELIBEXPORT_OTHERARTISTS = 0x0080,
+ ELIBEXPORT_ARTWORK = 0x0100,
+ ELIBEXPORT_NFOFILES = 0x0200,
+ ELIBEXPORT_ACTORTHUMBS = 0x0400,
+ ELIBEXPORT_ARTISTFOLDERS = 0x0800,
+ ELIBEXPORT_SONGS = 0x1000
+};
+
+class CLibExportSettings
+{
+public:
+ CLibExportSettings();
+ ~CLibExportSettings() = default;
+
+ bool operator!=(const CLibExportSettings &right) const;
+ bool IsItemExported(ELIBEXPORTOPTIONS item) const;
+ bool IsArtists() const;
+ std::vector<int> GetExportItems() const;
+ std::vector<int> GetLimitedItems(int items) const;
+ void ClearItems() { m_itemstoexport = 0; }
+ void AddItem(ELIBEXPORTOPTIONS item) { m_itemstoexport += item; }
+ unsigned int GetItemsToExport() { return m_itemstoexport; }
+ void SetItemsToExport(int itemstoexport) { m_itemstoexport = static_cast<unsigned int>(itemstoexport); }
+ unsigned int GetExportType() { return m_exporttype; }
+ void SetExportType(int exporttype) { m_exporttype = static_cast<unsigned int>(exporttype); }
+ bool IsSingleFile() const;
+ bool IsSeparateFiles() const;
+ bool IsToLibFolders() const;
+ bool IsArtistFoldersOnly() const;
+
+ std::string m_strPath;
+ bool m_overwrite;
+ bool m_artwork;
+ bool m_unscraped;
+ bool m_skipnfo;
+private:
+ unsigned int m_exporttype; //singlefile, separate files, to library folder
+ unsigned int m_itemstoexport;
+};
diff --git a/xbmc/settings/MediaSettings.cpp b/xbmc/settings/MediaSettings.cpp
new file mode 100644
index 0000000..09aad05
--- /dev/null
+++ b/xbmc/settings/MediaSettings.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2013-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 "MediaSettings.h"
+
+#include "PlayListPlayer.h"
+#include "ServiceBroker.h"
+#include "cores/RetroPlayer/RetroPlayerUtils.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "guilib/LocalizeStrings.h"
+#include "interfaces/AnnouncementManager.h"
+#include "interfaces/builtins/Builtins.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicLibraryQueue.h"
+#include "settings/Settings.h"
+#include "settings/dialogs/GUIDialogLibExportSettings.h"
+#include "settings/lib/Setting.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+#include "video/VideoDatabase.h"
+#include "video/VideoLibraryQueue.h"
+
+#include <limits.h>
+#include <mutex>
+#include <string>
+
+using namespace KODI;
+using namespace KODI::MESSAGING;
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+CMediaSettings::CMediaSettings()
+{
+ m_watchedModes["files"] = WatchedModeAll;
+ m_watchedModes["movies"] = WatchedModeAll;
+ m_watchedModes["tvshows"] = WatchedModeAll;
+ m_watchedModes["musicvideos"] = WatchedModeAll;
+ m_watchedModes["recordings"] = WatchedModeAll;
+
+ m_musicPlaylistRepeat = false;
+ m_musicPlaylistShuffle = false;
+ m_videoPlaylistRepeat = false;
+ m_videoPlaylistShuffle = false;
+
+ m_mediaStartWindowed = false;
+ m_additionalSubtitleDirectoryChecked = 0;
+
+ m_musicNeedsUpdate = 0;
+ m_videoNeedsUpdate = 0;
+}
+
+CMediaSettings::~CMediaSettings() = default;
+
+CMediaSettings& CMediaSettings::GetInstance()
+{
+ static CMediaSettings sMediaSettings;
+ return sMediaSettings;
+}
+
+bool CMediaSettings::Load(const TiXmlNode *settings)
+{
+ if (settings == NULL)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ const TiXmlElement *pElement = settings->FirstChildElement("defaultvideosettings");
+ if (pElement != NULL)
+ {
+ int interlaceMethod;
+ XMLUtils::GetInt(pElement, "interlacemethod", interlaceMethod, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_MAX);
+
+ m_defaultVideoSettings.m_InterlaceMethod = (EINTERLACEMETHOD)interlaceMethod;
+ int scalingMethod;
+ if (!XMLUtils::GetInt(pElement, "scalingmethod", scalingMethod, VS_SCALINGMETHOD_NEAREST, VS_SCALINGMETHOD_MAX))
+ scalingMethod = (int)VS_SCALINGMETHOD_LINEAR;
+ m_defaultVideoSettings.m_ScalingMethod = (ESCALINGMETHOD)scalingMethod;
+
+ XMLUtils::GetInt(pElement, "viewmode", m_defaultVideoSettings.m_ViewMode, ViewModeNormal, ViewModeZoom110Width);
+ if (!XMLUtils::GetFloat(pElement, "zoomamount", m_defaultVideoSettings.m_CustomZoomAmount, 0.5f, 2.0f))
+ m_defaultVideoSettings.m_CustomZoomAmount = 1.0f;
+ if (!XMLUtils::GetFloat(pElement, "pixelratio", m_defaultVideoSettings.m_CustomPixelRatio, 0.5f, 2.0f))
+ m_defaultVideoSettings.m_CustomPixelRatio = 1.0f;
+ if (!XMLUtils::GetFloat(pElement, "verticalshift", m_defaultVideoSettings.m_CustomVerticalShift, -2.0f, 2.0f))
+ m_defaultVideoSettings.m_CustomVerticalShift = 0.0f;
+ if (!XMLUtils::GetFloat(pElement, "volumeamplification", m_defaultVideoSettings.m_VolumeAmplification, VOLUME_DRC_MINIMUM * 0.01f, VOLUME_DRC_MAXIMUM * 0.01f))
+ m_defaultVideoSettings.m_VolumeAmplification = VOLUME_DRC_MINIMUM * 0.01f;
+ if (!XMLUtils::GetFloat(pElement, "noisereduction", m_defaultVideoSettings.m_NoiseReduction, 0.0f, 1.0f))
+ m_defaultVideoSettings.m_NoiseReduction = 0.0f;
+ XMLUtils::GetBoolean(pElement, "postprocess", m_defaultVideoSettings.m_PostProcess);
+ if (!XMLUtils::GetFloat(pElement, "sharpness", m_defaultVideoSettings.m_Sharpness, -1.0f, 1.0f))
+ m_defaultVideoSettings.m_Sharpness = 0.0f;
+ XMLUtils::GetBoolean(pElement, "showsubtitles", m_defaultVideoSettings.m_SubtitleOn);
+ if (!XMLUtils::GetFloat(pElement, "brightness", m_defaultVideoSettings.m_Brightness, 0, 100))
+ m_defaultVideoSettings.m_Brightness = 50;
+ if (!XMLUtils::GetFloat(pElement, "contrast", m_defaultVideoSettings.m_Contrast, 0, 100))
+ m_defaultVideoSettings.m_Contrast = 50;
+ if (!XMLUtils::GetFloat(pElement, "gamma", m_defaultVideoSettings.m_Gamma, 0, 100))
+ m_defaultVideoSettings.m_Gamma = 20;
+ if (!XMLUtils::GetFloat(pElement, "audiodelay", m_defaultVideoSettings.m_AudioDelay, -10.0f, 10.0f))
+ m_defaultVideoSettings.m_AudioDelay = 0.0f;
+ if (!XMLUtils::GetFloat(pElement, "subtitledelay", m_defaultVideoSettings.m_SubtitleDelay, -10.0f, 10.0f))
+ m_defaultVideoSettings.m_SubtitleDelay = 0.0f;
+ XMLUtils::GetBoolean(pElement, "nonlinstretch", m_defaultVideoSettings.m_CustomNonLinStretch);
+ if (!XMLUtils::GetInt(pElement, "stereomode", m_defaultVideoSettings.m_StereoMode))
+ m_defaultVideoSettings.m_StereoMode = 0;
+ if (!XMLUtils::GetInt(pElement, "centermixlevel", m_defaultVideoSettings.m_CenterMixLevel))
+ m_defaultVideoSettings.m_CenterMixLevel = 0;
+
+ int toneMapMethod;
+ if (!XMLUtils::GetInt(pElement, "tonemapmethod", toneMapMethod, VS_TONEMAPMETHOD_OFF,
+ VS_TONEMAPMETHOD_MAX))
+ toneMapMethod = VS_TONEMAPMETHOD_REINHARD;
+ m_defaultVideoSettings.m_ToneMapMethod = static_cast<ETONEMAPMETHOD>(toneMapMethod);
+
+ if (!XMLUtils::GetFloat(pElement, "tonemapparam", m_defaultVideoSettings.m_ToneMapParam, 0.1f, 5.0f))
+ m_defaultVideoSettings.m_ToneMapParam = 1.0f;
+ }
+
+ m_defaultGameSettings.Reset();
+ pElement = settings->FirstChildElement("defaultgamesettings");
+ if (pElement != nullptr)
+ {
+ std::string videoFilter;
+ if (XMLUtils::GetString(pElement, "videofilter", videoFilter))
+ m_defaultGameSettings.SetVideoFilter(videoFilter);
+
+ std::string stretchMode;
+ if (XMLUtils::GetString(pElement, "stretchmode", stretchMode))
+ {
+ RETRO::STRETCHMODE sm = RETRO::CRetroPlayerUtils::IdentifierToStretchMode(stretchMode);
+ m_defaultGameSettings.SetStretchMode(sm);
+ }
+
+ int rotation;
+ if (XMLUtils::GetInt(pElement, "rotation", rotation, 0, 270) && rotation >= 0)
+ m_defaultGameSettings.SetRotationDegCCW(static_cast<unsigned int>(rotation));
+ }
+
+ // mymusic settings
+ pElement = settings->FirstChildElement("mymusic");
+ if (pElement != NULL)
+ {
+ const TiXmlElement *pChild = pElement->FirstChildElement("playlist");
+ if (pChild != NULL)
+ {
+ XMLUtils::GetBoolean(pChild, "repeat", m_musicPlaylistRepeat);
+ XMLUtils::GetBoolean(pChild, "shuffle", m_musicPlaylistShuffle);
+ }
+ if (!XMLUtils::GetInt(pElement, "needsupdate", m_musicNeedsUpdate, 0, INT_MAX))
+ m_musicNeedsUpdate = 0;
+ }
+
+ // Set music playlist player repeat and shuffle from loaded settings
+ if (m_musicPlaylistRepeat)
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_MUSIC, PLAYLIST::RepeatState::ALL);
+ else
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_MUSIC,
+ PLAYLIST::RepeatState::NONE);
+ CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_MUSIC, m_musicPlaylistShuffle);
+
+ // Read the watchmode settings for the various media views
+ pElement = settings->FirstChildElement("myvideos");
+ if (pElement != NULL)
+ {
+ int tmp;
+ if (XMLUtils::GetInt(pElement, "watchmodemovies", tmp, (int)WatchedModeAll, (int)WatchedModeWatched))
+ m_watchedModes["movies"] = (WatchedMode)tmp;
+ if (XMLUtils::GetInt(pElement, "watchmodetvshows", tmp, (int)WatchedModeAll, (int)WatchedModeWatched))
+ m_watchedModes["tvshows"] = (WatchedMode)tmp;
+ if (XMLUtils::GetInt(pElement, "watchmodemusicvideos", tmp, (int)WatchedModeAll, (int)WatchedModeWatched))
+ m_watchedModes["musicvideos"] = (WatchedMode)tmp;
+ if (XMLUtils::GetInt(pElement, "watchmoderecordings", tmp, static_cast<int>(WatchedModeAll), static_cast<int>(WatchedModeWatched)))
+ m_watchedModes["recordings"] = static_cast<WatchedMode>(tmp);
+
+ const TiXmlElement *pChild = pElement->FirstChildElement("playlist");
+ if (pChild != NULL)
+ {
+ XMLUtils::GetBoolean(pChild, "repeat", m_videoPlaylistRepeat);
+ XMLUtils::GetBoolean(pChild, "shuffle", m_videoPlaylistShuffle);
+ }
+ if (!XMLUtils::GetInt(pElement, "needsupdate", m_videoNeedsUpdate, 0, INT_MAX))
+ m_videoNeedsUpdate = 0;
+ }
+
+ // Set video playlist player repeat and shuffle from loaded settings
+ if (m_videoPlaylistRepeat)
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_VIDEO, PLAYLIST::RepeatState::ALL);
+ else
+ CServiceBroker::GetPlaylistPlayer().SetRepeat(PLAYLIST::TYPE_VIDEO,
+ PLAYLIST::RepeatState::NONE);
+ CServiceBroker::GetPlaylistPlayer().SetShuffle(PLAYLIST::TYPE_VIDEO, m_videoPlaylistShuffle);
+
+ return true;
+}
+
+bool CMediaSettings::Save(TiXmlNode *settings) const
+{
+ if (settings == NULL)
+ return false;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // default video settings
+ TiXmlElement videoSettingsNode("defaultvideosettings");
+ TiXmlNode *pNode = settings->InsertEndChild(videoSettingsNode);
+ if (pNode == NULL)
+ return false;
+
+ XMLUtils::SetInt(pNode, "interlacemethod", m_defaultVideoSettings.m_InterlaceMethod);
+ XMLUtils::SetInt(pNode, "scalingmethod", m_defaultVideoSettings.m_ScalingMethod);
+ XMLUtils::SetFloat(pNode, "noisereduction", m_defaultVideoSettings.m_NoiseReduction);
+ XMLUtils::SetBoolean(pNode, "postprocess", m_defaultVideoSettings.m_PostProcess);
+ XMLUtils::SetFloat(pNode, "sharpness", m_defaultVideoSettings.m_Sharpness);
+ XMLUtils::SetInt(pNode, "viewmode", m_defaultVideoSettings.m_ViewMode);
+ XMLUtils::SetFloat(pNode, "zoomamount", m_defaultVideoSettings.m_CustomZoomAmount);
+ XMLUtils::SetFloat(pNode, "pixelratio", m_defaultVideoSettings.m_CustomPixelRatio);
+ XMLUtils::SetFloat(pNode, "verticalshift", m_defaultVideoSettings.m_CustomVerticalShift);
+ XMLUtils::SetFloat(pNode, "volumeamplification", m_defaultVideoSettings.m_VolumeAmplification);
+ XMLUtils::SetBoolean(pNode, "showsubtitles", m_defaultVideoSettings.m_SubtitleOn);
+ XMLUtils::SetFloat(pNode, "brightness", m_defaultVideoSettings.m_Brightness);
+ XMLUtils::SetFloat(pNode, "contrast", m_defaultVideoSettings.m_Contrast);
+ XMLUtils::SetFloat(pNode, "gamma", m_defaultVideoSettings.m_Gamma);
+ XMLUtils::SetFloat(pNode, "audiodelay", m_defaultVideoSettings.m_AudioDelay);
+ XMLUtils::SetFloat(pNode, "subtitledelay", m_defaultVideoSettings.m_SubtitleDelay);
+ XMLUtils::SetBoolean(pNode, "nonlinstretch", m_defaultVideoSettings.m_CustomNonLinStretch);
+ XMLUtils::SetInt(pNode, "stereomode", m_defaultVideoSettings.m_StereoMode);
+ XMLUtils::SetInt(pNode, "centermixlevel", m_defaultVideoSettings.m_CenterMixLevel);
+ XMLUtils::SetInt(pNode, "tonemapmethod", m_defaultVideoSettings.m_ToneMapMethod);
+ XMLUtils::SetFloat(pNode, "tonemapparam", m_defaultVideoSettings.m_ToneMapParam);
+
+ // default audio settings for dsp addons
+ TiXmlElement audioSettingsNode("defaultaudiosettings");
+ pNode = settings->InsertEndChild(audioSettingsNode);
+ if (pNode == NULL)
+ return false;
+
+ // Default game settings
+ TiXmlElement gameSettingsNode("defaultgamesettings");
+ pNode = settings->InsertEndChild(gameSettingsNode);
+ if (pNode == nullptr)
+ return false;
+
+ XMLUtils::SetString(pNode, "videofilter", m_defaultGameSettings.VideoFilter());
+ std::string sm = RETRO::CRetroPlayerUtils::StretchModeToIdentifier(m_defaultGameSettings.StretchMode());
+ XMLUtils::SetString(pNode, "stretchmode", sm);
+ XMLUtils::SetInt(pNode, "rotation", m_defaultGameSettings.RotationDegCCW());
+
+ // mymusic
+ pNode = settings->FirstChild("mymusic");
+ if (pNode == NULL)
+ {
+ TiXmlElement videosNode("mymusic");
+ pNode = settings->InsertEndChild(videosNode);
+ if (pNode == NULL)
+ return false;
+ }
+
+ TiXmlElement musicPlaylistNode("playlist");
+ TiXmlNode *playlistNode = pNode->InsertEndChild(musicPlaylistNode);
+ if (playlistNode == NULL)
+ return false;
+ XMLUtils::SetBoolean(playlistNode, "repeat", m_musicPlaylistRepeat);
+ XMLUtils::SetBoolean(playlistNode, "shuffle", m_musicPlaylistShuffle);
+
+ XMLUtils::SetInt(pNode, "needsupdate", m_musicNeedsUpdate);
+
+ // myvideos
+ pNode = settings->FirstChild("myvideos");
+ if (pNode == NULL)
+ {
+ TiXmlElement videosNode("myvideos");
+ pNode = settings->InsertEndChild(videosNode);
+ if (pNode == NULL)
+ return false;
+ }
+
+ XMLUtils::SetInt(pNode, "watchmodemovies", m_watchedModes.find("movies")->second);
+ XMLUtils::SetInt(pNode, "watchmodetvshows", m_watchedModes.find("tvshows")->second);
+ XMLUtils::SetInt(pNode, "watchmodemusicvideos", m_watchedModes.find("musicvideos")->second);
+ XMLUtils::SetInt(pNode, "watchmoderecordings", m_watchedModes.find("recordings")->second);
+
+ TiXmlElement videoPlaylistNode("playlist");
+ playlistNode = pNode->InsertEndChild(videoPlaylistNode);
+ if (playlistNode == NULL)
+ return false;
+ XMLUtils::SetBoolean(playlistNode, "repeat", m_videoPlaylistRepeat);
+ XMLUtils::SetBoolean(playlistNode, "shuffle", m_videoPlaylistShuffle);
+
+ XMLUtils::SetInt(pNode, "needsupdate", m_videoNeedsUpdate);
+
+ return true;
+}
+
+void CMediaSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_CLEANUP)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{313}, CVariant{333}) == DialogResponse::CHOICE_YES)
+ {
+ if (CMusicLibraryQueue::GetInstance().IsRunning())
+ HELPERS::ShowOKDialogText(CVariant{700}, CVariant{703});
+ else
+ CMusicLibraryQueue::GetInstance().CleanLibrary(true);
+ }
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT)
+ {
+ CLibExportSettings m_musicExportSettings;
+ if (CGUIDialogLibExportSettings::Show(m_musicExportSettings))
+ {
+ // Export music library showing progress dialog
+ CMusicLibraryQueue::GetInstance().ExportLibrary(m_musicExportSettings, true);
+ }
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_IMPORT)
+ {
+ std::string path;
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+ CServiceBroker::GetMediaManager().GetRemovableDrives(shares);
+
+ if (CGUIDialogFileBrowser::ShowAndGetFile(shares, "musicdb.xml", g_localizeStrings.Get(651) , path))
+ {
+ // Import data to music library showing progress dialog
+ CMusicLibraryQueue::GetInstance().ImportLibrary(path, true);
+ }
+ }
+ else if (settingId == CSettings::SETTING_VIDEOLIBRARY_CLEANUP)
+ {
+ if (HELPERS::ShowYesNoDialogText(CVariant{313}, CVariant{333}) == DialogResponse::CHOICE_YES)
+ {
+ if (!CVideoLibraryQueue::GetInstance().CleanLibraryModal())
+ HELPERS::ShowOKDialogText(CVariant{700}, CVariant{703});
+ }
+ }
+ else if (settingId == CSettings::SETTING_VIDEOLIBRARY_EXPORT)
+ CBuiltins::GetInstance().Execute("exportlibrary(video)");
+ else if (settingId == CSettings::SETTING_VIDEOLIBRARY_IMPORT)
+ {
+ std::string path;
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+ CServiceBroker::GetMediaManager().GetRemovableDrives(shares);
+
+ if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(651) , path))
+ {
+ CVideoDatabase videodatabase;
+ videodatabase.Open();
+ videodatabase.ImportFromXML(path);
+ videodatabase.Close();
+ }
+ }
+}
+
+void CMediaSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ if (setting->GetId() == CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS)
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::VideoLibrary, "OnRefresh");
+}
+
+int CMediaSettings::GetWatchedMode(const std::string &content) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ WatchedModes::const_iterator it = m_watchedModes.find(GetWatchedContent(content));
+ if (it != m_watchedModes.end())
+ return it->second;
+
+ return WatchedModeAll;
+}
+
+void CMediaSettings::SetWatchedMode(const std::string &content, WatchedMode mode)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ WatchedModes::iterator it = m_watchedModes.find(GetWatchedContent(content));
+ if (it != m_watchedModes.end())
+ it->second = mode;
+}
+
+void CMediaSettings::CycleWatchedMode(const std::string &content)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ WatchedModes::iterator it = m_watchedModes.find(GetWatchedContent(content));
+ if (it != m_watchedModes.end())
+ {
+ it->second = (WatchedMode)((int)it->second + 1);
+ if (it->second > WatchedModeWatched)
+ it->second = WatchedModeAll;
+ }
+}
+
+std::string CMediaSettings::GetWatchedContent(const std::string &content)
+{
+ if (content == "seasons" || content == "episodes")
+ return "tvshows";
+
+ return content;
+}
diff --git a/xbmc/settings/MediaSettings.h b/xbmc/settings/MediaSettings.h
new file mode 100644
index 0000000..d239234
--- /dev/null
+++ b/xbmc/settings/MediaSettings.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "cores/VideoSettings.h"
+#include "settings/GameSettings.h"
+#include "settings/ISubSettings.h"
+#include "settings/LibExportSettings.h"
+#include "settings/lib/ISettingCallback.h"
+#include "settings/lib/ISettingsHandler.h"
+#include "threads/CriticalSection.h"
+
+#include <map>
+#include <string>
+
+#define VOLUME_DRC_MINIMUM 0 // 0dB
+#define VOLUME_DRC_MAXIMUM 6000 // 60dB
+
+class TiXmlNode;
+
+// Step used to increase/decrease audio delay
+static constexpr float AUDIO_DELAY_STEP = 0.025f;
+
+typedef enum {
+ WatchedModeAll = 0,
+ WatchedModeUnwatched,
+ WatchedModeWatched
+} WatchedMode;
+
+class CMediaSettings : public ISettingCallback, public ISettingsHandler, public ISubSettings
+{
+public:
+ static CMediaSettings& GetInstance();
+
+ bool Load(const TiXmlNode *settings) override;
+ bool Save(TiXmlNode *settings) const override;
+
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ const CVideoSettings& GetDefaultVideoSettings() const { return m_defaultVideoSettings; }
+ CVideoSettings& GetDefaultVideoSettings() { return m_defaultVideoSettings; }
+
+ const CGameSettings& GetDefaultGameSettings() const { return m_defaultGameSettings; }
+ CGameSettings& GetDefaultGameSettings() { return m_defaultGameSettings; }
+ const CGameSettings& GetCurrentGameSettings() const { return m_currentGameSettings; }
+ CGameSettings& GetCurrentGameSettings() { return m_currentGameSettings; }
+
+ /*! \brief Retrieve the watched mode for the given content type
+ \param content Current content type
+ \return the current watch mode for this content type, WATCH_MODE_ALL if the content type is unknown.
+ \sa SetWatchMode
+ */
+ int GetWatchedMode(const std::string &content) const;
+
+ /*! \brief Set the watched mode for the given content type
+ \param content Current content type
+ \param value Watched mode to set
+ \sa GetWatchMode
+ */
+ void SetWatchedMode(const std::string &content, WatchedMode mode);
+
+ /*! \brief Cycle the watched mode for the given content type
+ \param content Current content type
+ \sa GetWatchMode, SetWatchMode
+ */
+ void CycleWatchedMode(const std::string &content);
+
+ void SetMusicPlaylistRepeat(bool repeats) { m_musicPlaylistRepeat = repeats; }
+ void SetMusicPlaylistShuffled(bool shuffled) { m_musicPlaylistShuffle = shuffled; }
+
+ void SetVideoPlaylistRepeat(bool repeats) { m_videoPlaylistRepeat = repeats; }
+ void SetVideoPlaylistShuffled(bool shuffled) { m_videoPlaylistShuffle = shuffled; }
+
+ bool DoesMediaStartWindowed() const { return m_mediaStartWindowed; }
+ void SetMediaStartWindowed(bool windowed) { m_mediaStartWindowed = windowed; }
+ int GetAdditionalSubtitleDirectoryChecked() const { return m_additionalSubtitleDirectoryChecked; }
+ void SetAdditionalSubtitleDirectoryChecked(int checked) { m_additionalSubtitleDirectoryChecked = checked; }
+
+ int GetMusicNeedsUpdate() const { return m_musicNeedsUpdate; }
+ void SetMusicNeedsUpdate(int version) { m_musicNeedsUpdate = version; }
+ int GetVideoNeedsUpdate() const { return m_videoNeedsUpdate; }
+ void SetVideoNeedsUpdate(int version) { m_videoNeedsUpdate = version; }
+
+protected:
+ CMediaSettings();
+ CMediaSettings(const CMediaSettings&) = delete;
+ CMediaSettings& operator=(CMediaSettings const&) = delete;
+ ~CMediaSettings() override;
+
+ static std::string GetWatchedContent(const std::string &content);
+
+private:
+ CVideoSettings m_defaultVideoSettings;
+
+ CGameSettings m_defaultGameSettings;
+ CGameSettings m_currentGameSettings;
+
+ typedef std::map<std::string, WatchedMode> WatchedModes;
+ WatchedModes m_watchedModes;
+
+ bool m_musicPlaylistRepeat;
+ bool m_musicPlaylistShuffle;
+ bool m_videoPlaylistRepeat;
+ bool m_videoPlaylistShuffle;
+
+ bool m_mediaStartWindowed;
+ int m_additionalSubtitleDirectoryChecked;
+
+ int m_musicNeedsUpdate; ///< if a database update means an update is required (set to the version number of the db)
+ int m_videoNeedsUpdate; ///< if a database update means an update is required (set to the version number of the db)
+
+ mutable CCriticalSection m_critical;
+};
diff --git a/xbmc/settings/MediaSourceSettings.cpp b/xbmc/settings/MediaSourceSettings.cpp
new file mode 100644
index 0000000..183131f
--- /dev/null
+++ b/xbmc/settings/MediaSourceSettings.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2013-2020 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 "MediaSourceSettings.h"
+
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "Util.h"
+#include "media/MediaLockState.h"
+#include "network/WakeOnAccess.h"
+#include "profiles/ProfileManager.h"
+#include "settings/SettingsComponent.h"
+#include "utils/FileUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <cstdlib>
+#include <string>
+
+#define SOURCES_FILE "sources.xml"
+#define XML_SOURCES "sources"
+#define XML_SOURCE "source"
+
+CMediaSourceSettings::CMediaSourceSettings()
+{
+ Clear();
+}
+
+CMediaSourceSettings::~CMediaSourceSettings() = default;
+
+CMediaSourceSettings& CMediaSourceSettings::GetInstance()
+{
+ static CMediaSourceSettings sMediaSourceSettings;
+ return sMediaSourceSettings;
+}
+
+std::string CMediaSourceSettings::GetSourcesFile()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ std::string file;
+ if (profileManager->GetCurrentProfile().hasSources())
+ file = profileManager->GetProfileUserDataFolder();
+ else
+ file = profileManager->GetUserDataFolder();
+
+ return URIUtils::AddFileToFolder(file, SOURCES_FILE);
+}
+
+void CMediaSourceSettings::OnSettingsLoaded()
+{
+ Load();
+}
+
+void CMediaSourceSettings::OnSettingsUnloaded()
+{
+ Clear();
+}
+
+bool CMediaSourceSettings::Load()
+{
+ return Load(GetSourcesFile());
+}
+
+bool CMediaSourceSettings::Load(const std::string &file)
+{
+ Clear();
+
+ if (!CFileUtils::Exists(file))
+ return false;
+
+ CLog::Log(LOGINFO, "CMediaSourceSettings: loading media sources from {}", file);
+
+ // load xml file
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(file))
+ {
+ CLog::Log(LOGERROR, "CMediaSourceSettings: error loading {}: Line {}, {}", file,
+ xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
+ return false;
+ }
+
+ TiXmlElement *pRootElement = xmlDoc.RootElement();
+ if (pRootElement == NULL || !StringUtils::EqualsNoCase(pRootElement->ValueStr(), XML_SOURCES))
+ CLog::Log(LOGERROR, "CMediaSourceSettings: sources.xml file does not contain <sources>");
+
+ // parse sources
+ std::string dummy;
+ GetSources(pRootElement, "video", m_videoSources, dummy);
+ GetSources(pRootElement, "programs", m_programSources, m_defaultProgramSource);
+ GetSources(pRootElement, "pictures", m_pictureSources, m_defaultPictureSource);
+ GetSources(pRootElement, "files", m_fileSources, m_defaultFileSource);
+ GetSources(pRootElement, "music", m_musicSources, m_defaultMusicSource);
+ GetSources(pRootElement, "games", m_gameSources, dummy);
+
+ return true;
+}
+
+bool CMediaSourceSettings::Save()
+{
+ return Save(GetSourcesFile());
+}
+
+bool CMediaSourceSettings::Save(const std::string &file) const
+{
+ //! @todo Should we be specifying utf8 here??
+ CXBMCTinyXML doc;
+ TiXmlElement xmlRootElement(XML_SOURCES);
+ TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement);
+ if (pRoot == NULL)
+ return false;
+
+ // ok, now run through and save each sources section
+ SetSources(pRoot, "programs", m_programSources, m_defaultProgramSource);
+ SetSources(pRoot, "video", m_videoSources, "");
+ SetSources(pRoot, "music", m_musicSources, m_defaultMusicSource);
+ SetSources(pRoot, "pictures", m_pictureSources, m_defaultPictureSource);
+ SetSources(pRoot, "files", m_fileSources, m_defaultFileSource);
+ SetSources(pRoot, "games", m_gameSources, "");
+
+ CWakeOnAccess::GetInstance().QueueMACDiscoveryForAllRemotes();
+
+ return doc.SaveFile(file);
+}
+
+void CMediaSourceSettings::Clear()
+{
+ m_programSources.clear();
+ m_pictureSources.clear();
+ m_fileSources.clear();
+ m_musicSources.clear();
+ m_videoSources.clear();
+ m_gameSources.clear();
+}
+
+VECSOURCES* CMediaSourceSettings::GetSources(const std::string &type)
+{
+ if (type == "programs" || type == "myprograms")
+ return &m_programSources;
+ else if (type == "files")
+ return &m_fileSources;
+ else if (type == "music")
+ return &m_musicSources;
+ else if (type == "video" || type == "videos")
+ return &m_videoSources;
+ else if (type == "pictures")
+ return &m_pictureSources;
+ else if (type == "games")
+ return &m_gameSources;
+
+ return NULL;
+}
+
+const std::string& CMediaSourceSettings::GetDefaultSource(const std::string &type) const
+{
+ if (type == "programs" || type == "myprograms")
+ return m_defaultProgramSource;
+ else if (type == "files")
+ return m_defaultFileSource;
+ else if (type == "music")
+ return m_defaultMusicSource;
+ else if (type == "pictures")
+ return m_defaultPictureSource;
+
+ return StringUtils::Empty;
+}
+
+void CMediaSourceSettings::SetDefaultSource(const std::string &type, const std::string &source)
+{
+ if (type == "programs" || type == "myprograms")
+ m_defaultProgramSource = source;
+ else if (type == "files")
+ m_defaultFileSource = source;
+ else if (type == "music")
+ m_defaultMusicSource = source;
+ else if (type == "pictures")
+ m_defaultPictureSource = source;
+}
+
+// NOTE: This function does NOT save the sources.xml file - you need to call SaveSources() separately.
+bool CMediaSourceSettings::UpdateSource(const std::string &strType, const std::string &strOldName, const std::string &strUpdateChild, const std::string &strUpdateValue)
+{
+ VECSOURCES *pShares = GetSources(strType);
+ if (pShares == NULL)
+ return false;
+
+ for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
+ {
+ if (it->strName == strOldName)
+ {
+ if (strUpdateChild == "name")
+ it->strName = strUpdateValue;
+ else if (strUpdateChild == "lockmode")
+ it->m_iLockMode = (LockType)std::strtol(strUpdateValue.c_str(), NULL, 10);
+ else if (strUpdateChild == "lockcode")
+ it->m_strLockCode = strUpdateValue;
+ else if (strUpdateChild == "badpwdcount")
+ it->m_iBadPwdCount = (int)std::strtol(strUpdateValue.c_str(), NULL, 10);
+ else if (strUpdateChild == "thumbnail")
+ it->m_strThumbnailImage = strUpdateValue;
+ else if (strUpdateChild == "path")
+ {
+ it->vecPaths.clear();
+ it->strPath = strUpdateValue;
+ it->vecPaths.push_back(strUpdateValue);
+ }
+ else
+ return false;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool CMediaSourceSettings::DeleteSource(const std::string &strType, const std::string &strName, const std::string &strPath, bool virtualSource /* = false */)
+{
+ VECSOURCES *pShares = GetSources(strType);
+ if (pShares == NULL)
+ return false;
+
+ bool found = false;
+
+ for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
+ {
+ if (it->strName == strName && it->strPath == strPath)
+ {
+ CLog::Log(LOGDEBUG, "CMediaSourceSettings: found share, removing!");
+ pShares->erase(it);
+ found = true;
+ break;
+ }
+ }
+
+ if (virtualSource)
+ return found;
+
+ return Save();
+}
+
+bool CMediaSourceSettings::AddShare(const std::string &type, const CMediaSource &share)
+{
+ VECSOURCES *pShares = GetSources(type);
+ if (pShares == NULL)
+ return false;
+
+ // translate dir and add to our current shares
+ std::string strPath1 = share.strPath;
+ if (strPath1.empty())
+ {
+ CLog::Log(LOGERROR, "CMediaSourceSettings: unable to add empty path");
+ return false;
+ }
+ StringUtils::ToUpper(strPath1);
+
+ CMediaSource shareToAdd = share;
+ if (strPath1.at(0) == '$')
+ {
+ shareToAdd.strPath = CUtil::TranslateSpecialSource(strPath1);
+ if (!share.strPath.empty())
+ CLog::Log(LOGDEBUG, "CMediaSourceSettings: translated ({}) to path ({})", strPath1,
+ shareToAdd.strPath);
+ else
+ {
+ CLog::Log(LOGDEBUG, "CMediaSourceSettings: skipping invalid special directory token ({})",
+ strPath1);
+ return false;
+ }
+ }
+ pShares->push_back(shareToAdd);
+
+ if (!share.m_ignore)
+ return Save();
+
+ return true;
+}
+
+bool CMediaSourceSettings::UpdateShare(const std::string &type, const std::string &oldName, const CMediaSource &share)
+{
+ VECSOURCES *pShares = GetSources(type);
+ if (pShares == NULL)
+ return false;
+
+ // update our current share list
+ CMediaSource* pShare = NULL;
+ for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
+ {
+ if (it->strName == oldName)
+ {
+ it->strName = share.strName;
+ it->strPath = share.strPath;
+ it->vecPaths = share.vecPaths;
+ pShare = &(*it);
+ break;
+ }
+ }
+
+ if (pShare == NULL)
+ return false;
+
+ // Update our XML file as well
+ return Save();
+}
+
+bool CMediaSourceSettings::GetSource(const std::string &category, const TiXmlNode *source, CMediaSource &share)
+{
+ const TiXmlNode *pNodeName = source->FirstChild("name");
+ std::string strName;
+ if (pNodeName && pNodeName->FirstChild())
+ strName = pNodeName->FirstChild()->ValueStr();
+
+ // get multiple paths
+ std::vector<std::string> vecPaths;
+ const TiXmlElement *pPathName = source->FirstChildElement("path");
+ while (pPathName != NULL)
+ {
+ if (pPathName->FirstChild())
+ {
+ std::string strPath = pPathName->FirstChild()->ValueStr();
+
+ // make sure there are no virtualpaths or stack paths defined in sources.xml
+ if (!URIUtils::IsStack(strPath))
+ {
+ // translate special tags
+ if (!strPath.empty() && strPath.at(0) == '$')
+ strPath = CUtil::TranslateSpecialSource(strPath);
+
+ // need to check path validity again as CUtil::TranslateSpecialSource() may have failed
+ if (!strPath.empty())
+ {
+ URIUtils::AddSlashAtEnd(strPath);
+ vecPaths.push_back(strPath);
+ }
+ }
+ else
+ CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) in source", strPath);
+ }
+
+ pPathName = pPathName->NextSiblingElement("path");
+ }
+
+ const TiXmlNode *pLockMode = source->FirstChild("lockmode");
+ const TiXmlNode *pLockCode = source->FirstChild("lockcode");
+ const TiXmlNode *pBadPwdCount = source->FirstChild("badpwdcount");
+ const TiXmlNode *pThumbnailNode = source->FirstChild("thumbnail");
+
+ if (strName.empty() || vecPaths.empty())
+ return false;
+
+ std::vector<std::string> verifiedPaths;
+ // disallowed for files, or there's only a single path in the vector
+ if (StringUtils::EqualsNoCase(category, "files") || vecPaths.size() == 1)
+ verifiedPaths.push_back(vecPaths[0]);
+ // multiple paths?
+ else
+ {
+ // validate the paths
+ for (std::vector<std::string>::const_iterator path = vecPaths.begin(); path != vecPaths.end(); ++path)
+ {
+ CURL url(*path);
+ bool bIsInvalid = false;
+
+ // for my programs
+ if (StringUtils::EqualsNoCase(category, "programs") || StringUtils::EqualsNoCase(category, "myprograms"))
+ {
+ // only allow HD and plugins
+ if (url.IsLocal() || url.IsProtocol("plugin"))
+ verifiedPaths.push_back(*path);
+ else
+ bIsInvalid = true;
+ }
+ // for others allow everything (if the user does something silly, we can't stop them)
+ else
+ verifiedPaths.push_back(*path);
+
+ // error message
+ if (bIsInvalid)
+ CLog::Log(LOGERROR, "CMediaSourceSettings: invalid path type ({}) for multipath source",
+ path->c_str());
+ }
+
+ // no valid paths? skip to next source
+ if (verifiedPaths.empty())
+ {
+ CLog::Log(LOGERROR,"CMediaSourceSettings: missing or invalid <name> and/or <path> in source");
+ return false;
+ }
+ }
+
+ share.FromNameAndPaths(category, strName, verifiedPaths);
+
+ share.m_iBadPwdCount = 0;
+ if (pLockMode)
+ {
+ share.m_iLockMode = (LockType)std::strtol(pLockMode->FirstChild()->Value(), NULL, 10);
+ share.m_iHasLock = LOCK_STATE_LOCKED;
+ }
+
+ if (pLockCode && pLockCode->FirstChild())
+ share.m_strLockCode = pLockCode->FirstChild()->Value();
+
+ if (pBadPwdCount && pBadPwdCount->FirstChild())
+ share.m_iBadPwdCount = (int)std::strtol(pBadPwdCount->FirstChild()->Value(), NULL, 10);
+
+ if (pThumbnailNode && pThumbnailNode->FirstChild())
+ share.m_strThumbnailImage = pThumbnailNode->FirstChild()->Value();
+
+ XMLUtils::GetBoolean(source, "allowsharing", share.m_allowSharing);
+
+ return true;
+}
+
+void CMediaSourceSettings::GetSources(const TiXmlNode* pRootElement, const std::string& strTagName, VECSOURCES& items, std::string& strDefault)
+{
+ strDefault = "";
+ items.clear();
+
+ const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str());
+ if (pChild == NULL)
+ {
+ CLog::Log(LOGDEBUG, "CMediaSourceSettings: <{}> tag is missing or sources.xml is malformed",
+ strTagName);
+ return;
+ }
+
+ pChild = pChild->FirstChild();
+ while (pChild != NULL)
+ {
+ std::string strValue = pChild->ValueStr();
+ if (strValue == XML_SOURCE || strValue == "bookmark") // "bookmark" left in for backwards compatibility
+ {
+ CMediaSource share;
+ if (GetSource(strTagName, pChild, share))
+ items.push_back(share);
+ else
+ CLog::Log(LOGERROR, "CMediaSourceSettings: Missing or invalid <name> and/or <path> in source");
+ }
+ else if (strValue == "default")
+ {
+ const TiXmlNode *pValueNode = pChild->FirstChild();
+ if (pValueNode)
+ {
+ std::string pszText = pChild->FirstChild()->ValueStr();
+ if (!pszText.empty())
+ strDefault = pszText;
+ CLog::Log(LOGDEBUG, "CMediaSourceSettings: Setting <default> source to : {}",
+ strDefault);
+ }
+ }
+
+ pChild = pChild->NextSibling();
+ }
+}
+
+bool CMediaSourceSettings::SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const std::string &defaultPath) const
+{
+ TiXmlElement sectionElement(section);
+ TiXmlNode *sectionNode = root->InsertEndChild(sectionElement);
+ if (sectionNode == NULL)
+ return false;
+
+ XMLUtils::SetPath(sectionNode, "default", defaultPath);
+ for (CIVECSOURCES it = shares.begin(); it != shares.end(); it++)
+ {
+ const CMediaSource &share = *it;
+ if (share.m_ignore)
+ continue;
+
+ TiXmlElement source(XML_SOURCE);
+ XMLUtils::SetString(&source, "name", share.strName);
+
+ for (unsigned int i = 0; i < share.vecPaths.size(); i++)
+ XMLUtils::SetPath(&source, "path", share.vecPaths[i]);
+
+ if (share.m_iHasLock)
+ {
+ XMLUtils::SetInt(&source, "lockmode", share.m_iLockMode);
+ XMLUtils::SetString(&source, "lockcode", share.m_strLockCode);
+ XMLUtils::SetInt(&source, "badpwdcount", share.m_iBadPwdCount);
+ }
+
+ if (!share.m_strThumbnailImage.empty())
+ XMLUtils::SetPath(&source, "thumbnail", share.m_strThumbnailImage);
+
+ XMLUtils::SetBoolean(&source, "allowsharing", share.m_allowSharing);
+
+ sectionNode->InsertEndChild(source);
+ }
+
+ return true;
+}
diff --git a/xbmc/settings/MediaSourceSettings.h b/xbmc/settings/MediaSourceSettings.h
new file mode 100644
index 0000000..7fe456e
--- /dev/null
+++ b/xbmc/settings/MediaSourceSettings.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "MediaSource.h"
+#include "settings/lib/ISettingsHandler.h"
+
+#include <string>
+
+class CProfileManager;
+class TiXmlNode;
+
+class CMediaSourceSettings : public ISettingsHandler
+{
+public:
+ static CMediaSourceSettings& GetInstance();
+
+ static std::string GetSourcesFile();
+
+ void OnSettingsLoaded() override;
+ void OnSettingsUnloaded() override;
+
+ bool Load();
+ bool Load(const std::string &file);
+ bool Save();
+ bool Save(const std::string &file) const;
+ void Clear();
+
+ VECSOURCES* GetSources(const std::string &type);
+ const std::string& GetDefaultSource(const std::string &type) const;
+ void SetDefaultSource(const std::string &type, const std::string &source);
+
+ bool UpdateSource(const std::string &strType, const std::string &strOldName, const std::string &strUpdateChild, const std::string &strUpdateValue);
+ bool DeleteSource(const std::string &strType, const std::string &strName, const std::string &strPath, bool virtualSource = false);
+ bool AddShare(const std::string &type, const CMediaSource &share);
+ bool UpdateShare(const std::string &type, const std::string &oldName, const CMediaSource &share);
+
+protected:
+ CMediaSourceSettings();
+ CMediaSourceSettings(const CMediaSourceSettings&) = delete;
+ CMediaSourceSettings& operator=(CMediaSourceSettings const&) = delete;
+ ~CMediaSourceSettings() override;
+
+private:
+ bool GetSource(const std::string &category, const TiXmlNode *source, CMediaSource &share);
+ void GetSources(const TiXmlNode* pRootElement, const std::string& strTagName, VECSOURCES& items, std::string& strDefault);
+ bool SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const std::string &defaultPath) const;
+
+ VECSOURCES m_programSources;
+ VECSOURCES m_pictureSources;
+ VECSOURCES m_fileSources;
+ VECSOURCES m_musicSources;
+ VECSOURCES m_videoSources;
+ VECSOURCES m_gameSources;
+
+ std::string m_defaultProgramSource;
+ std::string m_defaultMusicSource;
+ std::string m_defaultPictureSource;
+ std::string m_defaultFileSource;
+};
diff --git a/xbmc/settings/SettingAddon.cpp b/xbmc/settings/SettingAddon.cpp
new file mode 100644
index 0000000..fd39fe9
--- /dev/null
+++ b/xbmc/settings/SettingAddon.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013-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 "SettingAddon.h"
+
+#include "addons/Addon.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/addoninfo/AddonType.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+CSettingAddon::CSettingAddon(const std::string &id, CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingString(id, settingsManager)
+{ }
+
+CSettingAddon::CSettingAddon(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingString(id, label, value, settingsManager)
+{ }
+
+CSettingAddon::CSettingAddon(const std::string &id, const CSettingAddon &setting)
+ : CSettingString(id, setting)
+{
+ copyaddontype(setting);
+}
+
+SettingPtr CSettingAddon::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingAddon>(id, *this);
+}
+
+bool CSettingAddon::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSettingString::Deserialize(node, update))
+ return false;
+
+ if (m_control != nullptr &&
+ (m_control->GetType() != "button" || m_control->GetFormat() != "addon"))
+ {
+ CLog::Log(LOGERROR, "CSettingAddon: invalid <control> of \"{}\"", m_id);
+ return false;
+ }
+
+ bool ok = false;
+ std::string strAddonType;
+ auto constraints = node->FirstChild("constraints");
+ if (constraints != nullptr)
+ {
+ // get the addon type
+ if (XMLUtils::GetString(constraints, "addontype", strAddonType) && !strAddonType.empty())
+ {
+ m_addonType = ADDON::CAddonInfo::TranslateType(strAddonType);
+ if (m_addonType != ADDON::AddonType::UNKNOWN)
+ ok = true;
+ }
+ }
+
+ if (!ok && !update)
+ {
+ CLog::Log(LOGERROR, "CSettingAddon: error reading the addontype value \"{}\" of \"{}\"",
+ strAddonType, m_id);
+ return false;
+ }
+
+ return true;
+}
+
+void CSettingAddon::copyaddontype(const CSettingAddon &setting)
+{
+ CSettingString::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_addonType = setting.m_addonType;
+}
diff --git a/xbmc/settings/SettingAddon.h b/xbmc/settings/SettingAddon.h
new file mode 100644
index 0000000..d4aefc8
--- /dev/null
+++ b/xbmc/settings/SettingAddon.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "addons/IAddon.h"
+#include "settings/lib/Setting.h"
+
+namespace ADDON
+{
+enum class AddonType;
+}
+
+class CSettingAddon : public CSettingString
+{
+public:
+ CSettingAddon(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingAddon(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = nullptr);
+ CSettingAddon(const std::string &id, const CSettingAddon &setting);
+ ~CSettingAddon() override = default;
+
+ SettingPtr Clone(const std::string &id) const override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ ADDON::AddonType GetAddonType() const { return m_addonType; }
+ void SetAddonType(ADDON::AddonType addonType) { m_addonType = addonType; }
+
+private:
+ void copyaddontype(const CSettingAddon &setting);
+
+ ADDON::AddonType m_addonType{};
+};
diff --git a/xbmc/settings/SettingConditions.cpp b/xbmc/settings/SettingConditions.cpp
new file mode 100644
index 0000000..bb85e97
--- /dev/null
+++ b/xbmc/settings/SettingConditions.cpp
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2013-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 "SettingConditions.h"
+
+#include "LockType.h"
+#include "addons/AddonManager.h"
+#include "addons/Skin.h"
+#include "addons/addoninfo/AddonType.h"
+#include "application/AppParams.h"
+#include "cores/AudioEngine/Engines/ActiveAE/ActiveAESettings.h"
+#include "ServiceBroker.h"
+#include "GUIPassword.h"
+#if defined(HAS_WEB_SERVER)
+#include "network/WebServer.h"
+#endif
+#include "peripherals/Peripherals.h"
+#include "profiles/ProfileManager.h"
+#include "settings/SettingAddon.h"
+#include "settings/SettingsComponent.h"
+#include "utils/FontUtils.h"
+#include "utils/StringUtils.h"
+#include "windowing/WinSystem.h"
+
+namespace
+{
+bool AddonHasSettings(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ if (setting == NULL)
+ return false;
+
+ std::shared_ptr<const CSettingAddon> settingAddon = std::dynamic_pointer_cast<const CSettingAddon>(setting);
+ if (settingAddon == NULL)
+ return false;
+
+ ADDON::AddonPtr addon;
+ if (!CServiceBroker::GetAddonMgr().GetAddon(settingAddon->GetValue(), addon,
+ settingAddon->GetAddonType(),
+ ADDON::OnlyEnabled::CHOICE_YES) ||
+ addon == NULL)
+ return false;
+
+ if (addon->Type() == ADDON::AddonType::SKIN)
+ return ((ADDON::CSkinInfo*)addon.get())->HasSkinFile("SkinSettings.xml");
+
+ return addon->CanHaveAddonOrInstanceSettings();
+}
+
+bool CheckMasterLock(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return g_passwordManager.IsMasterLockUnlocked(StringUtils::EqualsNoCase(value, "true"));
+}
+
+bool HasPeripherals(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetPeripherals().GetNumberOfPeripherals() > 0;
+}
+
+bool HasPeripheralLibraries(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetAddonMgr().HasInstalledAddons(ADDON::AddonType::PERIPHERALDLL);
+}
+
+bool HasRumbleFeature(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetPeripherals().SupportsFeature(PERIPHERALS::FEATURE_RUMBLE);
+}
+
+bool HasRumbleController(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetPeripherals().HasPeripheralWithFeature(PERIPHERALS::FEATURE_RUMBLE);
+}
+
+bool HasPowerOffFeature(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetPeripherals().SupportsFeature(PERIPHERALS::FEATURE_POWER_OFF);
+}
+
+bool IsHDRDisplay(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CServiceBroker::GetWinSystem()->IsHDRDisplay();
+}
+
+bool IsMasterUser(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return g_passwordManager.bMasterUser;
+}
+
+bool HasSubtitlesFontExtensions(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ auto settingStr = std::dynamic_pointer_cast<const CSettingString>(setting);
+ if (!settingStr)
+ return false;
+
+ return UTILS::FONT::IsSupportedFontExtension(settingStr->GetValue());
+}
+
+bool ProfileCanWriteDatabase(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().canWriteDatabases();
+}
+
+bool ProfileCanWriteSources(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().canWriteSources();
+}
+
+bool ProfileHasAddons(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().hasAddons();
+}
+
+bool ProfileHasDatabase(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().hasDatabases();
+}
+
+bool ProfileHasSources(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().hasSources();
+}
+
+bool ProfileHasAddonManagerLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().addonmanagerLocked();
+}
+
+bool ProfileHasFilesLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().filesLocked();
+}
+
+bool ProfileHasMusicLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().musicLocked();
+}
+
+bool ProfileHasPicturesLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().picturesLocked();
+}
+
+bool ProfileHasProgramsLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().programsLocked();
+}
+
+bool ProfileHasSettingsLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ LOCK_LEVEL::SETTINGS_LOCK slValue=LOCK_LEVEL::ALL;
+ if (StringUtils::EqualsNoCase(value, "none"))
+ slValue = LOCK_LEVEL::NONE;
+ else if (StringUtils::EqualsNoCase(value, "standard"))
+ slValue = LOCK_LEVEL::STANDARD;
+ else if (StringUtils::EqualsNoCase(value, "advanced"))
+ slValue = LOCK_LEVEL::ADVANCED;
+ else if (StringUtils::EqualsNoCase(value, "expert"))
+ slValue = LOCK_LEVEL::EXPERT;
+ return slValue <= CSettingConditions::GetCurrentProfile().settingsLockLevel();
+}
+
+bool ProfileHasVideosLocked(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ return CSettingConditions::GetCurrentProfile().videoLocked();
+}
+
+bool ProfileLockMode(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ char* tmp = nullptr;
+ LockType lock = (LockType)strtol(value.c_str(), &tmp, 0);
+ if (tmp != NULL && *tmp != '\0')
+ return false;
+
+ return CSettingConditions::GetCurrentProfile().getLockMode() == lock;
+}
+
+bool GreaterThan(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ if (setting == NULL)
+ return false;
+
+ std::shared_ptr<const CSettingInt> settingInt = std::dynamic_pointer_cast<const CSettingInt>(setting);
+ if (settingInt == NULL)
+ return false;
+
+ char* tmp = nullptr;
+
+ int lhs = settingInt->GetValue();
+ int rhs = StringUtils::IsInteger(value) ? (int)strtol(value.c_str(), &tmp, 0) : 0;
+
+ return lhs > rhs;
+}
+
+bool GreaterThanOrEqual(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ if (setting == NULL)
+ return false;
+
+ std::shared_ptr<const CSettingInt> settingInt = std::dynamic_pointer_cast<const CSettingInt>(setting);
+ if (settingInt == NULL)
+ return false;
+
+ char* tmp = nullptr;
+
+ int lhs = settingInt->GetValue();
+ int rhs = StringUtils::IsInteger(value) ? (int)strtol(value.c_str(), &tmp, 0) : 0;
+
+ return lhs >= rhs;
+}
+
+bool LessThan(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ if (setting == NULL)
+ return false;
+
+ std::shared_ptr<const CSettingInt> settingInt = std::dynamic_pointer_cast<const CSettingInt>(setting);
+ if (settingInt == NULL)
+ return false;
+
+ char* tmp = nullptr;
+
+ int lhs = settingInt->GetValue();
+ int rhs = StringUtils::IsInteger(value) ? (int)strtol(value.c_str(), &tmp, 0) : 0;
+
+ return lhs < rhs;
+}
+
+bool LessThanOrEqual(const std::string& condition,
+ const std::string& value,
+ const SettingConstPtr& setting,
+ void* data)
+{
+ if (setting == NULL)
+ return false;
+
+ std::shared_ptr<const CSettingInt> settingInt = std::dynamic_pointer_cast<const CSettingInt>(setting);
+ if (settingInt == NULL)
+ return false;
+
+ char* tmp = nullptr;
+
+ int lhs = settingInt->GetValue();
+ int rhs = StringUtils::IsInteger(value) ? (int)strtol(value.c_str(), &tmp, 0) : 0;
+
+ return lhs <= rhs;
+}
+}; // anonymous namespace
+
+const CProfileManager* CSettingConditions::m_profileManager = nullptr;
+std::set<std::string> CSettingConditions::m_simpleConditions;
+std::map<std::string, SettingConditionCheck> CSettingConditions::m_complexConditions;
+
+void CSettingConditions::Initialize()
+{
+ if (!m_simpleConditions.empty())
+ return;
+
+ // add simple conditions
+ m_simpleConditions.emplace("true");
+#ifdef HAS_UPNP
+ m_simpleConditions.emplace("has_upnp");
+#endif
+#ifdef HAS_AIRPLAY
+ m_simpleConditions.emplace("has_airplay");
+#endif
+#ifdef HAVE_X11
+ m_simpleConditions.emplace("have_x11");
+#endif
+#ifdef HAVE_WAYLAND
+ m_simpleConditions.emplace("have_wayland");
+#endif
+#ifdef HAS_GL
+ m_simpleConditions.emplace("has_gl");
+#endif
+#ifdef HAS_GLES
+ m_simpleConditions.emplace("has_gles");
+#endif
+#if HAS_GLES >= 2
+ m_simpleConditions.emplace("has_glesv2");
+#endif
+#ifdef HAS_TIME_SERVER
+ m_simpleConditions.emplace("has_time_server");
+#endif
+#ifdef HAS_WEB_SERVER
+ m_simpleConditions.emplace("has_web_server");
+#endif
+#ifdef HAS_FILESYSTEM_SMB
+ m_simpleConditions.emplace("has_filesystem_smb");
+#endif
+#ifdef HAS_FILESYSTEM_NFS
+ m_simpleConditions.insert("has_filesystem_nfs");
+#endif
+#ifdef HAS_ZEROCONF
+ m_simpleConditions.emplace("has_zeroconf");
+#endif
+#ifdef HAVE_LIBVA
+ m_simpleConditions.emplace("have_libva");
+#endif
+#ifdef HAVE_LIBVDPAU
+ m_simpleConditions.emplace("have_libvdpau");
+#endif
+#ifdef TARGET_ANDROID
+ m_simpleConditions.emplace("has_mediacodec");
+#endif
+#ifdef TARGET_DARWIN
+ m_simpleConditions.emplace("HasVTB");
+#endif
+#ifdef TARGET_DARWIN_OSX
+ m_simpleConditions.emplace("have_osx");
+#endif
+#ifdef TARGET_DARWIN_IOS
+ m_simpleConditions.emplace("have_ios");
+#endif
+#ifdef TARGET_DARWIN_TVOS
+ m_simpleConditions.emplace("have_tvos");
+#endif
+#if defined(TARGET_WINDOWS)
+ m_simpleConditions.emplace("has_dx");
+ m_simpleConditions.emplace("hasdxva2");
+#endif
+#ifdef HAVE_LCMS2
+ m_simpleConditions.emplace("have_lcms2");
+#endif
+
+#ifdef TARGET_ANDROID
+ m_simpleConditions.emplace("isstandalone");
+#else
+ if (CServiceBroker::GetAppParams()->IsStandAlone())
+ m_simpleConditions.emplace("isstandalone");
+#endif
+
+ m_simpleConditions.emplace("has_ae_quality_levels");
+
+#ifdef HAS_WEB_SERVER
+ if (CWebServer::WebServerSupportsSSL())
+ m_simpleConditions.emplace("webserver_has_ssl");
+#endif
+
+#ifdef HAVE_LIBBLURAY
+ m_simpleConditions.emplace("have_libbluray");
+#endif
+
+#ifdef HAS_CDDA_RIPPER
+ m_simpleConditions.emplace("has_cdda_ripper");
+#endif
+
+#ifdef HAS_DVD_DRIVE
+ m_simpleConditions.emplace("has_dvd_drive");
+#endif
+
+ // add complex conditions
+ m_complexConditions.emplace("addonhassettings", AddonHasSettings);
+ m_complexConditions.emplace("checkmasterlock", CheckMasterLock);
+ m_complexConditions.emplace("hasperipherals", HasPeripherals);
+ m_complexConditions.emplace("hasperipherallibraries", HasPeripheralLibraries);
+ m_complexConditions.emplace("hasrumblefeature", HasRumbleFeature);
+ m_complexConditions.emplace("hasrumblecontroller", HasRumbleController);
+ m_complexConditions.emplace("haspowerofffeature", HasPowerOffFeature);
+ m_complexConditions.emplace("ishdrdisplay", IsHDRDisplay);
+ m_complexConditions.emplace("ismasteruser", IsMasterUser);
+ m_complexConditions.emplace("hassubtitlesfontextensions", HasSubtitlesFontExtensions);
+ m_complexConditions.emplace("profilecanwritedatabase", ProfileCanWriteDatabase);
+ m_complexConditions.emplace("profilecanwritesources", ProfileCanWriteSources);
+ m_complexConditions.emplace("profilehasaddons", ProfileHasAddons);
+ m_complexConditions.emplace("profilehasdatabase", ProfileHasDatabase);
+ m_complexConditions.emplace("profilehassources", ProfileHasSources);
+ m_complexConditions.emplace("profilehasaddonmanagerlocked", ProfileHasAddonManagerLocked);
+ m_complexConditions.emplace("profilehasfileslocked", ProfileHasFilesLocked);
+ m_complexConditions.emplace("profilehasmusiclocked", ProfileHasMusicLocked);
+ m_complexConditions.emplace("profilehaspictureslocked", ProfileHasPicturesLocked);
+ m_complexConditions.emplace("profilehasprogramslocked", ProfileHasProgramsLocked);
+ m_complexConditions.emplace("profilehassettingslocked", ProfileHasSettingsLocked);
+ m_complexConditions.emplace("profilehasvideoslocked", ProfileHasVideosLocked);
+ m_complexConditions.emplace("profilelockmode", ProfileLockMode);
+ m_complexConditions.emplace("aesettingvisible", ActiveAE::CActiveAESettings::IsSettingVisible);
+ m_complexConditions.emplace("gt", GreaterThan);
+ m_complexConditions.emplace("gte", GreaterThanOrEqual);
+ m_complexConditions.emplace("lt", LessThan);
+ m_complexConditions.emplace("lte", LessThanOrEqual);
+}
+
+void CSettingConditions::Deinitialize()
+{
+ m_profileManager = nullptr;
+}
+
+const CProfile& CSettingConditions::GetCurrentProfile()
+{
+ if (!m_profileManager)
+ m_profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager().get();
+
+ if (m_profileManager)
+ return m_profileManager->GetCurrentProfile();
+
+ static CProfile emptyProfile;
+ return emptyProfile;
+}
+
+bool CSettingConditions::Check(const std::string& condition,
+ const std::string& value /* = "" */,
+ const SettingConstPtr& setting /* = NULL */)
+{
+ if (m_simpleConditions.find(condition) != m_simpleConditions.end())
+ return true;
+
+ std::map<std::string, SettingConditionCheck>::const_iterator itCondition = m_complexConditions.find(condition);
+ if (itCondition != m_complexConditions.end())
+ return itCondition->second(condition, value, setting, NULL);
+
+ return Check(condition);
+}
diff --git a/xbmc/settings/SettingConditions.h b/xbmc/settings/SettingConditions.h
new file mode 100644
index 0000000..f51d9e0
--- /dev/null
+++ b/xbmc/settings/SettingConditions.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#pragma once
+
+#include "settings/lib/SettingConditions.h"
+
+#include <set>
+#include <string>
+
+class CProfile;
+class CProfileManager;
+
+class CSettingConditions
+{
+public:
+ static void Initialize();
+ static void Deinitialize();
+
+ static const CProfile& GetCurrentProfile();
+
+ static const std::set<std::string>& GetSimpleConditions() { return m_simpleConditions; }
+ static const std::map<std::string, SettingConditionCheck>& GetComplexConditions() { return m_complexConditions; }
+
+ static bool Check(const std::string& condition,
+ const std::string& value = "",
+ const std::shared_ptr<const CSetting>& setting = NULL);
+
+private:
+ // Initialization parameters
+ static const CProfileManager *m_profileManager;
+
+ static std::set<std::string> m_simpleConditions;
+ static std::map<std::string, SettingConditionCheck> m_complexConditions;
+};
diff --git a/xbmc/settings/SettingControl.cpp b/xbmc/settings/SettingControl.cpp
new file mode 100644
index 0000000..3c73b89
--- /dev/null
+++ b/xbmc/settings/SettingControl.cpp
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2013-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 "SettingControl.h"
+
+#include "settings/lib/SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <vector>
+
+const char* SHOW_ADDONS_ALL = "all";
+const char* SHOW_ADDONS_INSTALLED = "installed";
+const char* SHOW_ADDONS_INSTALLABLE = "installable";
+
+std::shared_ptr<ISettingControl> CSettingControlCreator::CreateControl(const std::string &controlType) const
+{
+ if (StringUtils::EqualsNoCase(controlType, "toggle"))
+ return std::make_shared<CSettingControlCheckmark>();
+ else if (StringUtils::EqualsNoCase(controlType, "spinner"))
+ return std::make_shared<CSettingControlSpinner>();
+ else if (StringUtils::EqualsNoCase(controlType, "edit"))
+ return std::make_shared<CSettingControlEdit>();
+ else if (StringUtils::EqualsNoCase(controlType, "button"))
+ return std::make_shared<CSettingControlButton>();
+ else if (StringUtils::EqualsNoCase(controlType, "list"))
+ return std::make_shared<CSettingControlList>();
+ else if (StringUtils::EqualsNoCase(controlType, "slider"))
+ return std::make_shared<CSettingControlSlider>();
+ else if (StringUtils::EqualsNoCase(controlType, "range"))
+ return std::make_shared<CSettingControlRange>();
+ else if (StringUtils::EqualsNoCase(controlType, "title"))
+ return std::make_shared<CSettingControlTitle>();
+ else if (StringUtils::EqualsNoCase(controlType, "label"))
+ return std::make_shared<CSettingControlLabel>();
+ else if (StringUtils::EqualsNoCase(controlType, "colorbutton"))
+ return std::make_shared<CSettingControlColorButton>();
+
+ return nullptr;
+}
+
+bool CSettingControlCheckmark::SetFormat(const std::string &format)
+{
+ return format.empty() || StringUtils::EqualsNoCase(format, "boolean");
+}
+
+bool CSettingControlFormattedRange::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ if (m_format == "string")
+ {
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_FORMATLABEL, m_formatLabel);
+
+ // get the minimum label from <setting><constraints><minimum label="X" />
+ auto settingNode = node->Parent();
+ if (settingNode != nullptr)
+ {
+ auto constraintsNode = settingNode->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraintsNode != nullptr)
+ {
+ auto minimumNode = constraintsNode->FirstChild(SETTING_XML_ELM_MINIMUM);
+ if (minimumNode != nullptr)
+ {
+ auto minimumElem = minimumNode->ToElement();
+ if (minimumElem != nullptr)
+ {
+ if (minimumElem->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &m_minimumLabel) != TIXML_SUCCESS)
+ m_minimumLabel = -1;
+ }
+ }
+ }
+ }
+
+ if (m_minimumLabel < 0)
+ {
+ std::string strFormat;
+ if (XMLUtils::GetString(node, SETTING_XML_ATTR_FORMAT, strFormat) && !strFormat.empty())
+ m_formatString = strFormat;
+ }
+ }
+
+ return true;
+}
+
+bool CSettingControlSpinner::SetFormat(const std::string &format)
+{
+ if (!StringUtils::EqualsNoCase(format, "string") &&
+ !StringUtils::EqualsNoCase(format, "integer") &&
+ !StringUtils::EqualsNoCase(format, "number"))
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+
+ return true;
+}
+
+bool CSettingControlEdit::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_HIDDEN, m_hidden);
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_VERIFYNEW, m_verifyNewValue);
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_HEADING, m_heading);
+
+ return true;
+}
+
+bool CSettingControlEdit::SetFormat(const std::string &format)
+{
+ if (!StringUtils::EqualsNoCase(format, "string") &&
+ !StringUtils::EqualsNoCase(format, "integer") &&
+ !StringUtils::EqualsNoCase(format, "number") &&
+ !StringUtils::EqualsNoCase(format, "ip") &&
+ !StringUtils::EqualsNoCase(format, "md5") &&
+ !StringUtils::EqualsNoCase(format, "urlencoded"))
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+
+ return true;
+}
+
+bool CSettingControlButton::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_HEADING, m_heading);
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_HIDEVALUE, m_hideValue);
+
+ if (m_format == "action")
+ {
+ bool closeDialog = false;
+ if (XMLUtils::GetBoolean(node, "close", closeDialog))
+ m_closeDialog = closeDialog;
+ std::string strActionData;
+ if (XMLUtils::GetString(node, SETTING_XML_ELM_DATA, strActionData))
+ m_actionData = strActionData;
+ }
+ else if (m_format == "addon")
+ {
+ std::string strShowAddons;
+ if (XMLUtils::GetString(node, "show", strShowAddons) && !strShowAddons.empty())
+ {
+ if (StringUtils::EqualsNoCase(strShowAddons, SHOW_ADDONS_ALL))
+ {
+ m_showInstalledAddons = true;
+ m_showInstallableAddons = true;
+ }
+ else if (StringUtils::EqualsNoCase(strShowAddons, SHOW_ADDONS_INSTALLED))
+ {
+ m_showInstalledAddons = true;
+ m_showInstallableAddons = false;
+ }
+ else if (StringUtils::EqualsNoCase(strShowAddons, SHOW_ADDONS_INSTALLABLE))
+ {
+ m_showInstalledAddons = false;
+ m_showInstallableAddons = true;
+ }
+ else
+ CLog::Log(LOGWARNING, "CSettingControlButton: invalid <show>");
+
+ auto show = node->FirstChildElement("show");
+ if (show != nullptr)
+ {
+ const char *strShowDetails = nullptr;
+ if ((strShowDetails = show->Attribute(SETTING_XML_ATTR_SHOW_DETAILS)) != nullptr)
+ {
+ if (StringUtils::EqualsNoCase(strShowDetails, "false") || StringUtils::EqualsNoCase(strShowDetails, "true"))
+ m_showAddonDetails = StringUtils::EqualsNoCase(strShowDetails, "true");
+ else
+ CLog::Log(LOGWARNING, "CSettingControlButton: error reading \"details\" attribute of <show>");
+ }
+
+ if (!m_showInstallableAddons)
+ {
+ const char *strShowMore = nullptr;
+ if ((strShowMore = show->Attribute(SETTING_XML_ATTR_SHOW_MORE)) != nullptr)
+ {
+ if (StringUtils::EqualsNoCase(strShowMore, "false") || StringUtils::EqualsNoCase(strShowMore, "true"))
+ m_showMoreAddons = StringUtils::EqualsNoCase(strShowMore, "true");
+ else
+ CLog::Log(LOGWARNING, "CSettingControlButton: error reading \"more\" attribute of <show>");
+ }
+ }
+ }
+ }
+ }
+ else if (m_format == "file")
+ {
+ bool useThumbs = false;
+ if (XMLUtils::GetBoolean(node, "usethumbs", useThumbs))
+ m_useImageThumbs = useThumbs;
+ bool useFileDirectories = false;
+ if (XMLUtils::GetBoolean(node, "treatasfolder", useFileDirectories))
+ m_useFileDirectories = useFileDirectories;
+ }
+
+ return true;
+}
+
+bool CSettingControlButton::SetFormat(const std::string &format)
+{
+ if (!StringUtils::EqualsNoCase(format, "path") &&
+ !StringUtils::EqualsNoCase(format, "file") &&
+ !StringUtils::EqualsNoCase(format, "image") &&
+ !StringUtils::EqualsNoCase(format, "addon") &&
+ !StringUtils::EqualsNoCase(format, "action") &&
+ !StringUtils::EqualsNoCase(format, "infolabel") &&
+ !StringUtils::EqualsNoCase(format, "date") &&
+ !StringUtils::EqualsNoCase(format, "time"))
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+
+ return true;
+}
+
+bool CSettingControlList::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!CSettingControlFormattedRange::Deserialize(node, update))
+ return false;
+
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_HEADING, m_heading);
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_MULTISELECT, m_multiselect);
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_HIDEVALUE, m_hideValue);
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_ADDBUTTONLABEL, m_addButtonLabel);
+
+ return true;
+}
+
+bool CSettingControlList::SetFormat(const std::string &format)
+{
+ if (!StringUtils::EqualsNoCase(format, "string") &&
+ !StringUtils::EqualsNoCase(format, "integer"))
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+
+ return true;
+}
+
+bool CSettingControlSlider::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_HEADING, m_heading);
+ XMLUtils::GetBoolean(node, SETTING_XML_ELM_CONTROL_POPUP, m_popup);
+
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_FORMATLABEL, m_formatLabel);
+ if (m_formatLabel < 0)
+ {
+ std::string strFormat;
+ if (XMLUtils::GetString(node, SETTING_XML_ATTR_FORMAT, strFormat) && !strFormat.empty())
+ m_formatString = strFormat;
+ }
+
+ return true;
+}
+
+bool CSettingControlSlider::SetFormat(const std::string &format)
+{
+ if (!StringUtils::EqualsNoCase(format, "percentage") &&
+ !StringUtils::EqualsNoCase(format, "integer") &&
+ !StringUtils::EqualsNoCase(format, "number"))
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+ m_formatString = GetDefaultFormatString();
+
+ return true;
+}
+
+std::string CSettingControlSlider::GetDefaultFormatString() const
+{
+ if (m_format == "percentage")
+ return "{} %";
+ if (m_format == "integer")
+ return "{:d}";
+ if (m_format == "number")
+ return "{:.1f}";
+
+ return "{}";
+}
+
+bool CSettingControlRange::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ auto formatLabel = node->FirstChildElement(SETTING_XML_ELM_CONTROL_FORMATLABEL);
+ if (formatLabel != nullptr)
+ {
+ XMLUtils::GetInt(node, SETTING_XML_ELM_CONTROL_FORMATLABEL, m_formatLabel);
+ if (m_formatLabel < 0)
+ return false;
+
+ auto formatValue = formatLabel->Attribute(SETTING_XML_ELM_CONTROL_FORMATVALUE);
+ if (formatValue != nullptr)
+ {
+ if (StringUtils::IsInteger(formatValue))
+ m_valueFormatLabel = (int)strtol(formatValue, nullptr, 0);
+ else
+ {
+ m_valueFormat = formatValue;
+ if (!m_valueFormat.empty())
+ m_valueFormatLabel = -1;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool CSettingControlRange::SetFormat(const std::string &format)
+{
+ if (StringUtils::EqualsNoCase(format, "percentage"))
+ m_valueFormat = "{} %";
+ else if (StringUtils::EqualsNoCase(format, "integer"))
+ m_valueFormat = "{:d}";
+ else if (StringUtils::EqualsNoCase(format, "number"))
+ m_valueFormat = "{:.1f}";
+ else if (StringUtils::EqualsNoCase(format, "date") ||
+ StringUtils::EqualsNoCase(format, "time"))
+ m_valueFormat.clear();
+ else
+ return false;
+
+ m_format = format;
+ StringUtils::ToLower(m_format);
+
+ return true;
+}
+
+bool CSettingControlTitle::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (!ISettingControl::Deserialize(node, update))
+ return false;
+
+ std::string strTmp;
+ if (XMLUtils::GetString(node, SETTING_XML_ATTR_SEPARATOR_POSITION, strTmp))
+ {
+ if (!StringUtils::EqualsNoCase(strTmp, "top") && !StringUtils::EqualsNoCase(strTmp, "bottom"))
+ CLog::Log(LOGWARNING, "CSettingControlTitle: error reading \"value\" attribute of <{}>",
+ SETTING_XML_ATTR_SEPARATOR_POSITION);
+ else
+ m_separatorBelowLabel = StringUtils::EqualsNoCase(strTmp, "bottom");
+ }
+ XMLUtils::GetBoolean(node, SETTING_XML_ATTR_HIDE_SEPARATOR, m_separatorHidden);
+
+ return true;
+}
+
+CSettingControlLabel::CSettingControlLabel()
+{
+ m_format = "string";
+}
+
+bool CSettingControlColorButton::SetFormat(const std::string& format)
+{
+ return format.empty() || StringUtils::EqualsNoCase(format, "string");
+}
diff --git a/xbmc/settings/SettingControl.h b/xbmc/settings/SettingControl.h
new file mode 100644
index 0000000..c86f040
--- /dev/null
+++ b/xbmc/settings/SettingControl.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingControl.h"
+#include "settings/lib/ISettingControlCreator.h"
+
+#define SETTING_XML_ELM_CONTROL_FORMATLABEL "formatlabel"
+#define SETTING_XML_ELM_CONTROL_HIDDEN "hidden"
+#define SETTING_XML_ELM_CONTROL_VERIFYNEW "verifynew"
+#define SETTING_XML_ELM_CONTROL_HEADING "heading"
+#define SETTING_XML_ELM_CONTROL_HIDEVALUE "hidevalue"
+#define SETTING_XML_ELM_CONTROL_MULTISELECT "multiselect"
+#define SETTING_XML_ELM_CONTROL_POPUP "popup"
+#define SETTING_XML_ELM_CONTROL_FORMATVALUE "value"
+#define SETTING_XML_ELM_CONTROL_ADDBUTTONLABEL "addbuttonlabel"
+#define SETTING_XML_ATTR_SHOW_MORE "more"
+#define SETTING_XML_ATTR_SHOW_DETAILS "details"
+#define SETTING_XML_ATTR_SEPARATOR_POSITION "separatorposition"
+#define SETTING_XML_ATTR_HIDE_SEPARATOR "hideseparator"
+
+class CVariant;
+
+class CSettingControlCreator : public ISettingControlCreator
+{
+public:
+ // implementation of ISettingControlCreator
+ std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const override;
+
+protected:
+ CSettingControlCreator() = default;
+ ~CSettingControlCreator() override = default;
+};
+
+class CSettingControlCheckmark : public ISettingControl
+{
+public:
+ CSettingControlCheckmark()
+ {
+ m_format = "boolean";
+ }
+ ~CSettingControlCheckmark() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "toggle"; }
+ bool SetFormat(const std::string &format) override;
+};
+
+class CSettingControlFormattedRange : public ISettingControl
+{
+public:
+ ~CSettingControlFormattedRange() override = default;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ int GetFormatLabel() const { return m_formatLabel; }
+ void SetFormatLabel(int formatLabel) { m_formatLabel = formatLabel; }
+ const std::string& GetFormatString() const { return m_formatString; }
+ void SetFormatString(const std::string &formatString) { m_formatString = formatString; }
+ int GetMinimumLabel() const { return m_minimumLabel; }
+ void SetMinimumLabel(int minimumLabel) { m_minimumLabel = minimumLabel; }
+
+protected:
+ CSettingControlFormattedRange() = default;
+
+ int m_formatLabel = -1;
+ std::string m_formatString = "{}";
+ int m_minimumLabel = -1;
+};
+
+class CSettingControlSpinner : public CSettingControlFormattedRange
+{
+public:
+ CSettingControlSpinner() = default;
+ ~CSettingControlSpinner() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "spinner"; }
+
+ // specialization of CSettingControlFormattedRange
+ bool SetFormat(const std::string &format) override;
+};
+
+class CSettingControlEdit : public ISettingControl
+{
+public:
+ CSettingControlEdit()
+ {
+ m_delayed = true;
+ }
+ ~CSettingControlEdit() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "edit"; }
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetFormat(const std::string &format) override;
+
+ bool IsHidden() const { return m_hidden; }
+ void SetHidden(bool hidden) { m_hidden = hidden; }
+ bool VerifyNewValue() const { return m_verifyNewValue; }
+ void SetVerifyNewValue(bool verifyNewValue) { m_verifyNewValue = verifyNewValue; }
+ int GetHeading() const { return m_heading; }
+ void SetHeading(int heading) { m_heading = heading; }
+
+protected:
+ bool m_hidden = false;
+ bool m_verifyNewValue = false;
+ int m_heading = -1;
+};
+
+class CSettingControlButton : public ISettingControl
+{
+public:
+ CSettingControlButton() = default;
+ ~CSettingControlButton() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "button"; }
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetFormat(const std::string &format) override;
+
+ int GetHeading() const { return m_heading; }
+ void SetHeading(int heading) { m_heading = heading; }
+ bool HideValue() const { return m_hideValue; }
+ void SetHideValue(bool hideValue) { m_hideValue = hideValue; }
+
+ bool ShowAddonDetails() const { return m_showAddonDetails; }
+ void SetShowAddonDetails(bool showAddonDetails) { m_showAddonDetails = showAddonDetails; }
+ bool ShowInstalledAddons() const { return m_showInstalledAddons; }
+ void SetShowInstalledAddons(bool showInstalledAddons) { m_showInstalledAddons = showInstalledAddons; }
+ bool ShowInstallableAddons() const { return m_showInstallableAddons; }
+ void SetShowInstallableAddons(bool showInstallableAddons) { m_showInstallableAddons = showInstallableAddons; }
+ bool ShowMoreAddons() const { return !m_showInstallableAddons && m_showMoreAddons; }
+ void SetShowMoreAddons(bool showMoreAddons) { m_showMoreAddons = showMoreAddons; }
+
+ bool UseImageThumbs() const { return m_useImageThumbs; }
+ void SetUseImageThumbs(bool useImageThumbs) { m_useImageThumbs = useImageThumbs; }
+ bool UseFileDirectories() const { return m_useFileDirectories; }
+ void SetUseFileDirectories(bool useFileDirectories) { m_useFileDirectories = useFileDirectories; }
+
+ bool HasActionData() const { return !m_actionData.empty(); }
+ const std::string& GetActionData() const { return m_actionData; }
+ void SetActionData(const std::string& actionData) { m_actionData = actionData; }
+
+ bool CloseDialog() const { return m_closeDialog; }
+ void SetCloseDialog(bool closeDialog) { m_closeDialog = closeDialog; }
+
+protected:
+ int m_heading = -1;
+ bool m_hideValue = false;
+
+ bool m_showAddonDetails = true;
+ bool m_showInstalledAddons = true;
+ bool m_showInstallableAddons = false;
+ bool m_showMoreAddons = true;
+
+ bool m_useImageThumbs = false;
+ bool m_useFileDirectories = false;
+
+ std::string m_actionData;
+ bool m_closeDialog = false;
+};
+
+class CSetting;
+using SettingControlListValueFormatter =
+ std::string (*)(const std::shared_ptr<const CSetting>& setting);
+
+class CSettingControlList : public CSettingControlFormattedRange
+{
+public:
+ CSettingControlList() = default;
+ ~CSettingControlList() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "list"; }
+
+ // specialization of CSettingControlFormattedRange
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetFormat(const std::string &format) override;
+
+ int GetHeading() const { return m_heading; }
+ void SetHeading(int heading) { m_heading = heading; }
+ bool CanMultiSelect() const { return m_multiselect; }
+ void SetMultiSelect(bool multiselect) { m_multiselect = multiselect; }
+ bool HideValue() const { return m_hideValue; }
+ void SetHideValue(bool hideValue) { m_hideValue = hideValue; }
+ int GetAddButtonLabel() const { return m_addButtonLabel; }
+ void SetAddButtonLabel(int label) { m_addButtonLabel = label; }
+
+ SettingControlListValueFormatter GetFormatter() const { return m_formatter; }
+ void SetFormatter(SettingControlListValueFormatter formatter) { m_formatter = formatter; }
+
+ bool UseDetails() const { return m_useDetails; }
+ void SetUseDetails(bool useDetails) { m_useDetails = useDetails; }
+
+protected:
+ int m_heading = -1;
+ bool m_multiselect = false;
+ bool m_hideValue = false;
+ int m_addButtonLabel = -1;
+ SettingControlListValueFormatter m_formatter = nullptr;
+ bool m_useDetails{false};
+};
+
+class CSettingControlSlider;
+using SettingControlSliderFormatter =
+ std::string (*)(const std::shared_ptr<const CSettingControlSlider>& control,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum);
+
+class CSettingControlSlider : public ISettingControl
+{
+public:
+ CSettingControlSlider() = default;
+ ~CSettingControlSlider() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "slider"; }
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetFormat(const std::string &format) override;
+
+ int GetHeading() const { return m_heading; }
+ void SetHeading(int heading) { m_heading = heading; }
+ bool UsePopup() const { return m_popup; }
+ void SetPopup(bool popup) { m_popup = popup; }
+ int GetFormatLabel() const { return m_formatLabel; }
+ void SetFormatLabel(int formatLabel) { m_formatLabel = formatLabel; }
+ const std::string& GetFormatString() const { return m_formatString; }
+ void SetFormatString(const std::string &formatString) { m_formatString = formatString; }
+ std::string GetDefaultFormatString() const;
+
+ SettingControlSliderFormatter GetFormatter() const { return m_formatter; }
+ void SetFormatter(SettingControlSliderFormatter formatter) { m_formatter = formatter; }
+
+protected:
+ int m_heading = -1;
+ bool m_popup = false;
+ int m_formatLabel = -1;
+ std::string m_formatString;
+ SettingControlSliderFormatter m_formatter = nullptr;
+};
+
+class CSettingControlRange : public ISettingControl
+{
+public:
+ CSettingControlRange() = default;
+ ~CSettingControlRange() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "range"; }
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetFormat(const std::string &format) override;
+
+ int GetFormatLabel() const { return m_formatLabel; }
+ void SetFormatLabel(int formatLabel) { m_formatLabel = formatLabel; }
+ int GetValueFormatLabel() const { return m_valueFormatLabel; }
+ void SetValueFormatLabel(int valueFormatLabel) { m_valueFormatLabel = valueFormatLabel; }
+ const std::string& GetValueFormat() const { return m_valueFormat; }
+ void SetValueFormat(const std::string &valueFormat) { m_valueFormat = valueFormat; }
+
+protected:
+ int m_formatLabel = 21469;
+ int m_valueFormatLabel = -1;
+ std::string m_valueFormat = "{}";
+};
+
+class CSettingControlTitle : public ISettingControl
+{
+public:
+ CSettingControlTitle() = default;
+ ~CSettingControlTitle() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "title"; }
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool IsSeparatorHidden() const { return m_separatorHidden; }
+ void SetSeparatorHidden(bool hidden) { m_separatorHidden = hidden; }
+ bool IsSeparatorBelowLabel() const { return m_separatorBelowLabel; }
+ void SetSeparatorBelowLabel(bool below) { m_separatorBelowLabel = below; }
+
+protected:
+ bool m_separatorHidden = false;
+ bool m_separatorBelowLabel = true;
+};
+
+class CSettingControlLabel : public ISettingControl
+{
+public:
+ CSettingControlLabel();
+ ~CSettingControlLabel() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "label"; }
+};
+
+class CSettingControlColorButton : public ISettingControl
+{
+public:
+ CSettingControlColorButton() { m_format = "string"; }
+ ~CSettingControlColorButton() override = default;
+
+ // implementation of ISettingControl
+ std::string GetType() const override { return "colorbutton"; }
+ bool SetFormat(const std::string& format) override;
+};
diff --git a/xbmc/settings/SettingCreator.cpp b/xbmc/settings/SettingCreator.cpp
new file mode 100644
index 0000000..afe1d89
--- /dev/null
+++ b/xbmc/settings/SettingCreator.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013-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 "SettingCreator.h"
+
+#include "settings/SettingAddon.h"
+#include "settings/SettingDateTime.h"
+#include "settings/SettingPath.h"
+#include "utils/StringUtils.h"
+
+std::shared_ptr<CSetting> CSettingCreator::CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager /* = nullptr */) const
+{
+ if (StringUtils::EqualsNoCase(settingType, "addon"))
+ return std::make_shared<CSettingAddon>(settingId, settingsManager);
+ else if (StringUtils::EqualsNoCase(settingType, "path"))
+ return std::make_shared<CSettingPath>(settingId, settingsManager);
+ else if (StringUtils::EqualsNoCase(settingType, "date"))
+ return std::make_shared<CSettingDate>(settingId, settingsManager);
+ else if (StringUtils::EqualsNoCase(settingType, "time"))
+ return std::make_shared<CSettingTime>(settingId, settingsManager);
+
+ return nullptr;
+}
diff --git a/xbmc/settings/SettingCreator.h b/xbmc/settings/SettingCreator.h
new file mode 100644
index 0000000..68d4588
--- /dev/null
+++ b/xbmc/settings/SettingCreator.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingCreator.h"
+
+class CSettingCreator : public ISettingCreator
+{
+public:
+ // implementation of ISettingCreator
+ std::shared_ptr<CSetting> CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager = nullptr) const override;
+
+protected:
+ CSettingCreator() = default;
+ ~CSettingCreator() override = default;
+};
diff --git a/xbmc/settings/SettingDateTime.cpp b/xbmc/settings/SettingDateTime.cpp
new file mode 100644
index 0000000..48a736c
--- /dev/null
+++ b/xbmc/settings/SettingDateTime.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017-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 "SettingDateTime.h"
+
+#include "XBDateTime.h"
+#include "utils/TimeUtils.h"
+
+#include <shared_mutex>
+
+CSettingDate::CSettingDate(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
+ : CSettingString(id, settingsManager)
+{ }
+
+CSettingDate::CSettingDate(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager /* = NULL */)
+ : CSettingString(id, label, value, settingsManager)
+{ }
+
+CSettingDate::CSettingDate(const std::string &id, const CSettingDate &setting)
+ : CSettingString(id, setting)
+{ }
+
+SettingPtr CSettingDate::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingDate>(id, *this);
+}
+
+bool CSettingDate::CheckValidity(const std::string &value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (!CSettingString::CheckValidity(value))
+ return false;
+
+ return CDateTime::FromDBDate(value).IsValid();
+}
+
+CDateTime CSettingDate::GetDate() const
+{
+ return CDateTime::FromDBDate(GetValue());
+}
+
+bool CSettingDate::SetDate(const CDateTime& date)
+{
+ return SetValue(date.GetAsDBDate());
+}
+
+CSettingTime::CSettingTime(const std::string &id, CSettingsManager *settingsManager /* = NULL */)
+ : CSettingString(id, settingsManager)
+{ }
+
+CSettingTime::CSettingTime(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager /* = NULL */)
+ : CSettingString(id, label, value, settingsManager)
+{ }
+
+CSettingTime::CSettingTime(const std::string &id, const CSettingTime &setting)
+ : CSettingString(id, setting)
+{ }
+
+SettingPtr CSettingTime::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingTime>(id, *this);
+}
+
+bool CSettingTime::CheckValidity(const std::string &value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (!CSettingString::CheckValidity(value))
+ return false;
+
+ return CDateTime::FromDBTime(value).IsValid();
+}
+
+CDateTime CSettingTime::GetTime() const
+{
+ return CDateTime::FromDBTime(GetValue());
+}
+
+bool CSettingTime::SetTime(const CDateTime& time)
+{
+ return SetValue(CTimeUtils::WithoutSeconds(time.GetAsDBTime()));
+}
diff --git a/xbmc/settings/SettingDateTime.h b/xbmc/settings/SettingDateTime.h
new file mode 100644
index 0000000..5ee481f
--- /dev/null
+++ b/xbmc/settings/SettingDateTime.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+#include "settings/lib/Setting.h"
+
+class CDateTime;
+
+class CSettingDate : public CSettingString
+{
+public:
+ CSettingDate(const std::string &id, CSettingsManager *settingsManager = NULL);
+ CSettingDate(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = NULL);
+ CSettingDate(const std::string &id, const CSettingDate &setting);
+ ~CSettingDate() override = default;
+
+ SettingPtr Clone(const std::string &id) const override;
+
+ bool CheckValidity(const std::string &value) const override;
+
+ CDateTime GetDate() const;
+ bool SetDate(const CDateTime& date);
+};
+
+class CSettingTime : public CSettingString
+{
+public:
+ CSettingTime(const std::string &id, CSettingsManager *settingsManager = NULL);
+ CSettingTime(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = NULL);
+ CSettingTime(const std::string &id, const CSettingTime &setting);
+ ~CSettingTime() override = default;
+
+ SettingPtr Clone(const std::string &id) const override;
+
+ bool CheckValidity(const std::string &value) const override;
+
+ CDateTime GetTime() const;
+ bool SetTime(const CDateTime& time);
+};
diff --git a/xbmc/settings/SettingPath.cpp b/xbmc/settings/SettingPath.cpp
new file mode 100644
index 0000000..fa215ff
--- /dev/null
+++ b/xbmc/settings/SettingPath.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2013-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 "SettingPath.h"
+
+#include "settings/lib/SettingsManager.h"
+#include "utils/FileExtensionProvider.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+#define XML_ELM_DEFAULT "default"
+#define XML_ELM_CONSTRAINTS "constraints"
+
+CSettingPath::CSettingPath(const std::string &id, CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingString(id, settingsManager)
+{ }
+
+CSettingPath::CSettingPath(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingString(id, label, value, settingsManager)
+{ }
+
+CSettingPath::CSettingPath(const std::string &id, const CSettingPath &setting)
+ : CSettingString(id, setting)
+{
+ copy(setting);
+}
+
+SettingPtr CSettingPath::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingPath>(id, *this);
+}
+
+bool CSettingPath::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSettingString::Deserialize(node, update))
+ return false;
+
+ if (m_control != nullptr &&
+ (m_control->GetType() != "button" || (m_control->GetFormat() != "path" && m_control->GetFormat() != "file" && m_control->GetFormat() != "image")))
+ {
+ CLog::Log(LOGERROR, "CSettingPath: invalid <control> of \"{}\"", m_id);
+ return false;
+ }
+
+ auto constraints = node->FirstChild(XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get writable
+ XMLUtils::GetBoolean(constraints, "writable", m_writable);
+ // get hide extensions
+ XMLUtils::GetBoolean(constraints, "hideextensions", m_hideExtension);
+
+ // get sources
+ auto sources = constraints->FirstChild("sources");
+ if (sources != nullptr)
+ {
+ m_sources.clear();
+ auto source = sources->FirstChild("source");
+ while (source != nullptr)
+ {
+ auto child = source->FirstChild();
+ if (child != nullptr)
+ {
+ const std::string& strSource = child->ValueStr();
+ if (!strSource.empty())
+ m_sources.push_back(strSource);
+ }
+
+ source = source->NextSibling("source");
+ }
+ }
+
+ // get masking
+ auto masking = constraints->FirstChild("masking");
+ if (masking != nullptr)
+ m_masking = masking->FirstChild()->ValueStr();
+ }
+
+ return true;
+}
+
+bool CSettingPath::SetValue(const std::string &value)
+{
+ // for backwards compatibility to Frodo
+ if (StringUtils::EqualsNoCase(value, "select folder") ||
+ StringUtils::EqualsNoCase(value, "select writable folder"))
+ return CSettingString::SetValue("");
+
+ return CSettingString::SetValue(value);
+}
+
+std::string CSettingPath::GetMasking(const CFileExtensionProvider& fileExtensionProvider) const
+{
+ if (m_masking.empty())
+ return m_masking;
+
+ // setup masking
+ auto audioMask = fileExtensionProvider.GetMusicExtensions();
+ auto videoMask = fileExtensionProvider.GetVideoExtensions();
+ auto imageMask = fileExtensionProvider.GetPictureExtensions();
+ auto execMask = "";
+#if defined(TARGET_WINDOWS)
+ execMask = ".exe|.bat|.cmd|.py";
+#endif // defined(TARGET_WINDOWS)
+
+ std::string masking = m_masking;
+ if (masking == "video")
+ return videoMask;
+ if (masking == "audio")
+ return audioMask;
+ if (masking == "image")
+ return imageMask;
+ if (masking == "executable")
+ return execMask;
+
+ // convert mask qualifiers
+ StringUtils::Replace(masking, "$AUDIO", audioMask);
+ StringUtils::Replace(masking, "$VIDEO", videoMask);
+ StringUtils::Replace(masking, "$IMAGE", imageMask);
+ StringUtils::Replace(masking, "$EXECUTABLE", execMask);
+
+ return masking;
+}
+
+void CSettingPath::copy(const CSettingPath& setting)
+{
+ CSettingString::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_writable = setting.m_writable;
+ m_sources = setting.m_sources;
+ m_hideExtension = setting.m_hideExtension;
+ m_masking = setting.m_masking;
+}
diff --git a/xbmc/settings/SettingPath.h b/xbmc/settings/SettingPath.h
new file mode 100644
index 0000000..423782a
--- /dev/null
+++ b/xbmc/settings/SettingPath.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "settings/lib/Setting.h"
+
+#include <vector>
+
+class CFileExtensionProvider;
+
+class CSettingPath : public CSettingString
+{
+public:
+ CSettingPath(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingPath(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = nullptr);
+ CSettingPath(const std::string &id, const CSettingPath &setting);
+ ~CSettingPath() override = default;
+
+ SettingPtr Clone(const std::string &id) const override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+ bool SetValue(const std::string &value) override;
+
+ bool Writable() const { return m_writable; }
+ void SetWritable(bool writable) { m_writable = writable; }
+ const std::vector<std::string>& GetSources() const { return m_sources; }
+ void SetSources(const std::vector<std::string> &sources) { m_sources = sources; }
+ bool HideExtension() const { return m_hideExtension; }
+ void SetHideExtension(bool hideExtension) { m_hideExtension = hideExtension; }
+ std::string GetMasking(const CFileExtensionProvider& fileExtensionProvider) const;
+ void SetMasking(const std::string& masking) { m_masking = masking; }
+
+private:
+ using CSettingString::copy;
+ void copy(const CSettingPath &setting);
+
+ bool m_writable = true;
+ std::vector<std::string> m_sources;
+ bool m_hideExtension = false;
+ std::string m_masking;
+};
diff --git a/xbmc/settings/SettingUtils.cpp b/xbmc/settings/SettingUtils.cpp
new file mode 100644
index 0000000..c690932
--- /dev/null
+++ b/xbmc/settings/SettingUtils.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2013-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 "SettingUtils.h"
+
+#include "settings/lib/Setting.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+#include <algorithm>
+
+std::vector<CVariant> CSettingUtils::GetList(const std::shared_ptr<const CSettingList>& settingList)
+{
+ return ListToValues(settingList, settingList->GetValue());
+}
+
+bool CSettingUtils::SetList(const std::shared_ptr<CSettingList>& settingList,
+ const std::vector<CVariant>& value)
+{
+ SettingList newValues;
+ if (!ValuesToList(settingList, value, newValues))
+ return false;
+
+ return settingList->SetValue(newValues);
+}
+
+std::vector<CVariant> CSettingUtils::ListToValues(
+ const std::shared_ptr<const CSettingList>& setting,
+ const std::vector<std::shared_ptr<CSetting>>& values)
+{
+ std::vector<CVariant> realValues;
+
+ if (setting == NULL)
+ return realValues;
+
+ for (const auto& value : values)
+ {
+ switch (setting->GetElementType())
+ {
+ case SettingType::Boolean:
+ realValues.emplace_back(std::static_pointer_cast<const CSettingBool>(value)->GetValue());
+ break;
+
+ case SettingType::Integer:
+ realValues.emplace_back(std::static_pointer_cast<const CSettingInt>(value)->GetValue());
+ break;
+
+ case SettingType::Number:
+ realValues.emplace_back(std::static_pointer_cast<const CSettingNumber>(value)->GetValue());
+ break;
+
+ case SettingType::String:
+ realValues.emplace_back(std::static_pointer_cast<const CSettingString>(value)->GetValue());
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return realValues;
+}
+
+bool CSettingUtils::ValuesToList(const std::shared_ptr<const CSettingList>& setting,
+ const std::vector<CVariant>& values,
+ std::vector<std::shared_ptr<CSetting>>& newValues)
+{
+ if (setting == NULL)
+ return false;
+
+ int index = 0;
+ bool ret = true;
+ for (const auto& value : values)
+ {
+ SettingPtr settingValue =
+ setting->GetDefinition()->Clone(StringUtils::Format("{}.{}", setting->GetId(), index++));
+ if (settingValue == NULL)
+ return false;
+
+ switch (setting->GetElementType())
+ {
+ case SettingType::Boolean:
+ if (!value.isBoolean())
+ ret = false;
+ else
+ ret = std::static_pointer_cast<CSettingBool>(settingValue)->SetValue(value.asBoolean());
+ break;
+
+ case SettingType::Integer:
+ if (!value.isInteger())
+ ret = false;
+ else
+ ret = std::static_pointer_cast<CSettingInt>(settingValue)->SetValue(static_cast<int>(value.asInteger()));
+ break;
+
+ case SettingType::Number:
+ if (!value.isDouble())
+ ret = false;
+ else
+ ret = std::static_pointer_cast<CSettingNumber>(settingValue)->SetValue(value.asDouble());
+ break;
+
+ case SettingType::String:
+ if (!value.isString())
+ ret = false;
+ else
+ ret = std::static_pointer_cast<CSettingString>(settingValue)->SetValue(value.asString());
+ break;
+
+ default:
+ ret = false;
+ break;
+ }
+
+ if (!ret)
+ return false;
+
+ newValues.push_back(std::const_pointer_cast<CSetting>(settingValue));
+ }
+
+ return true;
+}
+
+bool CSettingUtils::FindIntInList(const std::shared_ptr<const CSettingList>& settingList, int value)
+{
+ if (settingList == nullptr || settingList->GetElementType() != SettingType::Integer)
+ return false;
+
+ const auto values = settingList->GetValue();
+ const auto matchingValue =
+ std::find_if(values.begin(), values.end(), [value](const SettingPtr& setting) {
+ return std::static_pointer_cast<CSettingInt>(setting)->GetValue() == value;
+ });
+ return matchingValue != values.end();
+}
diff --git a/xbmc/settings/SettingUtils.h b/xbmc/settings/SettingUtils.h
new file mode 100644
index 0000000..d7e456b
--- /dev/null
+++ b/xbmc/settings/SettingUtils.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <vector>
+
+class CVariant;
+class CSettingList;
+class CSetting;
+
+class CSettingUtils
+{
+public:
+ /*!
+ \brief Gets the values of the given list setting.
+
+ \param settingList List setting
+ \return List of values of the given list setting
+ */
+ static std::vector<CVariant> GetList(const std::shared_ptr<const CSettingList>& settingList);
+ /*!
+ \brief Sets the values of the given list setting.
+
+ \param settingList List setting
+ \param value Values to set
+ \return True if setting the values was successful, false otherwise
+ */
+ static bool SetList(const std::shared_ptr<CSettingList>& settingList,
+ const std::vector<CVariant>& value);
+
+ static std::vector<CVariant> ListToValues(const std::shared_ptr<const CSettingList>& setting,
+ const std::vector<std::shared_ptr<CSetting>>& values);
+ static bool ValuesToList(const std::shared_ptr<const CSettingList>& setting,
+ const std::vector<CVariant>& values,
+ std::vector<std::shared_ptr<CSetting>>& newValues);
+
+ /*!
+ \brief Search in a list of Ints for a given value.
+
+ \param settingList CSettingList instance
+ \param value value to search for
+ \return True if value was found in list, false otherwise
+ */
+ static bool FindIntInList(const std::shared_ptr<const CSettingList>& settingList, int value);
+};
diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp
new file mode 100644
index 0000000..b403b63
--- /dev/null
+++ b/xbmc/settings/Settings.cpp
@@ -0,0 +1,1088 @@
+/*
+ * 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 "Settings.h"
+
+#include "Autorun.h"
+#include "GUIPassword.h"
+#include "LangInfo.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/Skin.h"
+#include "application/AppParams.h"
+#include "cores/VideoPlayer/VideoRenderers/BaseRenderer.h"
+#include "filesystem/File.h"
+#include "guilib/GUIFontManager.h"
+#include "guilib/StereoscopicsManager.h"
+#include "input/KeyboardLayoutManager.h"
+
+#include <mutex>
+#if defined(TARGET_POSIX)
+#include "platform/posix/PosixTimezone.h"
+#endif // defined(TARGET_POSIX)
+#include "network/upnp/UPnPSettings.h"
+#include "network/WakeOnAccess.h"
+#if defined(TARGET_DARWIN_OSX)
+#include "platform/darwin/osx/XBMCHelper.h"
+#endif // defined(TARGET_DARWIN_OSX)
+#if defined(TARGET_DARWIN_TVOS)
+#include "platform/darwin/tvos/TVOSSettingsHandler.h"
+#endif // defined(TARGET_DARWIN_TVOS)
+#if defined(TARGET_DARWIN_EMBEDDED)
+#include "SettingAddon.h"
+#endif
+#include "DiscSettings.h"
+#include "SeekHandler.h"
+#include "ServiceBroker.h"
+#include "powermanagement/PowerTypes.h"
+#include "profiles/ProfileManager.h"
+#include "settings/DisplaySettings.h"
+#include "settings/MediaSettings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/SettingConditions.h"
+#include "settings/SettingsComponent.h"
+#include "settings/SkinSettings.h"
+#include "settings/SubtitlesSettings.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/CharsetConverter.h"
+#include "utils/RssManager.h"
+#include "utils/StringUtils.h"
+#include "utils/SystemInfo.h"
+#include "utils/Variant.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+#include "view/ViewStateSettings.h"
+
+#define SETTINGS_XML_FOLDER "special://xbmc/system/settings/"
+
+using namespace KODI;
+using namespace XFILE;
+
+//! @todo: remove in c++17
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SKIN;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SKINSETTINGS;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SKINTHEME;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SKINCOLORS;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_FONT;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SKINZOOM;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_STARTUPACTION;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_STARTUPWINDOW;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_ENABLERSSFEEDS;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_RSSEDIT;
+constexpr const char* CSettings::SETTING_LOOKANDFEEL_STEREOSTRENGTH;
+constexpr const char* CSettings::SETTING_LOCALE_LANGUAGE;
+constexpr const char* CSettings::SETTING_LOCALE_COUNTRY;
+constexpr const char* CSettings::SETTING_LOCALE_CHARSET;
+constexpr const char* CSettings::SETTING_LOCALE_KEYBOARDLAYOUTS;
+constexpr const char* CSettings::SETTING_LOCALE_ACTIVEKEYBOARDLAYOUT;
+constexpr const char* CSettings::SETTING_LOCALE_TIMEZONECOUNTRY;
+constexpr const char* CSettings::SETTING_LOCALE_TIMEZONE;
+constexpr const char* CSettings::SETTING_LOCALE_SHORTDATEFORMAT;
+constexpr const char* CSettings::SETTING_LOCALE_LONGDATEFORMAT;
+constexpr const char* CSettings::SETTING_LOCALE_TIMEFORMAT;
+constexpr const char* CSettings::SETTING_LOCALE_USE24HOURCLOCK;
+constexpr const char* CSettings::SETTING_LOCALE_TEMPERATUREUNIT;
+constexpr const char* CSettings::SETTING_LOCALE_SPEEDUNIT;
+constexpr const char* CSettings::SETTING_FILELISTS_SHOWPARENTDIRITEMS;
+constexpr const char* CSettings::SETTING_FILELISTS_SHOWEXTENSIONS;
+constexpr const char* CSettings::SETTING_FILELISTS_IGNORETHEWHENSORTING;
+constexpr const char* CSettings::SETTING_FILELISTS_ALLOWFILEDELETION;
+constexpr const char* CSettings::SETTING_FILELISTS_SHOWADDSOURCEBUTTONS;
+constexpr const char* CSettings::SETTING_FILELISTS_SHOWHIDDEN;
+constexpr const char* CSettings::SETTING_SCREENSAVER_MODE;
+constexpr const char* CSettings::SETTING_SCREENSAVER_SETTINGS;
+constexpr const char* CSettings::SETTING_SCREENSAVER_PREVIEW;
+constexpr const char* CSettings::SETTING_SCREENSAVER_TIME;
+constexpr const char* CSettings::SETTING_SCREENSAVER_USEMUSICVISINSTEAD;
+constexpr const char* CSettings::SETTING_SCREENSAVER_USEDIMONPAUSE;
+constexpr const char* CSettings::SETTING_WINDOW_WIDTH;
+constexpr const char* CSettings::SETTING_WINDOW_HEIGHT;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_ACTORTHUMBS;
+constexpr const char* CSettings::SETTING_MYVIDEOS_FLATTEN;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_FLATTENTVSHOWS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_TVSHOWSSELECTFIRSTUNWATCHEDITEM;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_TVSHOWSINCLUDEALLSEASONSANDSPECIALS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_SHOWALLITEMS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_GROUPMOVIESETS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_GROUPSINGLEITEMSETS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_UPDATEONSTARTUP;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_CLEANUP;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_EXPORT;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_IMPORT;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_SHOWEMPTYTVSHOWS;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_MOVIESETSFOLDER;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_ARTWORK_LEVEL;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED;
+constexpr const char* CSettings::SETTING_VIDEOLIBRARY_SHOWPERFORMERS;
+constexpr const char* CSettings::SETTING_LOCALE_AUDIOLANGUAGE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_SEEKSTEPS;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_SEEKDELAY;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_ERRORINASPECT;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_STRETCH43;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_TELETEXTENABLED;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_TELETEXTSCALE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_QUITSTEREOMODEONSTOP;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_RENDERMETHOD;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_HQSCALERS;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEMEDIACODEC;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEMEDIACODECSURFACE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVDPAU;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVDPAUMIXER;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG2;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVDPAUMPEG4;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVDPAUVC1;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEDXVA2;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEVTB;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USEPRIMEDECODER;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_USESTAGEFRIGHT;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_LIMITGUIUPDATE;
+constexpr const char* CSettings::SETTING_VIDEOPLAYER_SUPPORTMVC;
+constexpr const char* CSettings::SETTING_MYVIDEOS_SELECTACTION;
+constexpr const char* CSettings::SETTING_MYVIDEOS_USETAGS;
+constexpr const char* CSettings::SETTING_MYVIDEOS_EXTRACTFLAGS;
+constexpr const char* CSettings::SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS;
+constexpr const char* CSettings::SETTING_MYVIDEOS_REPLACELABELS;
+constexpr const char* CSettings::SETTING_MYVIDEOS_EXTRACTTHUMB;
+constexpr const char* CSettings::SETTING_MYVIDEOS_STACKVIDEOS;
+constexpr const char* CSettings::SETTING_LOCALE_SUBTITLELANGUAGE;
+constexpr const char* CSettings::SETTING_SUBTITLES_PARSECAPTIONS;
+constexpr const char* CSettings::SETTING_SUBTITLES_CAPTIONSALIGN;
+constexpr const char* CSettings::SETTING_SUBTITLES_ALIGN;
+constexpr const char* CSettings::SETTING_SUBTITLES_STEREOSCOPICDEPTH;
+constexpr const char* CSettings::SETTING_SUBTITLES_FONTNAME;
+constexpr const char* CSettings::SETTING_SUBTITLES_FONTSIZE;
+constexpr const char* CSettings::SETTING_SUBTITLES_STYLE;
+constexpr const char* CSettings::SETTING_SUBTITLES_COLOR;
+constexpr const char* CSettings::SETTING_SUBTITLES_BORDERSIZE;
+constexpr const char* CSettings::SETTING_SUBTITLES_BORDERCOLOR;
+constexpr const char* CSettings::SETTING_SUBTITLES_BGCOLOR;
+constexpr const char* CSettings::SETTING_SUBTITLES_BGOPACITY;
+constexpr const char* CSettings::SETTING_SUBTITLES_BLUR;
+constexpr const char* CSettings::SETTING_SUBTITLES_BACKGROUNDTYPE;
+constexpr const char* CSettings::SETTING_SUBTITLES_SHADOWCOLOR;
+constexpr const char* CSettings::SETTING_SUBTITLES_SHADOWOPACITY;
+constexpr const char* CSettings::SETTING_SUBTITLES_SHADOWSIZE;
+constexpr const char* CSettings::SETTING_SUBTITLES_CHARSET;
+constexpr const char* CSettings::SETTING_SUBTITLES_OVERRIDEFONTS;
+constexpr const char* CSettings::SETTING_SUBTITLES_OVERRIDESTYLES;
+constexpr const char* CSettings::SETTING_SUBTITLES_LANGUAGES;
+constexpr const char* CSettings::SETTING_SUBTITLES_STORAGEMODE;
+constexpr const char* CSettings::SETTING_SUBTITLES_CUSTOMPATH;
+constexpr const char* CSettings::SETTING_SUBTITLES_PAUSEONSEARCH;
+constexpr const char* CSettings::SETTING_SUBTITLES_DOWNLOADFIRST;
+constexpr const char* CSettings::SETTING_SUBTITLES_TV;
+constexpr const char* CSettings::SETTING_SUBTITLES_MOVIE;
+constexpr const char* CSettings::SETTING_DVDS_AUTORUN;
+constexpr const char* CSettings::SETTING_DVDS_PLAYERREGION;
+constexpr const char* CSettings::SETTING_DVDS_AUTOMENU;
+constexpr const char* CSettings::SETTING_DISC_PLAYBACK;
+constexpr const char* CSettings::SETTING_BLURAY_PLAYERREGION;
+constexpr const char* CSettings::SETTING_ACCESSIBILITY_AUDIOVISUAL;
+constexpr const char* CSettings::SETTING_ACCESSIBILITY_AUDIOHEARING;
+constexpr const char* CSettings::SETTING_ACCESSIBILITY_SUBHEARING;
+constexpr const char* CSettings::SETTING_SCRAPERS_MOVIESDEFAULT;
+constexpr const char* CSettings::SETTING_SCRAPERS_TVSHOWSDEFAULT;
+constexpr const char* CSettings::SETTING_SCRAPERS_MUSICVIDEOSDEFAULT;
+constexpr const char* CSettings::SETTING_PVRMANAGER_PRESELECTPLAYINGCHANNEL;
+constexpr const char* CSettings::SETTING_PVRMANAGER_SYNCCHANNELGROUPS;
+constexpr const char* CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER;
+constexpr const char* CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS;
+constexpr const char* CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERSALWAYS;
+constexpr const char* CSettings::SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE;
+constexpr const char* CSettings::SETTING_PVRMANAGER_CLIENTPRIORITIES;
+constexpr const char* CSettings::SETTING_PVRMANAGER_CHANNELMANAGER;
+constexpr const char* CSettings::SETTING_PVRMANAGER_GROUPMANAGER;
+constexpr const char* CSettings::SETTING_PVRMANAGER_CHANNELSCAN;
+constexpr const char* CSettings::SETTING_PVRMANAGER_RESETDB;
+constexpr const char* CSettings::SETTING_PVRMANAGER_ADDONS;
+constexpr const char* CSettings::SETTING_PVRMENU_DISPLAYCHANNELINFO;
+constexpr const char* CSettings::SETTING_PVRMENU_CLOSECHANNELOSDONSWITCH;
+constexpr const char* CSettings::SETTING_PVRMENU_ICONPATH;
+constexpr const char* CSettings::SETTING_PVRMENU_SEARCHICONS;
+constexpr const char* CSettings::SETTING_EPG_PAST_DAYSTODISPLAY;
+constexpr const char* CSettings::SETTING_EPG_FUTURE_DAYSTODISPLAY;
+constexpr const char* CSettings::SETTING_EPG_SELECTACTION;
+constexpr const char* CSettings::SETTING_EPG_HIDENOINFOAVAILABLE;
+constexpr const char* CSettings::SETTING_EPG_EPGUPDATE;
+constexpr const char* CSettings::SETTING_EPG_PREVENTUPDATESWHILEPLAYINGTV;
+constexpr const char* CSettings::SETTING_EPG_RESETEPG;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_SWITCHTOFULLSCREENCHANNELTYPES;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_SIGNALQUALITY;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_CHANNELENTRYTIMEOUT;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_DELAYMARKLASTWATCHED;
+constexpr const char* CSettings::SETTING_PVRPLAYBACK_FPS;
+constexpr const char* CSettings::SETTING_PVRRECORD_INSTANTRECORDACTION;
+constexpr const char* CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME;
+constexpr const char* CSettings::SETTING_PVRRECORD_MARGINSTART;
+constexpr const char* CSettings::SETTING_PVRRECORD_MARGINEND;
+constexpr const char* CSettings::SETTING_PVRRECORD_TIMERNOTIFICATIONS;
+constexpr const char* CSettings::SETTING_PVRRECORD_GROUPRECORDINGS;
+constexpr const char* CSettings::SETTING_PVRREMINDERS_AUTOCLOSEDELAY;
+constexpr const char* CSettings::SETTING_PVRREMINDERS_AUTORECORD;
+constexpr const char* CSettings::SETTING_PVRREMINDERS_AUTOSWITCH;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_ENABLED;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_SETWAKEUPCMD;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_PREWAKEUP;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUP;
+constexpr const char* CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME;
+constexpr const char* CSettings::SETTING_PVRPARENTAL_ENABLED;
+constexpr const char* CSettings::SETTING_PVRPARENTAL_PIN;
+constexpr const char* CSettings::SETTING_PVRPARENTAL_DURATION;
+constexpr const char* CSettings::SETTING_PVRCLIENT_MENUHOOK;
+constexpr const char* CSettings::SETTING_PVRTIMERS_HIDEDISABLEDTIMERS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_SHOWCOMPILATIONARTISTS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_SHOWDISCS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_USEORIGINALDATE;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_USEARTISTSORTNAME;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_PREFERONLINEALBUMART;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ARTWORKLEVEL;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_USEALLLOCALART;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_USEALLREMOTEART;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ARTISTART_WHITELIST;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ALBUMART_WHITELIST;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_MUSICTHUMBS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_OVERRIDETAGS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_SHOWALLITEMS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_UPDATEONSTARTUP;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_BACKGROUNDUPDATE;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_CLEANUP;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO;
+constexpr const char* CSettings::SETTING_MUSICLIBRARY_IMPORT;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_AUTOPLAYNEXTITEM;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_QUEUEBYDEFAULT;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_SEEKSTEPS;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_SEEKDELAY;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_REPLAYGAINTYPE;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_REPLAYGAINPREAMP;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_REPLAYGAINNOGAINPREAMP;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_CROSSFADE;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_CROSSFADEALBUMTRACKS;
+constexpr const char* CSettings::SETTING_MUSICPLAYER_VISUALISATION;
+constexpr const char* CSettings::SETTING_MUSICFILES_SELECTACTION;
+constexpr const char* CSettings::SETTING_MUSICFILES_USETAGS;
+constexpr const char* CSettings::SETTING_MUSICFILES_TRACKFORMAT;
+constexpr const char* CSettings::SETTING_MUSICFILES_NOWPLAYINGTRACKFORMAT;
+constexpr const char* CSettings::SETTING_MUSICFILES_LIBRARYTRACKFORMAT;
+constexpr const char* CSettings::SETTING_MUSICFILES_FINDREMOTETHUMBS;
+constexpr const char* CSettings::SETTING_AUDIOCDS_AUTOACTION;
+constexpr const char* CSettings::SETTING_AUDIOCDS_USECDDB;
+constexpr const char* CSettings::SETTING_AUDIOCDS_RECORDINGPATH;
+constexpr const char* CSettings::SETTING_AUDIOCDS_TRACKPATHFORMAT;
+constexpr const char* CSettings::SETTING_AUDIOCDS_ENCODER;
+constexpr const char* CSettings::SETTING_AUDIOCDS_SETTINGS;
+constexpr const char* CSettings::SETTING_AUDIOCDS_EJECTONRIP;
+constexpr const char* CSettings::SETTING_MYMUSIC_SONGTHUMBINVIS;
+constexpr const char* CSettings::SETTING_MYMUSIC_DEFAULTLIBVIEW;
+constexpr const char* CSettings::SETTING_PICTURES_USETAGS;
+constexpr const char* CSettings::SETTING_PICTURES_GENERATETHUMBS;
+constexpr const char* CSettings::SETTING_PICTURES_SHOWVIDEOS;
+constexpr const char* CSettings::SETTING_PICTURES_DISPLAYRESOLUTION;
+constexpr const char* CSettings::SETTING_SLIDESHOW_STAYTIME;
+constexpr const char* CSettings::SETTING_SLIDESHOW_DISPLAYEFFECTS;
+constexpr const char* CSettings::SETTING_SLIDESHOW_SHUFFLE;
+constexpr const char* CSettings::SETTING_SLIDESHOW_HIGHQUALITYDOWNSCALING;
+constexpr const char* CSettings::SETTING_WEATHER_CURRENTLOCATION;
+constexpr const char* CSettings::SETTING_WEATHER_ADDON;
+constexpr const char* CSettings::SETTING_WEATHER_ADDONSETTINGS;
+constexpr const char* CSettings::SETTING_SERVICES_DEVICENAME;
+constexpr const char* CSettings::SETTING_SERVICES_DEVICEUUID;
+constexpr const char* CSettings::SETTING_SERVICES_UPNP;
+constexpr const char* CSettings::SETTING_SERVICES_UPNPSERVER;
+constexpr const char* CSettings::SETTING_SERVICES_UPNPANNOUNCE;
+constexpr const char* CSettings::SETTING_SERVICES_UPNPLOOKFOREXTERNALSUBTITLES;
+constexpr const char* CSettings::SETTING_SERVICES_UPNPCONTROLLER;
+constexpr const char* CSettings::SETTING_SERVICES_UPNPRENDERER;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVER;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVERPORT;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVERAUTHENTICATION;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVERUSERNAME;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVERPASSWORD;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSERVERSSL;
+constexpr const char* CSettings::SETTING_SERVICES_WEBSKIN;
+constexpr const char* CSettings::SETTING_SERVICES_ESENABLED;
+constexpr const char* CSettings::SETTING_SERVICES_ESPORT;
+constexpr const char* CSettings::SETTING_SERVICES_ESPORTRANGE;
+constexpr const char* CSettings::SETTING_SERVICES_ESMAXCLIENTS;
+constexpr const char* CSettings::SETTING_SERVICES_ESALLINTERFACES;
+constexpr const char* CSettings::SETTING_SERVICES_ESINITIALDELAY;
+constexpr const char* CSettings::SETTING_SERVICES_ESCONTINUOUSDELAY;
+constexpr const char* CSettings::SETTING_SERVICES_ZEROCONF;
+constexpr const char* CSettings::SETTING_SERVICES_AIRPLAY;
+constexpr const char* CSettings::SETTING_SERVICES_AIRPLAYVOLUMECONTROL;
+constexpr const char* CSettings::SETTING_SERVICES_USEAIRPLAYPASSWORD;
+constexpr const char* CSettings::SETTING_SERVICES_AIRPLAYPASSWORD;
+constexpr const char* CSettings::SETTING_SERVICES_AIRPLAYVIDEOSUPPORT;
+constexpr const char* CSettings::SETTING_SMB_WINSSERVER;
+constexpr const char* CSettings::SETTING_SMB_WORKGROUP;
+constexpr const char* CSettings::SETTING_SMB_MINPROTOCOL;
+constexpr const char* CSettings::SETTING_SMB_MAXPROTOCOL;
+constexpr const char* CSettings::SETTING_SMB_LEGACYSECURITY;
+constexpr const char* CSettings::SETTING_SERVICES_WSDISCOVERY;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_MONITOR;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_SCREEN;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_WHITELIST;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_RESOLUTION;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_SCREENMODE;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_STEREOSCOPICMODE;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_NOOFBUFFERS;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_3DLUT;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_DISPLAYPROFILE;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_GUICALIBRATION;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_TESTPATTERN;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_FRAMEPACKING;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_10BITSURFACES;
+constexpr const char* CSettings::SETTING_VIDEOSCREEN_GUISDRPEAKLUMINANCE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_AUDIODEVICE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_CHANNELS;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_CONFIG;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_SAMPLERATE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_STEREOUPMIX;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_MAINTAINORIGINALVOLUME;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_PROCESSQUALITY;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_ATEMPOTHRESHOLD;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_STREAMSILENCE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_STREAMNOISE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_GUISOUNDMODE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_PASSTHROUGHDEVICE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_AC3PASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_AC3TRANSCODE;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_EAC3PASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_DTSPASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_TRUEHDPASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_DTSHDPASSTHROUGH;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_DTSHDCOREFALLBACK;
+constexpr const char* CSettings::SETTING_AUDIOOUTPUT_VOLUMESTEPS;
+constexpr const char* CSettings::SETTING_INPUT_PERIPHERALS;
+constexpr const char* CSettings::SETTING_INPUT_PERIPHERALLIBRARIES;
+constexpr const char* CSettings::SETTING_INPUT_ENABLEMOUSE;
+constexpr const char* CSettings::SETTING_INPUT_ASKNEWCONTROLLERS;
+constexpr const char* CSettings::SETTING_INPUT_CONTROLLERCONFIG;
+constexpr const char* CSettings::SETTING_INPUT_RUMBLENOTIFY;
+constexpr const char* CSettings::SETTING_INPUT_TESTRUMBLE;
+constexpr const char* CSettings::SETTING_INPUT_CONTROLLERPOWEROFF;
+constexpr const char* CSettings::SETTING_INPUT_APPLEREMOTEMODE;
+constexpr const char* CSettings::SETTING_INPUT_APPLEREMOTEALWAYSON;
+constexpr const char* CSettings::SETTING_INPUT_APPLEREMOTESEQUENCETIME;
+constexpr const char* CSettings::SETTING_INPUT_SIRIREMOTEIDLETIMERENABLED;
+constexpr const char* CSettings::SETTING_INPUT_SIRIREMOTEIDLETIME;
+constexpr const char* CSettings::SETTING_INPUT_SIRIREMOTEHORIZONTALSENSITIVITY;
+constexpr const char* CSettings::SETTING_INPUT_SIRIREMOTEVERTICALSENSITIVITY;
+constexpr const char* CSettings::SETTING_INPUT_TVOSUSEKODIKEYBOARD;
+constexpr const char* CSettings::SETTING_NETWORK_USEHTTPPROXY;
+constexpr const char* CSettings::SETTING_NETWORK_HTTPPROXYTYPE;
+constexpr const char* CSettings::SETTING_NETWORK_HTTPPROXYSERVER;
+constexpr const char* CSettings::SETTING_NETWORK_HTTPPROXYPORT;
+constexpr const char* CSettings::SETTING_NETWORK_HTTPPROXYUSERNAME;
+constexpr const char* CSettings::SETTING_NETWORK_HTTPPROXYPASSWORD;
+constexpr const char* CSettings::SETTING_NETWORK_BANDWIDTH;
+constexpr const char* CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF;
+constexpr const char* CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNTIME;
+constexpr const char* CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE;
+constexpr const char* CSettings::SETTING_POWERMANAGEMENT_WAKEONACCESS;
+constexpr const char* CSettings::SETTING_POWERMANAGEMENT_WAITFORNETWORK;
+constexpr const char* CSettings::SETTING_DEBUG_SHOWLOGINFO;
+constexpr const char* CSettings::SETTING_DEBUG_EXTRALOGGING;
+constexpr const char* CSettings::SETTING_DEBUG_SETEXTRALOGLEVEL;
+constexpr const char* CSettings::SETTING_DEBUG_SCREENSHOTPATH;
+constexpr const char* CSettings::SETTING_DEBUG_SHARE_LOG;
+constexpr const char* CSettings::SETTING_EVENTLOG_ENABLED;
+constexpr const char* CSettings::SETTING_EVENTLOG_ENABLED_NOTIFICATIONS;
+constexpr const char* CSettings::SETTING_EVENTLOG_SHOW;
+constexpr const char* CSettings::SETTING_MASTERLOCK_LOCKCODE;
+constexpr const char* CSettings::SETTING_MASTERLOCK_STARTUPLOCK;
+constexpr const char* CSettings::SETTING_MASTERLOCK_MAXRETRIES;
+constexpr const char* CSettings::SETTING_CACHE_HARDDISK;
+constexpr const char* CSettings::SETTING_CACHEVIDEO_DVDROM;
+constexpr const char* CSettings::SETTING_CACHEVIDEO_LAN;
+constexpr const char* CSettings::SETTING_CACHEVIDEO_INTERNET;
+constexpr const char* CSettings::SETTING_CACHEAUDIO_DVDROM;
+constexpr const char* CSettings::SETTING_CACHEAUDIO_LAN;
+constexpr const char* CSettings::SETTING_CACHEAUDIO_INTERNET;
+constexpr const char* CSettings::SETTING_CACHEDVD_DVDROM;
+constexpr const char* CSettings::SETTING_CACHEDVD_LAN;
+constexpr const char* CSettings::SETTING_CACHEUNKNOWN_INTERNET;
+constexpr const char* CSettings::SETTING_SYSTEM_PLAYLISTSPATH;
+constexpr const char* CSettings::SETTING_ADDONS_AUTOUPDATES;
+constexpr const char* CSettings::SETTING_ADDONS_NOTIFICATIONS;
+constexpr const char* CSettings::SETTING_ADDONS_SHOW_RUNNING;
+constexpr const char* CSettings::SETTING_ADDONS_ALLOW_UNKNOWN_SOURCES;
+constexpr const char* CSettings::SETTING_ADDONS_UPDATEMODE;
+constexpr const char* CSettings::SETTING_ADDONS_MANAGE_DEPENDENCIES;
+constexpr const char* CSettings::SETTING_ADDONS_REMOVE_ORPHANED_DEPENDENCIES;
+constexpr const char* CSettings::SETTING_GENERAL_ADDONFOREIGNFILTER;
+constexpr const char* CSettings::SETTING_GENERAL_ADDONBROKENFILTER;
+constexpr const char* CSettings::SETTING_SOURCE_VIDEOS;
+constexpr const char* CSettings::SETTING_SOURCE_MUSIC;
+constexpr const char* CSettings::SETTING_SOURCE_PICTURES;
+//! @todo: remove in c++17
+
+bool CSettings::Initialize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (m_initialized)
+ return false;
+
+ // register custom setting types
+ InitializeSettingTypes();
+ // register custom setting controls
+ InitializeControls();
+
+ // option fillers and conditions need to be
+ // initialized before the setting definitions
+ InitializeOptionFillers();
+ InitializeConditions();
+
+ // load the settings definitions
+ if (!InitializeDefinitions())
+ return false;
+
+ GetSettingsManager()->SetInitialized();
+
+ InitializeISettingsHandlers();
+ InitializeISubSettings();
+ InitializeISettingCallbacks();
+
+ m_initialized = true;
+
+ return true;
+}
+
+void CSettings::RegisterSubSettings(ISubSettings* subSettings)
+{
+ if (subSettings == nullptr)
+ return;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_subSettings.insert(subSettings);
+}
+
+void CSettings::UnregisterSubSettings(ISubSettings* subSettings)
+{
+ if (subSettings == nullptr)
+ return;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_subSettings.erase(subSettings);
+}
+
+bool CSettings::Load()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ return Load(profileManager->GetSettingsFile());
+}
+
+bool CSettings::Load(const std::string &file)
+{
+ CXBMCTinyXML xmlDoc;
+ bool updated = false;
+ if (!XFILE::CFile::Exists(file) || !xmlDoc.LoadFile(file) ||
+ !Load(xmlDoc.RootElement(), updated))
+ {
+ CLog::Log(LOGERROR, "CSettings: unable to load settings from {}, creating new default settings",
+ file);
+ if (!Reset())
+ return false;
+
+ if (!Load(file))
+ return false;
+ }
+ // if the settings had to be updated, we need to save the changes
+ else if (updated)
+ return Save(file);
+
+ return true;
+}
+
+bool CSettings::Load(const TiXmlElement* root)
+{
+ bool updated = false;
+ return Load(root, updated);
+}
+
+bool CSettings::Save()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ return Save(profileManager->GetSettingsFile());
+}
+
+bool CSettings::Save(const std::string &file)
+{
+ CXBMCTinyXML xmlDoc;
+ if (!SaveValuesToXml(xmlDoc))
+ return false;
+
+ TiXmlElement* root = xmlDoc.RootElement();
+ if (root == nullptr)
+ return false;
+
+ if (!Save(root))
+ return false;
+
+ return xmlDoc.SaveFile(file);
+}
+
+bool CSettings::Save(TiXmlNode* root) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // save any ISubSettings implementations
+ for (const auto& subSetting : m_subSettings)
+ {
+ if (!subSetting->Save(root))
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettings::LoadSetting(const TiXmlNode *node, const std::string &settingId)
+{
+ return GetSettingsManager()->LoadSetting(node, settingId);
+}
+
+bool CSettings::GetBool(const std::string& id) const
+{
+ // Backward compatibility (skins use this setting)
+ if (StringUtils::EqualsNoCase(id, "lookandfeel.enablemouse"))
+ return CSettingsBase::GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE);
+
+ return CSettingsBase::GetBool(id);
+}
+
+void CSettings::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (!m_initialized)
+ return;
+
+ GetSettingsManager()->Clear();
+
+ for (auto& subSetting : m_subSettings)
+ subSetting->Clear();
+
+ m_initialized = false;
+}
+
+bool CSettings::Load(const TiXmlElement* root, bool& updated)
+{
+ if (root == nullptr)
+ return false;
+
+ if (!CSettingsBase::LoadValuesFromXml(root, updated))
+ return false;
+
+ return Load(static_cast<const TiXmlNode*>(root));
+}
+
+bool CSettings::Load(const TiXmlNode* settings)
+{
+ bool ok = true;
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ for (const auto& subSetting : m_subSettings)
+ ok &= subSetting->Load(settings);
+
+ return ok;
+}
+
+bool CSettings::Initialize(const std::string &file)
+{
+ CXBMCTinyXML xmlDoc;
+ if (!xmlDoc.LoadFile(file.c_str()))
+ {
+ CLog::Log(LOGERROR, "CSettings: error loading settings definition from {}, Line {}\n{}", file,
+ xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
+ return false;
+ }
+
+ CLog::Log(LOGDEBUG, "CSettings: loaded settings definition from {}", file);
+
+ return InitializeDefinitionsFromXml(xmlDoc);
+}
+
+bool CSettings::InitializeDefinitions()
+{
+ if (!Initialize(SETTINGS_XML_FOLDER "settings.xml"))
+ {
+ CLog::Log(LOGFATAL, "Unable to load settings definitions");
+ return false;
+ }
+#if defined(TARGET_WINDOWS)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "windows.xml") && !Initialize(SETTINGS_XML_FOLDER "windows.xml"))
+ CLog::Log(LOGFATAL, "Unable to load windows-specific settings definitions");
+#if defined(TARGET_WINDOWS_DESKTOP)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "win32.xml") && !Initialize(SETTINGS_XML_FOLDER "win32.xml"))
+ CLog::Log(LOGFATAL, "Unable to load win32-specific settings definitions");
+#elif defined(TARGET_WINDOWS_STORE)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "win10.xml") && !Initialize(SETTINGS_XML_FOLDER "win10.xml"))
+ CLog::Log(LOGFATAL, "Unable to load win10-specific settings definitions");
+#endif
+#elif defined(TARGET_ANDROID)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "android.xml") && !Initialize(SETTINGS_XML_FOLDER "android.xml"))
+ CLog::Log(LOGFATAL, "Unable to load android-specific settings definitions");
+#elif defined(TARGET_FREEBSD)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "freebsd.xml") && !Initialize(SETTINGS_XML_FOLDER "freebsd.xml"))
+ CLog::Log(LOGFATAL, "Unable to load freebsd-specific settings definitions");
+#elif defined(TARGET_LINUX)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "linux.xml") && !Initialize(SETTINGS_XML_FOLDER "linux.xml"))
+ CLog::Log(LOGFATAL, "Unable to load linux-specific settings definitions");
+#elif defined(TARGET_DARWIN)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "darwin.xml") && !Initialize(SETTINGS_XML_FOLDER "darwin.xml"))
+ CLog::Log(LOGFATAL, "Unable to load darwin-specific settings definitions");
+#if defined(TARGET_DARWIN_OSX)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "darwin_osx.xml") && !Initialize(SETTINGS_XML_FOLDER "darwin_osx.xml"))
+ CLog::Log(LOGFATAL, "Unable to load osx-specific settings definitions");
+#elif defined(TARGET_DARWIN_IOS)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "darwin_ios.xml") && !Initialize(SETTINGS_XML_FOLDER "darwin_ios.xml"))
+ CLog::Log(LOGFATAL, "Unable to load ios-specific settings definitions");
+#elif defined(TARGET_DARWIN_TVOS)
+ if (CFile::Exists(SETTINGS_XML_FOLDER "darwin_tvos.xml") &&
+ !Initialize(SETTINGS_XML_FOLDER "darwin_tvos.xml"))
+ CLog::Log(LOGFATAL, "Unable to load tvos-specific settings definitions");
+#endif
+#endif
+
+#if defined(PLATFORM_SETTINGS_FILE)
+ if (CFile::Exists(SETTINGS_XML_FOLDER DEF_TO_STR_VALUE(PLATFORM_SETTINGS_FILE)) && !Initialize(SETTINGS_XML_FOLDER DEF_TO_STR_VALUE(PLATFORM_SETTINGS_FILE)))
+ CLog::Log(LOGFATAL, "Unable to load platform-specific settings definitions ({})",
+ DEF_TO_STR_VALUE(PLATFORM_SETTINGS_FILE));
+#endif
+
+ // load any custom visibility and default values before loading the special
+ // appliance.xml so that appliances are able to overwrite even those values
+ InitializeVisibility();
+ InitializeDefaults();
+
+ if (CFile::Exists(SETTINGS_XML_FOLDER "appliance.xml") && !Initialize(SETTINGS_XML_FOLDER "appliance.xml"))
+ CLog::Log(LOGFATAL, "Unable to load appliance-specific settings definitions");
+
+ return true;
+}
+
+void CSettings::InitializeSettingTypes()
+{
+ GetSettingsManager()->RegisterSettingType("addon", this);
+ GetSettingsManager()->RegisterSettingType("date", this);
+ GetSettingsManager()->RegisterSettingType("path", this);
+ GetSettingsManager()->RegisterSettingType("time", this);
+}
+
+void CSettings::InitializeControls()
+{
+ GetSettingsManager()->RegisterSettingControl("toggle", this);
+ GetSettingsManager()->RegisterSettingControl("spinner", this);
+ GetSettingsManager()->RegisterSettingControl("edit", this);
+ GetSettingsManager()->RegisterSettingControl("button", this);
+ GetSettingsManager()->RegisterSettingControl("list", this);
+ GetSettingsManager()->RegisterSettingControl("slider", this);
+ GetSettingsManager()->RegisterSettingControl("range", this);
+ GetSettingsManager()->RegisterSettingControl("title", this);
+ GetSettingsManager()->RegisterSettingControl("colorbutton", this);
+}
+
+void CSettings::InitializeVisibility()
+{
+ // hide some settings if necessary
+#if defined(TARGET_DARWIN_EMBEDDED)
+ std::shared_ptr<CSettingString> timezonecountry = std::static_pointer_cast<CSettingString>(GetSettingsManager()->GetSetting(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY));
+ std::shared_ptr<CSettingString> timezone = std::static_pointer_cast<CSettingString>(GetSettingsManager()->GetSetting(CSettings::SETTING_LOCALE_TIMEZONE));
+
+ timezonecountry->SetRequirementsMet(false);
+ timezone->SetRequirementsMet(false);
+#endif
+}
+
+void CSettings::InitializeDefaults()
+{
+ // set some default values if necessary
+#if defined(TARGET_POSIX)
+ std::shared_ptr<CSettingString> timezonecountry = std::static_pointer_cast<CSettingString>(GetSettingsManager()->GetSetting(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY));
+ std::shared_ptr<CSettingString> timezone = std::static_pointer_cast<CSettingString>(GetSettingsManager()->GetSetting(CSettings::SETTING_LOCALE_TIMEZONE));
+
+ if (timezonecountry->IsVisible())
+ timezonecountry->SetDefault(g_timezone.GetCountryByTimezone(g_timezone.GetOSConfiguredTimezone()));
+ if (timezone->IsVisible())
+ timezone->SetDefault(g_timezone.GetOSConfiguredTimezone());
+#endif // defined(TARGET_POSIX)
+
+#if defined(TARGET_WINDOWS)
+ // We prefer a fake fullscreen mode (window covering the screen rather than dedicated fullscreen)
+ // as it works nicer with switching to other applications. However on some systems vsync is broken
+ // when we do this (eg non-Aero on ATI in particular) and on others (AppleTV) we can't get XBMC to
+ // the front
+ if (g_sysinfo.IsAeroDisabled())
+ {
+ auto setting = GetSettingsManager()->GetSetting(CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN);
+ if (!setting)
+ CLog::Log(LOGERROR, "Failed to load setting for: {}",
+ CSettings::SETTING_VIDEOSCREEN_FAKEFULLSCREEN);
+ else
+ std::static_pointer_cast<CSettingBool>(setting)->SetDefault(false);
+ }
+#endif
+
+ if (CServiceBroker::GetAppParams()->IsStandAlone())
+ {
+ auto setting =
+ GetSettingsManager()->GetSetting(CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE);
+ if (!setting)
+ CLog::Log(LOGERROR, "Failed to load setting for: {}",
+ CSettings::SETTING_POWERMANAGEMENT_SHUTDOWNSTATE);
+ else
+ std::static_pointer_cast<CSettingInt>(setting)->SetDefault(POWERSTATE_SHUTDOWN);
+ }
+
+ // Initialize deviceUUID if not already set, used in zeroconf advertisements.
+ std::shared_ptr<CSettingString> deviceUUID = std::static_pointer_cast<CSettingString>(GetSettingsManager()->GetSetting(CSettings::SETTING_SERVICES_DEVICEUUID));
+ if (deviceUUID->GetValue().empty())
+ {
+ const std::string& uuid = StringUtils::CreateUUID();
+ auto setting = GetSettingsManager()->GetSetting(CSettings::SETTING_SERVICES_DEVICEUUID);
+ if (!setting)
+ CLog::Log(LOGERROR, "Failed to load setting for: {}", CSettings::SETTING_SERVICES_DEVICEUUID);
+ else
+ std::static_pointer_cast<CSettingString>(setting)->SetValue(uuid);
+ }
+}
+
+void CSettings::InitializeOptionFillers()
+{
+ // register setting option fillers
+#ifdef HAS_DVD_DRIVE
+ GetSettingsManager()->RegisterSettingOptionsFiller("audiocdactions", MEDIA_DETECT::CAutorun::SettingOptionAudioCdActionsFiller);
+#endif
+ GetSettingsManager()->RegisterSettingOptionsFiller("charsets", CCharsetConverter::SettingOptionsCharsetsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("fonts", GUIFontManager::SettingOptionsFontsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller(
+ "subtitlesfonts", SUBTITLES::CSubtitlesSettings::SettingOptionsSubtitleFontsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("languagenames", CLangInfo::SettingOptionsLanguageNamesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("refreshchangedelays", CDisplaySettings::SettingOptionsRefreshChangeDelaysFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("refreshrates", CDisplaySettings::SettingOptionsRefreshRatesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("regions", CLangInfo::SettingOptionsRegionsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("shortdateformats", CLangInfo::SettingOptionsShortDateFormatsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("longdateformats", CLangInfo::SettingOptionsLongDateFormatsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("timeformats", CLangInfo::SettingOptionsTimeFormatsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("24hourclockformats", CLangInfo::SettingOptions24HourClockFormatsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("speedunits", CLangInfo::SettingOptionsSpeedUnitsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("temperatureunits", CLangInfo::SettingOptionsTemperatureUnitsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("rendermethods", CBaseRenderer::SettingOptionsRenderMethodsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("modes", CDisplaySettings::SettingOptionsModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("resolutions", CDisplaySettings::SettingOptionsResolutionsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("screens", CDisplaySettings::SettingOptionsDispModeFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("stereoscopicmodes", CDisplaySettings::SettingOptionsStereoscopicModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("preferedstereoscopicviewmodes", CDisplaySettings::SettingOptionsPreferredStereoscopicViewModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("monitors", CDisplaySettings::SettingOptionsMonitorsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("cmsmodes", CDisplaySettings::SettingOptionsCmsModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("cmswhitepoints", CDisplaySettings::SettingOptionsCmsWhitepointsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("cmsprimaries", CDisplaySettings::SettingOptionsCmsPrimariesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("cmsgammamodes", CDisplaySettings::SettingOptionsCmsGammaModesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("videoseeksteps", CSeekHandler::SettingOptionsSeekStepsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("startupwindows", ADDON::CSkinInfo::SettingOptionsStartupWindowsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("audiostreamlanguages", CLangInfo::SettingOptionsAudioStreamLanguagesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("subtitlestreamlanguages", CLangInfo::SettingOptionsSubtitleStreamLanguagesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("subtitledownloadlanguages", CLangInfo::SettingOptionsSubtitleDownloadlanguagesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("iso6391languages", CLangInfo::SettingOptionsISO6391LanguagesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("skincolors", ADDON::CSkinInfo::SettingOptionsSkinColorsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("skinfonts", ADDON::CSkinInfo::SettingOptionsSkinFontsFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("skinthemes", ADDON::CSkinInfo::SettingOptionsSkinThemesFiller);
+#ifdef TARGET_LINUX
+ GetSettingsManager()->RegisterSettingOptionsFiller("timezonecountries", CPosixTimezone::SettingOptionsTimezoneCountriesFiller);
+ GetSettingsManager()->RegisterSettingOptionsFiller("timezones", CPosixTimezone::SettingOptionsTimezonesFiller);
+#endif
+ GetSettingsManager()->RegisterSettingOptionsFiller("keyboardlayouts", CKeyboardLayoutManager::SettingOptionsKeyboardLayoutsFiller);
+}
+
+void CSettings::UninitializeOptionFillers()
+{
+ GetSettingsManager()->UnregisterSettingOptionsFiller("audiocdactions");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("audiocdencoders");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("charsets");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("fontheights");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("fonts");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("subtitlesfonts");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("languagenames");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("refreshchangedelays");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("refreshrates");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("regions");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("shortdateformats");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("longdateformats");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("timeformats");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("24hourclockformats");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("speedunits");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("temperatureunits");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("rendermethods");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("resolutions");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("screens");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("stereoscopicmodes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("preferedstereoscopicviewmodes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("monitors");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("cmsmodes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("cmswhitepoints");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("cmsprimaries");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("cmsgammamodes");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("videoseeksteps");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("shutdownstates");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("startupwindows");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("audiostreamlanguages");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("subtitlestreamlanguages");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("subtitledownloadlanguages");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("iso6391languages");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("skincolors");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("skinfonts");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("skinthemes");
+#if defined(TARGET_LINUX)
+ GetSettingsManager()->UnregisterSettingOptionsFiller("timezonecountries");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("timezones");
+#endif // defined(TARGET_LINUX)
+ GetSettingsManager()->UnregisterSettingOptionsFiller("verticalsyncs");
+ GetSettingsManager()->UnregisterSettingOptionsFiller("keyboardlayouts");
+}
+
+void CSettings::InitializeConditions()
+{
+ CSettingConditions::Initialize();
+
+ // add basic conditions
+ const std::set<std::string> &simpleConditions = CSettingConditions::GetSimpleConditions();
+ for (std::set<std::string>::const_iterator itCondition = simpleConditions.begin(); itCondition != simpleConditions.end(); ++itCondition)
+ GetSettingsManager()->AddCondition(*itCondition);
+
+ // add more complex conditions
+ const std::map<std::string, SettingConditionCheck> &complexConditions = CSettingConditions::GetComplexConditions();
+ for (std::map<std::string, SettingConditionCheck>::const_iterator itCondition = complexConditions.begin(); itCondition != complexConditions.end(); ++itCondition)
+ GetSettingsManager()->AddDynamicCondition(itCondition->first, itCondition->second);
+}
+
+void CSettings::UninitializeConditions()
+{
+ CSettingConditions::Deinitialize();
+}
+
+void CSettings::InitializeISettingsHandlers()
+{
+ // register ISettingsHandler implementations
+ // The order of these matters! Handlers are processed in the order they were registered.
+ GetSettingsManager()->RegisterSettingsHandler(&CMediaSourceSettings::GetInstance());
+#ifdef HAS_UPNP
+ GetSettingsManager()->RegisterSettingsHandler(&CUPnPSettings::GetInstance());
+#endif
+ GetSettingsManager()->RegisterSettingsHandler(&CWakeOnAccess::GetInstance());
+ GetSettingsManager()->RegisterSettingsHandler(&CRssManager::GetInstance());
+ GetSettingsManager()->RegisterSettingsHandler(&g_langInfo);
+#if defined(TARGET_LINUX) && !defined(TARGET_ANDROID) && !defined(__UCLIBC__)
+ GetSettingsManager()->RegisterSettingsHandler(&g_timezone);
+#endif
+ GetSettingsManager()->RegisterSettingsHandler(&CMediaSettings::GetInstance());
+}
+
+void CSettings::UninitializeISettingsHandlers()
+{
+ // unregister ISettingsHandler implementations
+ GetSettingsManager()->UnregisterSettingsHandler(&CMediaSettings::GetInstance());
+#if defined(TARGET_LINUX)
+ GetSettingsManager()->UnregisterSettingsHandler(&g_timezone);
+#endif // defined(TARGET_LINUX)
+ GetSettingsManager()->UnregisterSettingsHandler(&g_langInfo);
+ GetSettingsManager()->UnregisterSettingsHandler(&CRssManager::GetInstance());
+ GetSettingsManager()->UnregisterSettingsHandler(&CWakeOnAccess::GetInstance());
+#ifdef HAS_UPNP
+ GetSettingsManager()->UnregisterSettingsHandler(&CUPnPSettings::GetInstance());
+#endif
+ GetSettingsManager()->UnregisterSettingsHandler(&CMediaSourceSettings::GetInstance());
+}
+
+void CSettings::InitializeISubSettings()
+{
+ // register ISubSettings implementations
+ RegisterSubSettings(&CDisplaySettings::GetInstance());
+ RegisterSubSettings(&CMediaSettings::GetInstance());
+ RegisterSubSettings(&CSkinSettings::GetInstance());
+ RegisterSubSettings(&g_sysinfo);
+ RegisterSubSettings(&CViewStateSettings::GetInstance());
+}
+
+void CSettings::UninitializeISubSettings()
+{
+ // unregister ISubSettings implementations
+ UnregisterSubSettings(&CDisplaySettings::GetInstance());
+ UnregisterSubSettings(&CMediaSettings::GetInstance());
+ UnregisterSubSettings(&CSkinSettings::GetInstance());
+ UnregisterSubSettings(&g_sysinfo);
+ UnregisterSubSettings(&CViewStateSettings::GetInstance());
+}
+
+void CSettings::InitializeISettingCallbacks()
+{
+ // register any ISettingCallback implementations
+ std::set<std::string> settingSet;
+ settingSet.insert(CSettings::SETTING_MUSICLIBRARY_CLEANUP);
+ settingSet.insert(CSettings::SETTING_MUSICLIBRARY_EXPORT);
+ settingSet.insert(CSettings::SETTING_MUSICLIBRARY_IMPORT);
+ settingSet.insert(CSettings::SETTING_MUSICFILES_TRACKFORMAT);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_FLATTENTVSHOWS);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_GROUPMOVIESETS);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_CLEANUP);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_IMPORT);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_EXPORT);
+ settingSet.insert(CSettings::SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS);
+ GetSettingsManager()->RegisterCallback(&CMediaSettings::GetInstance(), settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_SCREEN);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_RESOLUTION);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_SCREENMODE);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_MONITOR);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_3DLUT);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_DISPLAYPROFILE);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_BLANKDISPLAYS);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_WHITELIST);
+ settingSet.insert(CSettings::SETTING_VIDEOSCREEN_10BITSURFACES);
+ GetSettingsManager()->RegisterCallback(&CDisplaySettings::GetInstance(), settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_SUBTITLES_CHARSET);
+ settingSet.insert(CSettings::SETTING_LOCALE_CHARSET);
+ GetSettingsManager()->RegisterCallback(&g_charsetConverter, settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_LOCALE_AUDIOLANGUAGE);
+ settingSet.insert(CSettings::SETTING_LOCALE_SUBTITLELANGUAGE);
+ settingSet.insert(CSettings::SETTING_LOCALE_LANGUAGE);
+ settingSet.insert(CSettings::SETTING_LOCALE_COUNTRY);
+ settingSet.insert(CSettings::SETTING_LOCALE_SHORTDATEFORMAT);
+ settingSet.insert(CSettings::SETTING_LOCALE_LONGDATEFORMAT);
+ settingSet.insert(CSettings::SETTING_LOCALE_TIMEFORMAT);
+ settingSet.insert(CSettings::SETTING_LOCALE_USE24HOURCLOCK);
+ settingSet.insert(CSettings::SETTING_LOCALE_TEMPERATUREUNIT);
+ settingSet.insert(CSettings::SETTING_LOCALE_SPEEDUNIT);
+ GetSettingsManager()->RegisterCallback(&g_langInfo, settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_MASTERLOCK_LOCKCODE);
+ GetSettingsManager()->RegisterCallback(&g_passwordManager, settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_LOOKANDFEEL_RSSEDIT);
+ GetSettingsManager()->RegisterCallback(&CRssManager::GetInstance(), settingSet);
+
+#if defined(TARGET_LINUX)
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_LOCALE_TIMEZONE);
+ settingSet.insert(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY);
+ GetSettingsManager()->RegisterCallback(&g_timezone, settingSet);
+#endif
+
+#if defined(TARGET_DARWIN_OSX)
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_INPUT_APPLEREMOTEMODE);
+ settingSet.insert(CSettings::SETTING_INPUT_APPLEREMOTEALWAYSON);
+ GetSettingsManager()->RegisterCallback(&XBMCHelper::GetInstance(), settingSet);
+#endif
+
+#if defined(TARGET_DARWIN_TVOS)
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_INPUT_SIRIREMOTEIDLETIMERENABLED);
+ settingSet.insert(CSettings::SETTING_INPUT_SIRIREMOTEIDLETIME);
+ settingSet.insert(CSettings::SETTING_INPUT_SIRIREMOTEHORIZONTALSENSITIVITY);
+ settingSet.insert(CSettings::SETTING_INPUT_SIRIREMOTEVERTICALSENSITIVITY);
+ GetSettingsManager()->RegisterCallback(&CTVOSInputSettings::GetInstance(), settingSet);
+#endif
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_ADDONS_SHOW_RUNNING);
+ settingSet.insert(CSettings::SETTING_ADDONS_MANAGE_DEPENDENCIES);
+ settingSet.insert(CSettings::SETTING_ADDONS_REMOVE_ORPHANED_DEPENDENCIES);
+ settingSet.insert(CSettings::SETTING_ADDONS_ALLOW_UNKNOWN_SOURCES);
+ GetSettingsManager()->RegisterCallback(&ADDON::CAddonSystemSettings::GetInstance(), settingSet);
+
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_POWERMANAGEMENT_WAKEONACCESS);
+ GetSettingsManager()->RegisterCallback(&CWakeOnAccess::GetInstance(), settingSet);
+
+#ifdef HAVE_LIBBLURAY
+ settingSet.clear();
+ settingSet.insert(CSettings::SETTING_DISC_PLAYBACK);
+ GetSettingsManager()->RegisterCallback(&CDiscSettings::GetInstance(), settingSet);
+#endif
+}
+
+void CSettings::UninitializeISettingCallbacks()
+{
+ GetSettingsManager()->UnregisterCallback(&CMediaSettings::GetInstance());
+ GetSettingsManager()->UnregisterCallback(&CDisplaySettings::GetInstance());
+ GetSettingsManager()->UnregisterCallback(&g_charsetConverter);
+ GetSettingsManager()->UnregisterCallback(&g_langInfo);
+ GetSettingsManager()->UnregisterCallback(&g_passwordManager);
+ GetSettingsManager()->UnregisterCallback(&CRssManager::GetInstance());
+#if defined(TARGET_LINUX)
+ GetSettingsManager()->UnregisterCallback(&g_timezone);
+#endif // defined(TARGET_LINUX)
+#if defined(TARGET_DARWIN_OSX)
+ GetSettingsManager()->UnregisterCallback(&XBMCHelper::GetInstance());
+#endif
+ GetSettingsManager()->UnregisterCallback(&CWakeOnAccess::GetInstance());
+#ifdef HAVE_LIBBLURAY
+ GetSettingsManager()->UnregisterCallback(&CDiscSettings::GetInstance());
+#endif
+}
+
+bool CSettings::Reset()
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ const std::string settingsFile = profileManager->GetSettingsFile();
+
+ // try to delete the settings file
+ if (XFILE::CFile::Exists(settingsFile, false) && !XFILE::CFile::Delete(settingsFile))
+ CLog::Log(LOGWARNING, "Unable to delete old settings file at {}", settingsFile);
+
+ // unload any loaded settings
+ Unload();
+
+ // try to save the default settings
+ if (!Save())
+ {
+ CLog::Log(LOGWARNING, "Failed to save the default settings to {}", settingsFile);
+ return false;
+ }
+
+ return true;
+}
diff --git a/xbmc/settings/Settings.h b/xbmc/settings/Settings.h
new file mode 100644
index 0000000..cdd2d31
--- /dev/null
+++ b/xbmc/settings/Settings.h
@@ -0,0 +1,598 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "settings/ISubSettings.h"
+#include "settings/SettingControl.h"
+#include "settings/SettingCreator.h"
+#include "settings/SettingsBase.h"
+
+#include <string>
+
+class CSettingList;
+class TiXmlNode;
+
+/*!
+ \brief Wrapper around CSettingsManager responsible for properly setting up
+ the settings manager and registering all the callbacks, handlers and custom
+ setting types.
+ \sa CSettingsManager
+ */
+class CSettings : public CSettingsBase, public CSettingCreator, public CSettingControlCreator
+ , private ISubSettings
+{
+public:
+ static constexpr auto SETTING_LOOKANDFEEL_SKIN = "lookandfeel.skin";
+ static constexpr auto SETTING_LOOKANDFEEL_SKINSETTINGS = "lookandfeel.skinsettings";
+ static constexpr auto SETTING_LOOKANDFEEL_SKINTHEME = "lookandfeel.skintheme";
+ static constexpr auto SETTING_LOOKANDFEEL_SKINCOLORS = "lookandfeel.skincolors";
+ static constexpr auto SETTING_LOOKANDFEEL_FONT = "lookandfeel.font";
+ static constexpr auto SETTING_LOOKANDFEEL_SKINZOOM = "lookandfeel.skinzoom";
+ static constexpr auto SETTING_LOOKANDFEEL_STARTUPACTION = "lookandfeel.startupaction";
+ static constexpr auto SETTING_LOOKANDFEEL_STARTUPWINDOW = "lookandfeel.startupwindow";
+ static constexpr auto SETTING_LOOKANDFEEL_SOUNDSKIN = "lookandfeel.soundskin";
+ static constexpr auto SETTING_LOOKANDFEEL_ENABLERSSFEEDS = "lookandfeel.enablerssfeeds";
+ static constexpr auto SETTING_LOOKANDFEEL_RSSEDIT = "lookandfeel.rssedit";
+ static constexpr auto SETTING_LOOKANDFEEL_STEREOSTRENGTH = "lookandfeel.stereostrength";
+ static constexpr auto SETTING_LOCALE_LANGUAGE = "locale.language";
+ static constexpr auto SETTING_LOCALE_COUNTRY = "locale.country";
+ static constexpr auto SETTING_LOCALE_CHARSET = "locale.charset";
+ static constexpr auto SETTING_LOCALE_KEYBOARDLAYOUTS = "locale.keyboardlayouts";
+ static constexpr auto SETTING_LOCALE_ACTIVEKEYBOARDLAYOUT = "locale.activekeyboardlayout";
+ static constexpr auto SETTING_LOCALE_TIMEZONECOUNTRY = "locale.timezonecountry";
+ static constexpr auto SETTING_LOCALE_TIMEZONE = "locale.timezone";
+ static constexpr auto SETTING_LOCALE_SHORTDATEFORMAT = "locale.shortdateformat";
+ static constexpr auto SETTING_LOCALE_LONGDATEFORMAT = "locale.longdateformat";
+ static constexpr auto SETTING_LOCALE_TIMEFORMAT = "locale.timeformat";
+ static constexpr auto SETTING_LOCALE_USE24HOURCLOCK = "locale.use24hourclock";
+ static constexpr auto SETTING_LOCALE_TEMPERATUREUNIT = "locale.temperatureunit";
+ static constexpr auto SETTING_LOCALE_SPEEDUNIT = "locale.speedunit";
+ static constexpr auto SETTING_FILELISTS_SHOWPARENTDIRITEMS = "filelists.showparentdiritems";
+ static constexpr auto SETTING_FILELISTS_SHOWEXTENSIONS = "filelists.showextensions";
+ static constexpr auto SETTING_FILELISTS_IGNORETHEWHENSORTING = "filelists.ignorethewhensorting";
+ static constexpr auto SETTING_FILELISTS_ALLOWFILEDELETION = "filelists.allowfiledeletion";
+ static constexpr auto SETTING_FILELISTS_SHOWADDSOURCEBUTTONS = "filelists.showaddsourcebuttons";
+ static constexpr auto SETTING_FILELISTS_SHOWHIDDEN = "filelists.showhidden";
+ static constexpr auto SETTING_SCREENSAVER_MODE = "screensaver.mode";
+ static constexpr auto SETTING_SCREENSAVER_SETTINGS = "screensaver.settings";
+ static constexpr auto SETTING_SCREENSAVER_PREVIEW = "screensaver.preview";
+ static constexpr auto SETTING_SCREENSAVER_TIME = "screensaver.time";
+ static constexpr auto SETTING_SCREENSAVER_USEMUSICVISINSTEAD = "screensaver.usemusicvisinstead";
+ static constexpr auto SETTING_SCREENSAVER_USEDIMONPAUSE = "screensaver.usedimonpause";
+ static constexpr auto SETTING_WINDOW_WIDTH = "window.width";
+ static constexpr auto SETTING_WINDOW_HEIGHT = "window.height";
+ static constexpr auto SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS = "videolibrary.showunwatchedplots";
+ static constexpr auto SETTING_VIDEOLIBRARY_ACTORTHUMBS = "videolibrary.actorthumbs";
+ static constexpr auto SETTING_MYVIDEOS_FLATTEN = "myvideos.flatten";
+ static constexpr auto SETTING_VIDEOLIBRARY_FLATTENTVSHOWS = "videolibrary.flattentvshows";
+ static constexpr auto SETTING_VIDEOLIBRARY_TVSHOWSSELECTFIRSTUNWATCHEDITEM =
+ "videolibrary.tvshowsselectfirstunwatcheditem";
+ static constexpr auto SETTING_VIDEOLIBRARY_TVSHOWSINCLUDEALLSEASONSANDSPECIALS =
+ "videolibrary.tvshowsincludeallseasonsandspecials";
+ static constexpr auto SETTING_VIDEOLIBRARY_SHOWALLITEMS = "videolibrary.showallitems";
+ static constexpr auto SETTING_VIDEOLIBRARY_GROUPMOVIESETS = "videolibrary.groupmoviesets";
+ static constexpr auto SETTING_VIDEOLIBRARY_GROUPSINGLEITEMSETS =
+ "videolibrary.groupsingleitemsets";
+ static constexpr auto SETTING_VIDEOLIBRARY_UPDATEONSTARTUP = "videolibrary.updateonstartup";
+ static constexpr auto SETTING_VIDEOLIBRARY_BACKGROUNDUPDATE = "videolibrary.backgroundupdate";
+ static constexpr auto SETTING_VIDEOLIBRARY_CLEANUP = "videolibrary.cleanup";
+ static constexpr auto SETTING_VIDEOLIBRARY_EXPORT = "videolibrary.export";
+ static constexpr auto SETTING_VIDEOLIBRARY_IMPORT = "videolibrary.import";
+ static constexpr auto SETTING_VIDEOLIBRARY_SHOWEMPTYTVSHOWS = "videolibrary.showemptytvshows";
+ static constexpr auto SETTING_VIDEOLIBRARY_MOVIESETSFOLDER = "videolibrary.moviesetsfolder";
+ static constexpr auto SETTING_VIDEOLIBRARY_ARTWORK_LEVEL = "videolibrary.artworklevel";
+ static constexpr auto SETTING_VIDEOLIBRARY_MOVIEART_WHITELIST = "videolibrary.movieartwhitelist";
+ static constexpr auto SETTING_VIDEOLIBRARY_TVSHOWART_WHITELIST =
+ "videolibrary.tvshowartwhitelist";
+ static constexpr auto SETTING_VIDEOLIBRARY_EPISODEART_WHITELIST =
+ "videolibrary.episodeartwhitelist";
+ static constexpr auto SETTING_VIDEOLIBRARY_MUSICVIDEOART_WHITELIST =
+ "videolibrary.musicvideoartwhitelist";
+ static constexpr auto SETTING_VIDEOLIBRARY_ARTSETTINGS_UPDATED =
+ "videolibrary.artsettingsupdated";
+ static constexpr auto SETTING_VIDEOLIBRARY_SHOWPERFORMERS =
+ "videolibrary.musicvideosallperformers";
+ static constexpr auto SETTING_LOCALE_AUDIOLANGUAGE = "locale.audiolanguage";
+ static constexpr auto SETTING_VIDEOPLAYER_PREFERDEFAULTFLAG = "videoplayer.preferdefaultflag";
+ static constexpr auto SETTING_VIDEOPLAYER_AUTOPLAYNEXTITEM = "videoplayer.autoplaynextitem";
+ static constexpr auto SETTING_VIDEOPLAYER_SEEKSTEPS = "videoplayer.seeksteps";
+ static constexpr auto SETTING_VIDEOPLAYER_SEEKDELAY = "videoplayer.seekdelay";
+ static constexpr auto SETTING_VIDEOPLAYER_ADJUSTREFRESHRATE = "videoplayer.adjustrefreshrate";
+ static constexpr auto SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK = "videoplayer.usedisplayasclock";
+ static constexpr auto SETTING_VIDEOPLAYER_ERRORINASPECT = "videoplayer.errorinaspect";
+ static constexpr auto SETTING_VIDEOPLAYER_STRETCH43 = "videoplayer.stretch43";
+ static constexpr auto SETTING_VIDEOPLAYER_TELETEXTENABLED = "videoplayer.teletextenabled";
+ static constexpr auto SETTING_VIDEOPLAYER_TELETEXTSCALE = "videoplayer.teletextscale";
+ static constexpr auto SETTING_VIDEOPLAYER_STEREOSCOPICPLAYBACKMODE =
+ "videoplayer.stereoscopicplaybackmode";
+ static constexpr auto SETTING_VIDEOPLAYER_QUITSTEREOMODEONSTOP =
+ "videoplayer.quitstereomodeonstop";
+ static constexpr auto SETTING_VIDEOPLAYER_RENDERMETHOD = "videoplayer.rendermethod";
+ static constexpr auto SETTING_VIDEOPLAYER_HQSCALERS = "videoplayer.hqscalers";
+ static constexpr auto SETTING_VIDEOPLAYER_USEMEDIACODEC = "videoplayer.usemediacodec";
+ static constexpr auto SETTING_VIDEOPLAYER_USEMEDIACODECSURFACE =
+ "videoplayer.usemediacodecsurface";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVDPAU = "videoplayer.usevdpau";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVDPAUMIXER = "videoplayer.usevdpaumixer";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVDPAUMPEG2 = "videoplayer.usevdpaumpeg2";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVDPAUMPEG4 = "videoplayer.usevdpaumpeg4";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVDPAUVC1 = "videoplayer.usevdpauvc1";
+ static constexpr auto SETTING_VIDEOPLAYER_USEDXVA2 = "videoplayer.usedxva2";
+ static constexpr auto SETTING_VIDEOPLAYER_USEVTB = "videoplayer.usevtb";
+ static constexpr auto SETTING_VIDEOPLAYER_USEPRIMEDECODER = "videoplayer.useprimedecoder";
+ static constexpr auto SETTING_VIDEOPLAYER_USESTAGEFRIGHT = "videoplayer.usestagefright";
+ static constexpr auto SETTING_VIDEOPLAYER_LIMITGUIUPDATE = "videoplayer.limitguiupdate";
+ static constexpr auto SETTING_VIDEOPLAYER_SUPPORTMVC = "videoplayer.supportmvc";
+ static constexpr auto SETTING_MYVIDEOS_SELECTACTION = "myvideos.selectaction";
+ static constexpr auto SETTING_MYVIDEOS_USETAGS = "myvideos.usetags";
+ static constexpr auto SETTING_MYVIDEOS_EXTRACTFLAGS = "myvideos.extractflags";
+ static constexpr auto SETTING_MYVIDEOS_EXTRACTCHAPTERTHUMBS = "myvideos.extractchapterthumbs";
+ static constexpr auto SETTING_MYVIDEOS_REPLACELABELS = "myvideos.replacelabels";
+ static constexpr auto SETTING_MYVIDEOS_EXTRACTTHUMB = "myvideos.extractthumb";
+ static constexpr auto SETTING_MYVIDEOS_STACKVIDEOS = "myvideos.stackvideos";
+ static constexpr auto SETTING_LOCALE_SUBTITLELANGUAGE = "locale.subtitlelanguage";
+ static constexpr auto SETTING_SUBTITLES_PARSECAPTIONS = "subtitles.parsecaptions";
+ static constexpr auto SETTING_SUBTITLES_CAPTIONSALIGN = "subtitles.captionsalign";
+ static constexpr auto SETTING_SUBTITLES_ALIGN = "subtitles.align";
+ static constexpr auto SETTING_SUBTITLES_STEREOSCOPICDEPTH = "subtitles.stereoscopicdepth";
+ static constexpr auto SETTING_SUBTITLES_FONTNAME = "subtitles.fontname";
+ static constexpr auto SETTING_SUBTITLES_FONTSIZE = "subtitles.fontsize";
+ static constexpr auto SETTING_SUBTITLES_STYLE = "subtitles.style";
+ static constexpr auto SETTING_SUBTITLES_COLOR = "subtitles.colorpick";
+ static constexpr auto SETTING_SUBTITLES_BORDERSIZE = "subtitles.bordersize";
+ static constexpr auto SETTING_SUBTITLES_BORDERCOLOR = "subtitles.bordercolorpick";
+ static constexpr auto SETTING_SUBTITLES_OPACITY = "subtitles.opacity";
+ static constexpr auto SETTING_SUBTITLES_BLUR = "subtitles.blur";
+ static constexpr auto SETTING_SUBTITLES_BACKGROUNDTYPE = "subtitles.backgroundtype";
+ static constexpr auto SETTING_SUBTITLES_SHADOWCOLOR = "subtitles.shadowcolor";
+ static constexpr auto SETTING_SUBTITLES_SHADOWOPACITY = "subtitles.shadowopacity";
+ static constexpr auto SETTING_SUBTITLES_SHADOWSIZE = "subtitles.shadowsize";
+ static constexpr auto SETTING_SUBTITLES_BGCOLOR = "subtitles.bgcolorpick";
+ static constexpr auto SETTING_SUBTITLES_BGOPACITY = "subtitles.bgopacity";
+ static constexpr auto SETTING_SUBTITLES_MARGINVERTICAL = "subtitles.marginvertical";
+ static constexpr auto SETTING_SUBTITLES_CHARSET = "subtitles.charset";
+ static constexpr auto SETTING_SUBTITLES_OVERRIDEFONTS = "subtitles.overridefonts";
+ static constexpr auto SETTING_SUBTITLES_OVERRIDESTYLES = "subtitles.overridestyles";
+ static constexpr auto SETTING_SUBTITLES_LANGUAGES = "subtitles.languages";
+ static constexpr auto SETTING_SUBTITLES_STORAGEMODE = "subtitles.storagemode";
+ static constexpr auto SETTING_SUBTITLES_CUSTOMPATH = "subtitles.custompath";
+ static constexpr auto SETTING_SUBTITLES_PAUSEONSEARCH = "subtitles.pauseonsearch";
+ static constexpr auto SETTING_SUBTITLES_DOWNLOADFIRST = "subtitles.downloadfirst";
+ static constexpr auto SETTING_SUBTITLES_TV = "subtitles.tv";
+ static constexpr auto SETTING_SUBTITLES_MOVIE = "subtitles.movie";
+ static constexpr auto SETTING_DVDS_AUTORUN = "dvds.autorun";
+ static constexpr auto SETTING_DVDS_PLAYERREGION = "dvds.playerregion";
+ static constexpr auto SETTING_DVDS_AUTOMENU = "dvds.automenu";
+ static constexpr auto SETTING_DISC_PLAYBACK = "disc.playback";
+ static constexpr auto SETTING_BLURAY_PLAYERREGION = "bluray.playerregion";
+ static constexpr auto SETTING_ACCESSIBILITY_AUDIOVISUAL = "accessibility.audiovisual";
+ static constexpr auto SETTING_ACCESSIBILITY_AUDIOHEARING = "accessibility.audiohearing";
+ static constexpr auto SETTING_ACCESSIBILITY_SUBHEARING = "accessibility.subhearing";
+ static constexpr auto SETTING_SCRAPERS_MOVIESDEFAULT = "scrapers.moviesdefault";
+ static constexpr auto SETTING_SCRAPERS_TVSHOWSDEFAULT = "scrapers.tvshowsdefault";
+ static constexpr auto SETTING_SCRAPERS_MUSICVIDEOSDEFAULT = "scrapers.musicvideosdefault";
+ static constexpr auto SETTING_PVRMANAGER_PRESELECTPLAYINGCHANNEL =
+ "pvrmanager.preselectplayingchannel";
+ static constexpr auto SETTING_PVRMANAGER_SYNCCHANNELGROUPS = "pvrmanager.syncchannelgroups";
+ static constexpr auto SETTING_PVRMANAGER_BACKENDCHANNELORDER = "pvrmanager.backendchannelorder";
+ static constexpr auto SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS =
+ "pvrmanager.usebackendchannelnumbers";
+ static constexpr auto SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERSALWAYS =
+ "pvrmanager.usebackendchannelnumbersalways";
+ static constexpr auto SETTING_PVRMANAGER_STARTGROUPCHANNELNUMBERSFROMONE =
+ "pvrmanager.startgroupchannelnumbersfromone";
+ static constexpr auto SETTING_PVRMANAGER_CLIENTPRIORITIES = "pvrmanager.clientpriorities";
+ static constexpr auto SETTING_PVRMANAGER_CHANNELMANAGER = "pvrmanager.channelmanager";
+ static constexpr auto SETTING_PVRMANAGER_GROUPMANAGER = "pvrmanager.groupmanager";
+ static constexpr auto SETTING_PVRMANAGER_CHANNELSCAN = "pvrmanager.channelscan";
+ static constexpr auto SETTING_PVRMANAGER_RESETDB = "pvrmanager.resetdb";
+ static constexpr auto SETTING_PVRMANAGER_ADDONS = "pvrmanager.addons";
+ static constexpr auto SETTING_PVRMENU_DISPLAYCHANNELINFO = "pvrmenu.displaychannelinfo";
+ static constexpr auto SETTING_PVRMENU_CLOSECHANNELOSDONSWITCH = "pvrmenu.closechannelosdonswitch";
+ static constexpr auto SETTING_PVRMENU_ICONPATH = "pvrmenu.iconpath";
+ static constexpr auto SETTING_PVRMENU_SEARCHICONS = "pvrmenu.searchicons";
+ static constexpr auto SETTING_EPG_PAST_DAYSTODISPLAY = "epg.pastdaystodisplay";
+ static constexpr auto SETTING_EPG_FUTURE_DAYSTODISPLAY = "epg.futuredaystodisplay";
+ static constexpr auto SETTING_EPG_SELECTACTION = "epg.selectaction";
+ static constexpr auto SETTING_EPG_HIDENOINFOAVAILABLE = "epg.hidenoinfoavailable";
+ static constexpr auto SETTING_EPG_EPGUPDATE = "epg.epgupdate";
+ static constexpr auto SETTING_EPG_PREVENTUPDATESWHILEPLAYINGTV =
+ "epg.preventupdateswhileplayingtv";
+ static constexpr auto SETTING_EPG_RESETEPG = "epg.resetepg";
+ static constexpr auto SETTING_PVRPLAYBACK_SWITCHTOFULLSCREENCHANNELTYPES =
+ "pvrplayback.switchtofullscreenchanneltypes";
+ static constexpr auto SETTING_PVRPLAYBACK_SIGNALQUALITY = "pvrplayback.signalquality";
+ static constexpr auto SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH =
+ "pvrplayback.confirmchannelswitch";
+ static constexpr auto SETTING_PVRPLAYBACK_CHANNELENTRYTIMEOUT = "pvrplayback.channelentrytimeout";
+ static constexpr auto SETTING_PVRPLAYBACK_DELAYMARKLASTWATCHED =
+ "pvrplayback.delaymarklastwatched";
+ static constexpr auto SETTING_PVRPLAYBACK_FPS = "pvrplayback.fps";
+ static constexpr auto SETTING_PVRRECORD_INSTANTRECORDACTION = "pvrrecord.instantrecordaction";
+ static constexpr auto SETTING_PVRRECORD_INSTANTRECORDTIME = "pvrrecord.instantrecordtime";
+ static constexpr auto SETTING_PVRRECORD_MARGINSTART = "pvrrecord.marginstart";
+ static constexpr auto SETTING_PVRRECORD_MARGINEND = "pvrrecord.marginend";
+ static constexpr auto SETTING_PVRRECORD_TIMERNOTIFICATIONS = "pvrrecord.timernotifications";
+ static constexpr auto SETTING_PVRRECORD_GROUPRECORDINGS = "pvrrecord.grouprecordings";
+ static constexpr auto SETTING_PVRREMINDERS_AUTOCLOSEDELAY = "pvrreminders.autoclosedelay";
+ static constexpr auto SETTING_PVRREMINDERS_AUTORECORD = "pvrreminders.autorecord";
+ static constexpr auto SETTING_PVRREMINDERS_AUTOSWITCH = "pvrreminders.autoswitch";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_ENABLED = "pvrpowermanagement.enabled";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME =
+ "pvrpowermanagement.backendidletime";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_SETWAKEUPCMD = "pvrpowermanagement.setwakeupcmd";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_PREWAKEUP = "pvrpowermanagement.prewakeup";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUP = "pvrpowermanagement.dailywakeup";
+ static constexpr auto SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME =
+ "pvrpowermanagement.dailywakeuptime";
+ static constexpr auto SETTING_PVRPARENTAL_ENABLED = "pvrparental.enabled";
+ static constexpr auto SETTING_PVRPARENTAL_PIN = "pvrparental.pin";
+ static constexpr auto SETTING_PVRPARENTAL_DURATION = "pvrparental.duration";
+ static constexpr auto SETTING_PVRCLIENT_MENUHOOK = "pvrclient.menuhook";
+ static constexpr auto SETTING_PVRTIMERS_HIDEDISABLEDTIMERS = "pvrtimers.hidedisabledtimers";
+ static constexpr auto SETTING_MUSICLIBRARY_SHOWCOMPILATIONARTISTS =
+ "musiclibrary.showcompilationartists";
+ static constexpr auto SETTING_MUSICLIBRARY_SHOWDISCS = "musiclibrary.showdiscs";
+ static constexpr auto SETTING_MUSICLIBRARY_USEORIGINALDATE = "musiclibrary.useoriginaldate";
+ static constexpr auto SETTING_MUSICLIBRARY_USEARTISTSORTNAME = "musiclibrary.useartistsortname";
+ static constexpr auto SETTING_MUSICLIBRARY_DOWNLOADINFO = "musiclibrary.downloadinfo";
+ static constexpr auto SETTING_MUSICLIBRARY_ARTISTSFOLDER = "musiclibrary.artistsfolder";
+ static constexpr auto SETTING_MUSICLIBRARY_PREFERONLINEALBUMART =
+ "musiclibrary.preferonlinealbumart";
+ static constexpr auto SETTING_MUSICLIBRARY_ARTWORKLEVEL = "musiclibrary.artworklevel";
+ static constexpr auto SETTING_MUSICLIBRARY_USEALLLOCALART = "musiclibrary.usealllocalart";
+ static constexpr auto SETTING_MUSICLIBRARY_USEALLREMOTEART = "musiclibrary.useallremoteart";
+ static constexpr auto SETTING_MUSICLIBRARY_ARTISTART_WHITELIST =
+ "musiclibrary.artistartwhitelist";
+ static constexpr auto SETTING_MUSICLIBRARY_ALBUMART_WHITELIST = "musiclibrary.albumartwhitelist";
+ static constexpr auto SETTING_MUSICLIBRARY_MUSICTHUMBS = "musiclibrary.musicthumbs";
+ static constexpr auto SETTING_MUSICLIBRARY_ARTSETTINGS_UPDATED = "musiclibrary.artsettings";
+ static constexpr auto SETTING_MUSICLIBRARY_ALBUMSSCRAPER = "musiclibrary.albumsscraper";
+ static constexpr auto SETTING_MUSICLIBRARY_ARTISTSSCRAPER = "musiclibrary.artistsscraper";
+ static constexpr auto SETTING_MUSICLIBRARY_OVERRIDETAGS = "musiclibrary.overridetags";
+ static constexpr auto SETTING_MUSICLIBRARY_SHOWALLITEMS = "musiclibrary.showallitems";
+ static constexpr auto SETTING_MUSICLIBRARY_UPDATEONSTARTUP = "musiclibrary.updateonstartup";
+ static constexpr auto SETTING_MUSICLIBRARY_BACKGROUNDUPDATE = "musiclibrary.backgroundupdate";
+ static constexpr auto SETTING_MUSICLIBRARY_CLEANUP = "musiclibrary.cleanup";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT = "musiclibrary.export";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_FILETYPE = "musiclibrary.exportfiletype";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_FOLDER = "musiclibrary.exportfolder";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_ITEMS = "musiclibrary.exportitems";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED = "musiclibrary.exportunscraped";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_OVERWRITE = "musiclibrary.exportoverwrite";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_ARTWORK = "musiclibrary.exportartwork";
+ static constexpr auto SETTING_MUSICLIBRARY_EXPORT_SKIPNFO = "musiclibrary.exportskipnfo";
+ static constexpr auto SETTING_MUSICLIBRARY_IMPORT = "musiclibrary.import";
+ static constexpr auto SETTING_MUSICPLAYER_AUTOPLAYNEXTITEM = "musicplayer.autoplaynextitem";
+ static constexpr auto SETTING_MUSICPLAYER_QUEUEBYDEFAULT = "musicplayer.queuebydefault";
+ static constexpr auto SETTING_MUSICPLAYER_SEEKSTEPS = "musicplayer.seeksteps";
+ static constexpr auto SETTING_MUSICPLAYER_SEEKDELAY = "musicplayer.seekdelay";
+ static constexpr auto SETTING_MUSICPLAYER_REPLAYGAINTYPE = "musicplayer.replaygaintype";
+ static constexpr auto SETTING_MUSICPLAYER_REPLAYGAINPREAMP = "musicplayer.replaygainpreamp";
+ static constexpr auto SETTING_MUSICPLAYER_REPLAYGAINNOGAINPREAMP =
+ "musicplayer.replaygainnogainpreamp";
+ static constexpr auto SETTING_MUSICPLAYER_REPLAYGAINAVOIDCLIPPING =
+ "musicplayer.replaygainavoidclipping";
+ static constexpr auto SETTING_MUSICPLAYER_CROSSFADE = "musicplayer.crossfade";
+ static constexpr auto SETTING_MUSICPLAYER_CROSSFADEALBUMTRACKS =
+ "musicplayer.crossfadealbumtracks";
+ static constexpr auto SETTING_MUSICPLAYER_VISUALISATION = "musicplayer.visualisation";
+ static constexpr auto SETTING_MUSICFILES_SELECTACTION = "musicfiles.selectaction";
+ static constexpr auto SETTING_MUSICFILES_USETAGS = "musicfiles.usetags";
+ static constexpr auto SETTING_MUSICFILES_TRACKFORMAT = "musicfiles.trackformat";
+ static constexpr auto SETTING_MUSICFILES_NOWPLAYINGTRACKFORMAT =
+ "musicfiles.nowplayingtrackformat";
+ static constexpr auto SETTING_MUSICFILES_LIBRARYTRACKFORMAT = "musicfiles.librarytrackformat";
+ static constexpr auto SETTING_MUSICFILES_FINDREMOTETHUMBS = "musicfiles.findremotethumbs";
+ static constexpr auto SETTING_AUDIOCDS_AUTOACTION = "audiocds.autoaction";
+ static constexpr auto SETTING_AUDIOCDS_USECDDB = "audiocds.usecddb";
+ static constexpr auto SETTING_AUDIOCDS_RECORDINGPATH = "audiocds.recordingpath";
+ static constexpr auto SETTING_AUDIOCDS_TRACKPATHFORMAT = "audiocds.trackpathformat";
+ static constexpr auto SETTING_AUDIOCDS_ENCODER = "audiocds.encoder";
+ static constexpr auto SETTING_AUDIOCDS_SETTINGS = "audiocds.settings";
+ static constexpr auto SETTING_AUDIOCDS_EJECTONRIP = "audiocds.ejectonrip";
+ static constexpr auto SETTING_MYMUSIC_SONGTHUMBINVIS = "mymusic.songthumbinvis";
+ static constexpr auto SETTING_MYMUSIC_DEFAULTLIBVIEW = "mymusic.defaultlibview";
+ static constexpr auto SETTING_PICTURES_USETAGS = "pictures.usetags";
+ static constexpr auto SETTING_PICTURES_GENERATETHUMBS = "pictures.generatethumbs";
+ static constexpr auto SETTING_PICTURES_SHOWVIDEOS = "pictures.showvideos";
+ static constexpr auto SETTING_PICTURES_DISPLAYRESOLUTION = "pictures.displayresolution";
+ static constexpr auto SETTING_SLIDESHOW_STAYTIME = "slideshow.staytime";
+ static constexpr auto SETTING_SLIDESHOW_DISPLAYEFFECTS = "slideshow.displayeffects";
+ static constexpr auto SETTING_SLIDESHOW_SHUFFLE = "slideshow.shuffle";
+ static constexpr auto SETTING_SLIDESHOW_HIGHQUALITYDOWNSCALING =
+ "slideshow.highqualitydownscaling";
+ static constexpr auto SETTING_WEATHER_CURRENTLOCATION = "weather.currentlocation";
+ static constexpr auto SETTING_WEATHER_ADDON = "weather.addon";
+ static constexpr auto SETTING_WEATHER_ADDONSETTINGS = "weather.addonsettings";
+ static constexpr auto SETTING_SERVICES_DEVICENAME = "services.devicename";
+ static constexpr auto SETTING_SERVICES_DEVICEUUID = "services.deviceuuid";
+ static constexpr auto SETTING_SERVICES_UPNP = "services.upnp";
+ static constexpr auto SETTING_SERVICES_UPNPSERVER = "services.upnpserver";
+ static constexpr auto SETTING_SERVICES_UPNPANNOUNCE = "services.upnpannounce";
+ static constexpr auto SETTING_SERVICES_UPNPLOOKFOREXTERNALSUBTITLES =
+ "services.upnplookforexternalsubtitles";
+ static constexpr auto SETTING_SERVICES_UPNPCONTROLLER = "services.upnpcontroller";
+ static constexpr auto SETTING_SERVICES_UPNPRENDERER = "services.upnprenderer";
+ static constexpr auto SETTING_SERVICES_WEBSERVER = "services.webserver";
+ static constexpr auto SETTING_SERVICES_WEBSERVERPORT = "services.webserverport";
+ static constexpr auto SETTING_SERVICES_WEBSERVERAUTHENTICATION =
+ "services.webserverauthentication";
+ static constexpr auto SETTING_SERVICES_WEBSERVERUSERNAME = "services.webserverusername";
+ static constexpr auto SETTING_SERVICES_WEBSERVERPASSWORD = "services.webserverpassword";
+ static constexpr auto SETTING_SERVICES_WEBSERVERSSL = "services.webserverssl";
+ static constexpr auto SETTING_SERVICES_WEBSKIN = "services.webskin";
+ static constexpr auto SETTING_SERVICES_ESENABLED = "services.esenabled";
+ static constexpr auto SETTING_SERVICES_ESPORT = "services.esport";
+ static constexpr auto SETTING_SERVICES_ESPORTRANGE = "services.esportrange";
+ static constexpr auto SETTING_SERVICES_ESMAXCLIENTS = "services.esmaxclients";
+ static constexpr auto SETTING_SERVICES_ESALLINTERFACES = "services.esallinterfaces";
+ static constexpr auto SETTING_SERVICES_ESINITIALDELAY = "services.esinitialdelay";
+ static constexpr auto SETTING_SERVICES_ESCONTINUOUSDELAY = "services.escontinuousdelay";
+ static constexpr auto SETTING_SERVICES_ZEROCONF = "services.zeroconf";
+ static constexpr auto SETTING_SERVICES_AIRPLAY = "services.airplay";
+ static constexpr auto SETTING_SERVICES_AIRPLAYVOLUMECONTROL = "services.airplayvolumecontrol";
+ static constexpr auto SETTING_SERVICES_USEAIRPLAYPASSWORD = "services.useairplaypassword";
+ static constexpr auto SETTING_SERVICES_AIRPLAYPASSWORD = "services.airplaypassword";
+ static constexpr auto SETTING_SERVICES_AIRPLAYVIDEOSUPPORT = "services.airplayvideosupport";
+ static constexpr auto SETTING_SMB_WINSSERVER = "smb.winsserver";
+ static constexpr auto SETTING_SMB_WORKGROUP = "smb.workgroup";
+ static constexpr auto SETTING_SMB_MINPROTOCOL = "smb.minprotocol";
+ static constexpr auto SETTING_SMB_MAXPROTOCOL = "smb.maxprotocol";
+ static constexpr auto SETTING_SMB_LEGACYSECURITY = "smb.legacysecurity";
+ static constexpr auto SETTING_SERVICES_WSDISCOVERY = "services.wsdiscovery";
+ static constexpr auto SETTING_VIDEOSCREEN_MONITOR = "videoscreen.monitor";
+ static constexpr auto SETTING_VIDEOSCREEN_SCREEN = "videoscreen.screen";
+ static constexpr auto SETTING_VIDEOSCREEN_WHITELIST = "videoscreen.whitelist";
+ static constexpr auto SETTING_VIDEOSCREEN_RESOLUTION = "videoscreen.resolution";
+ static constexpr auto SETTING_VIDEOSCREEN_SCREENMODE = "videoscreen.screenmode";
+ static constexpr auto SETTING_VIDEOSCREEN_FAKEFULLSCREEN = "videoscreen.fakefullscreen";
+ static constexpr auto SETTING_VIDEOSCREEN_BLANKDISPLAYS = "videoscreen.blankdisplays";
+ static constexpr auto SETTING_VIDEOSCREEN_STEREOSCOPICMODE = "videoscreen.stereoscopicmode";
+ static constexpr auto SETTING_VIDEOSCREEN_PREFEREDSTEREOSCOPICMODE =
+ "videoscreen.preferedstereoscopicmode";
+ static constexpr auto SETTING_VIDEOSCREEN_NOOFBUFFERS = "videoscreen.noofbuffers";
+ static constexpr auto SETTING_VIDEOSCREEN_3DLUT = "videoscreen.cms3dlut";
+ static constexpr auto SETTING_VIDEOSCREEN_DISPLAYPROFILE = "videoscreen.displayprofile";
+ static constexpr auto SETTING_VIDEOSCREEN_GUICALIBRATION = "videoscreen.guicalibration";
+ static constexpr auto SETTING_VIDEOSCREEN_TESTPATTERN = "videoscreen.testpattern";
+ static constexpr auto SETTING_VIDEOSCREEN_LIMITEDRANGE = "videoscreen.limitedrange";
+ static constexpr auto SETTING_VIDEOSCREEN_FRAMEPACKING = "videoscreen.framepacking";
+ static constexpr auto SETTING_VIDEOSCREEN_10BITSURFACES = "videoscreen.10bitsurfaces";
+ static constexpr auto SETTING_VIDEOSCREEN_GUISDRPEAKLUMINANCE = "videoscreen.guisdrpeakluminance";
+ static constexpr auto SETTING_AUDIOOUTPUT_AUDIODEVICE = "audiooutput.audiodevice";
+ static constexpr auto SETTING_AUDIOOUTPUT_CHANNELS = "audiooutput.channels";
+ static constexpr auto SETTING_AUDIOOUTPUT_CONFIG = "audiooutput.config";
+ static constexpr auto SETTING_AUDIOOUTPUT_SAMPLERATE = "audiooutput.samplerate";
+ static constexpr auto SETTING_AUDIOOUTPUT_STEREOUPMIX = "audiooutput.stereoupmix";
+ static constexpr auto SETTING_AUDIOOUTPUT_MAINTAINORIGINALVOLUME =
+ "audiooutput.maintainoriginalvolume";
+ static constexpr auto SETTING_AUDIOOUTPUT_PROCESSQUALITY = "audiooutput.processquality";
+ static constexpr auto SETTING_AUDIOOUTPUT_ATEMPOTHRESHOLD = "audiooutput.atempothreshold";
+ static constexpr auto SETTING_AUDIOOUTPUT_STREAMSILENCE = "audiooutput.streamsilence";
+ static constexpr auto SETTING_AUDIOOUTPUT_STREAMNOISE = "audiooutput.streamnoise";
+ static constexpr auto SETTING_AUDIOOUTPUT_GUISOUNDMODE = "audiooutput.guisoundmode";
+ static constexpr auto SETTING_AUDIOOUTPUT_GUISOUNDVOLUME = "audiooutput.guisoundvolume";
+ static constexpr auto SETTING_AUDIOOUTPUT_PASSTHROUGH = "audiooutput.passthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_PASSTHROUGHDEVICE = "audiooutput.passthroughdevice";
+ static constexpr auto SETTING_AUDIOOUTPUT_AC3PASSTHROUGH = "audiooutput.ac3passthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_AC3TRANSCODE = "audiooutput.ac3transcode";
+ static constexpr auto SETTING_AUDIOOUTPUT_EAC3PASSTHROUGH = "audiooutput.eac3passthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_DTSPASSTHROUGH = "audiooutput.dtspassthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_TRUEHDPASSTHROUGH = "audiooutput.truehdpassthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_DTSHDPASSTHROUGH = "audiooutput.dtshdpassthrough";
+ static constexpr auto SETTING_AUDIOOUTPUT_DTSHDCOREFALLBACK = "audiooutput.dtshdcorefallback";
+ static constexpr auto SETTING_AUDIOOUTPUT_VOLUMESTEPS = "audiooutput.volumesteps";
+ static constexpr auto SETTING_INPUT_PERIPHERALS = "input.peripherals";
+ static constexpr auto SETTING_INPUT_PERIPHERALLIBRARIES = "input.peripherallibraries";
+ static constexpr auto SETTING_INPUT_ENABLEMOUSE = "input.enablemouse";
+ static constexpr auto SETTING_INPUT_ASKNEWCONTROLLERS = "input.asknewcontrollers";
+ static constexpr auto SETTING_INPUT_CONTROLLERCONFIG = "input.controllerconfig";
+ static constexpr auto SETTING_INPUT_RUMBLENOTIFY = "input.rumblenotify";
+ static constexpr auto SETTING_INPUT_TESTRUMBLE = "input.testrumble";
+ static constexpr auto SETTING_INPUT_CONTROLLERPOWEROFF = "input.controllerpoweroff";
+ static constexpr auto SETTING_INPUT_APPLEREMOTEMODE = "input.appleremotemode";
+ static constexpr auto SETTING_INPUT_APPLEREMOTEALWAYSON = "input.appleremotealwayson";
+ static constexpr auto SETTING_INPUT_APPLEREMOTESEQUENCETIME = "input.appleremotesequencetime";
+ static constexpr auto SETTING_INPUT_SIRIREMOTEIDLETIMERENABLED = "input.siriremoteidletimerenabled";
+ static constexpr auto SETTING_INPUT_SIRIREMOTEIDLETIME = "input.siriremoteidletime";
+ static constexpr auto SETTING_INPUT_SIRIREMOTEHORIZONTALSENSITIVITY =
+ "input.siriremotehorizontalsensitivity";
+ static constexpr auto SETTING_INPUT_SIRIREMOTEVERTICALSENSITIVITY =
+ "input.siriremoteverticalsensitivity";
+ static constexpr auto SETTING_INPUT_TVOSUSEKODIKEYBOARD = "input.tvosusekodikeyboard";
+ static constexpr auto SETTING_NETWORK_USEHTTPPROXY = "network.usehttpproxy";
+ static constexpr auto SETTING_NETWORK_HTTPPROXYTYPE = "network.httpproxytype";
+ static constexpr auto SETTING_NETWORK_HTTPPROXYSERVER = "network.httpproxyserver";
+ static constexpr auto SETTING_NETWORK_HTTPPROXYPORT = "network.httpproxyport";
+ static constexpr auto SETTING_NETWORK_HTTPPROXYUSERNAME = "network.httpproxyusername";
+ static constexpr auto SETTING_NETWORK_HTTPPROXYPASSWORD = "network.httpproxypassword";
+ static constexpr auto SETTING_NETWORK_BANDWIDTH = "network.bandwidth";
+ static constexpr auto SETTING_POWERMANAGEMENT_DISPLAYSOFF = "powermanagement.displaysoff";
+ static constexpr auto SETTING_POWERMANAGEMENT_SHUTDOWNTIME = "powermanagement.shutdowntime";
+ static constexpr auto SETTING_POWERMANAGEMENT_SHUTDOWNSTATE = "powermanagement.shutdownstate";
+ static constexpr auto SETTING_POWERMANAGEMENT_WAKEONACCESS = "powermanagement.wakeonaccess";
+ static constexpr auto SETTING_POWERMANAGEMENT_WAITFORNETWORK = "powermanagement.waitfornetwork";
+ static constexpr auto SETTING_DEBUG_SHOWLOGINFO = "debug.showloginfo";
+ static constexpr auto SETTING_DEBUG_EXTRALOGGING = "debug.extralogging";
+ static constexpr auto SETTING_DEBUG_SETEXTRALOGLEVEL = "debug.setextraloglevel";
+ static constexpr auto SETTING_DEBUG_SCREENSHOTPATH = "debug.screenshotpath";
+ static constexpr auto SETTING_DEBUG_SHARE_LOG = "debug.sharelog";
+ static constexpr auto SETTING_EVENTLOG_ENABLED = "eventlog.enabled";
+ static constexpr auto SETTING_EVENTLOG_ENABLED_NOTIFICATIONS = "eventlog.enablednotifications";
+ static constexpr auto SETTING_EVENTLOG_SHOW = "eventlog.show";
+ static constexpr auto SETTING_MASTERLOCK_LOCKCODE = "masterlock.lockcode";
+ static constexpr auto SETTING_MASTERLOCK_STARTUPLOCK = "masterlock.startuplock";
+ static constexpr auto SETTING_MASTERLOCK_MAXRETRIES = "masterlock.maxretries";
+ static constexpr auto SETTING_CACHE_HARDDISK = "cache.harddisk";
+ static constexpr auto SETTING_CACHEVIDEO_DVDROM = "cachevideo.dvdrom";
+ static constexpr auto SETTING_CACHEVIDEO_LAN = "cachevideo.lan";
+ static constexpr auto SETTING_CACHEVIDEO_INTERNET = "cachevideo.internet";
+ static constexpr auto SETTING_CACHEAUDIO_DVDROM = "cacheaudio.dvdrom";
+ static constexpr auto SETTING_CACHEAUDIO_LAN = "cacheaudio.lan";
+ static constexpr auto SETTING_CACHEAUDIO_INTERNET = "cacheaudio.internet";
+ static constexpr auto SETTING_CACHEDVD_DVDROM = "cachedvd.dvdrom";
+ static constexpr auto SETTING_CACHEDVD_LAN = "cachedvd.lan";
+ static constexpr auto SETTING_CACHEUNKNOWN_INTERNET = "cacheunknown.internet";
+ static constexpr auto SETTING_SYSTEM_PLAYLISTSPATH = "system.playlistspath";
+ static constexpr auto SETTING_ADDONS_AUTOUPDATES = "general.addonupdates";
+ static constexpr auto SETTING_ADDONS_NOTIFICATIONS = "general.addonnotifications";
+ static constexpr auto SETTING_ADDONS_SHOW_RUNNING = "addons.showrunning";
+ static constexpr auto SETTING_ADDONS_ALLOW_UNKNOWN_SOURCES = "addons.unknownsources";
+ static constexpr auto SETTING_ADDONS_UPDATEMODE = "addons.updatemode";
+ static constexpr auto SETTING_ADDONS_MANAGE_DEPENDENCIES = "addons.managedependencies";
+ static constexpr auto SETTING_ADDONS_REMOVE_ORPHANED_DEPENDENCIES =
+ "addons.removeorphaneddependencies";
+ static constexpr auto SETTING_GENERAL_ADDONFOREIGNFILTER = "general.addonforeignfilter";
+ static constexpr auto SETTING_GENERAL_ADDONBROKENFILTER = "general.addonbrokenfilter";
+ static constexpr auto SETTING_SOURCE_VIDEOS = "source.videos";
+ static constexpr auto SETTING_SOURCE_MUSIC = "source.music";
+ static constexpr auto SETTING_SOURCE_PICTURES = "source.pictures";
+
+ // values for SETTING_VIDEOLIBRARY_SHOWUNWATCHEDPLOTS
+ static const int VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_MOVIES = 0;
+ static const int VIDEOLIBRARY_PLOTS_SHOW_UNWATCHED_TVSHOWEPISODES = 1;
+ static const int VIDEOLIBRARY_THUMB_SHOW_UNWATCHED_EPISODE = 2;
+ // values for SETTING_VIDEOLIBRARY_ARTWORK_LEVEL
+ static const int VIDEOLIBRARY_ARTWORK_LEVEL_ALL = 0;
+ static const int VIDEOLIBRARY_ARTWORK_LEVEL_BASIC = 1;
+ static const int VIDEOLIBRARY_ARTWORK_LEVEL_CUSTOM = 2;
+ static const int VIDEOLIBRARY_ARTWORK_LEVEL_NONE = 3;
+
+ // values for SETTING_MUSICLIBRARY_ARTWORKLEVEL
+ static const int MUSICLIBRARY_ARTWORK_LEVEL_ALL = 0;
+ static const int MUSICLIBRARY_ARTWORK_LEVEL_BASIC = 1;
+ static const int MUSICLIBRARY_ARTWORK_LEVEL_CUSTOM = 2;
+ static const int MUSICLIBRARY_ARTWORK_LEVEL_NONE = 3;
+
+ /*!
+ \brief Creates a new settings wrapper around a new settings manager.
+
+ For access to the "global" settings wrapper the static GetInstance() method should
+ be used.
+ */
+ CSettings() = default;
+ ~CSettings() override = default;
+
+ CSettingsManager* GetSettingsManager() const { return m_settingsManager; }
+
+ // specialization of CSettingsBase
+ bool Initialize() override;
+
+ /*!
+ \brief Registers the given ISubSettings implementation.
+
+ \param subSettings ISubSettings implementation
+ */
+ void RegisterSubSettings(ISubSettings* subSettings);
+ /*!
+ \brief Unregisters the given ISubSettings implementation.
+
+ \param subSettings ISubSettings implementation
+ */
+ void UnregisterSubSettings(ISubSettings* subSettings);
+
+ // implementations of CSettingsBase
+ bool Load() override;
+ bool Save() override;
+
+ /*!
+ \brief Loads setting values from the given (XML) file.
+
+ \param file Path to an XML file containing setting values
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool Load(const std::string &file);
+ /*!
+ \brief Loads setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool Load(const TiXmlElement* root);
+ /*!
+ \brief Loads setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \param hide Whether to hide the loaded settings or not
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool LoadHidden(const TiXmlElement *root) { return CSettingsBase::LoadHiddenValuesFromXml(root); }
+
+ /*!
+ \brief Saves the setting values to the given (XML) file.
+
+ \param file Path to an XML file
+ \return True if the setting values were successfully saved, false otherwise
+ */
+ bool Save(const std::string &file);
+ /*!
+ \brief Saves the setting values to the given XML node.
+
+ \param root XML node
+ \return True if the setting values were successfully saved, false otherwise
+ */
+ bool Save(TiXmlNode* root) const override;
+
+ /*!
+ \brief Loads the setting being represented by the given XML node with the
+ given identifier.
+
+ \param node XML node representing the setting to load
+ \param settingId Setting identifier
+ \return True if the setting was successfully loaded from the given XML node, false otherwise
+ */
+ bool LoadSetting(const TiXmlNode *node, const std::string &settingId);
+
+ // overwrite (not override) from CSettingsBase
+ bool GetBool(const std::string& id) const;
+
+ /*!
+ \brief Clears the complete settings.
+
+ This removes all initialized settings, groups, categories and sections and
+ returns to the uninitialized state. Any registered callbacks or
+ implementations stay registered.
+ */
+ void Clear() override;
+
+protected:
+ // specializations of CSettingsBase
+ void InitializeSettingTypes() override;
+ void InitializeControls() override;
+ void InitializeOptionFillers() override;
+ void UninitializeOptionFillers() override;
+ void InitializeConditions() override;
+ void UninitializeConditions() override;
+ void InitializeVisibility() override;
+ void InitializeDefaults() override;
+ void InitializeISettingsHandlers() override;
+ void UninitializeISettingsHandlers() override;
+ void InitializeISubSettings() override;
+ void UninitializeISubSettings() override;
+ void InitializeISettingCallbacks() override;
+ void UninitializeISettingCallbacks() override;
+
+ // implementation of CSettingsBase
+ bool InitializeDefinitions() override;
+
+private:
+ CSettings(const CSettings&) = delete;
+ CSettings const& operator=(CSettings const&) = delete;
+
+ bool Load(const TiXmlElement* root, bool& updated);
+
+ // implementation of ISubSettings
+ bool Load(const TiXmlNode* settings) override;
+
+ bool Initialize(const std::string &file);
+ bool Reset();
+
+ std::set<ISubSettings*> m_subSettings;
+};
diff --git a/xbmc/settings/SettingsBase.cpp b/xbmc/settings/SettingsBase.cpp
new file mode 100644
index 0000000..a118a36
--- /dev/null
+++ b/xbmc/settings/SettingsBase.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2016-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 "SettingsBase.h"
+
+#include "settings/SettingUtils.h"
+#include "settings/SettingsValueXmlSerializer.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/Variant.h"
+#include "utils/XBMCTinyXML.h"
+
+#include <mutex>
+
+#define SETTINGS_XML_ROOT "settings"
+
+CSettingsBase::CSettingsBase()
+ : m_settingsManager(new CSettingsManager())
+{ }
+
+CSettingsBase::~CSettingsBase()
+{
+ Uninitialize();
+
+ delete m_settingsManager;
+}
+
+bool CSettingsBase::Initialize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (m_initialized)
+ return false;
+
+ // register custom setting types
+ InitializeSettingTypes();
+ // register custom setting controls
+ InitializeControls();
+
+ // option fillers and conditions need to be
+ // initialized before the setting definitions
+ InitializeOptionFillers();
+ InitializeConditions();
+
+ // load the settings definitions
+ if (!InitializeDefinitions())
+ return false;
+
+ InitializeVisibility();
+ InitializeDefaults();
+
+ m_settingsManager->SetInitialized();
+
+ InitializeISettingsHandlers();
+ InitializeISubSettings();
+ InitializeISettingCallbacks();
+
+ m_initialized = true;
+
+ return true;
+}
+
+bool CSettingsBase::IsInitialized() const
+{
+ return m_initialized && m_settingsManager->IsInitialized();
+}
+
+bool CSettingsBase::LoadValuesFromXml(const CXBMCTinyXML& xml, bool& updated)
+{
+ const TiXmlElement* xmlRoot = xml.RootElement();
+ if (xmlRoot == nullptr || xmlRoot->ValueStr() != SETTINGS_XML_ROOT)
+ return false;
+
+ return m_settingsManager->Load(xmlRoot, updated);
+}
+
+bool CSettingsBase::LoadValuesFromXml(const TiXmlElement* root, bool& updated)
+{
+ if (root == nullptr)
+ return false;
+
+ return m_settingsManager->Load(root, updated);
+}
+
+bool CSettingsBase::LoadHiddenValuesFromXml(const TiXmlElement* root)
+{
+ if (root == nullptr)
+ return false;
+
+ std::map<std::string, std::shared_ptr<CSetting>> loadedSettings;
+
+ bool updated;
+ // don't trigger events for hidden settings
+ bool success = m_settingsManager->Load(root, updated, false, &loadedSettings);
+ if (success)
+ {
+ for(std::map<std::string, std::shared_ptr<CSetting>>::const_iterator setting = loadedSettings.begin(); setting != loadedSettings.end(); ++setting)
+ setting->second->SetVisible(false);
+ }
+
+ return success;
+}
+
+void CSettingsBase::SetLoaded()
+{
+ m_settingsManager->SetLoaded();
+}
+
+bool CSettingsBase::IsLoaded() const
+{
+ return m_settingsManager->IsLoaded();
+}
+
+bool CSettingsBase::SaveValuesToXml(CXBMCTinyXML& xml) const
+{
+ std::string serializedSettings;
+ auto xmlSerializer = std::make_unique<CSettingsValueXmlSerializer>();
+ if (!m_settingsManager->Save(xmlSerializer.get(), serializedSettings))
+ return false;
+
+ if (!xml.Parse(serializedSettings))
+ return false;
+
+ return true;
+}
+
+void CSettingsBase::Unload()
+{
+ m_settingsManager->Unload();
+}
+
+void CSettingsBase::Uninitialize()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (!m_initialized)
+ return;
+
+ // unregister setting option fillers
+ UninitializeOptionFillers();
+
+ // unregister setting conditions
+ UninitializeConditions();
+
+ // unregister ISettingCallback implementations
+ UninitializeISettingCallbacks();
+
+ // cleanup the settings manager
+ m_settingsManager->Clear();
+
+ // unregister ISubSettings implementations
+ UninitializeISubSettings();
+ // unregister ISettingsHandler implementations
+ UninitializeISettingsHandlers();
+
+ m_initialized = false;
+}
+
+void CSettingsBase::RegisterCallback(ISettingCallback* callback, const std::set<std::string>& settingList)
+{
+ m_settingsManager->RegisterCallback(callback, settingList);
+}
+
+void CSettingsBase::UnregisterCallback(ISettingCallback* callback)
+{
+ m_settingsManager->UnregisterCallback(callback);
+}
+
+SettingPtr CSettingsBase::GetSetting(const std::string& id) const
+{
+ if (id.empty())
+ return nullptr;
+
+ return m_settingsManager->GetSetting(id);
+}
+
+std::vector<std::shared_ptr<CSettingSection>> CSettingsBase::GetSections() const
+{
+ return m_settingsManager->GetSections();
+}
+
+std::shared_ptr<CSettingSection> CSettingsBase::GetSection(const std::string& section) const
+{
+ if (section.empty())
+ return nullptr;
+
+ return m_settingsManager->GetSection(section);
+}
+
+bool CSettingsBase::GetBool(const std::string& id) const
+{
+ return m_settingsManager->GetBool(id);
+}
+
+bool CSettingsBase::SetBool(const std::string& id, bool value)
+{
+ return m_settingsManager->SetBool(id, value);
+}
+
+bool CSettingsBase::ToggleBool(const std::string& id)
+{
+ return m_settingsManager->ToggleBool(id);
+}
+
+int CSettingsBase::GetInt(const std::string& id) const
+{
+ return m_settingsManager->GetInt(id);
+}
+
+bool CSettingsBase::SetInt(const std::string& id, int value)
+{
+ return m_settingsManager->SetInt(id, value);
+}
+
+double CSettingsBase::GetNumber(const std::string& id) const
+{
+ return m_settingsManager->GetNumber(id);
+}
+
+bool CSettingsBase::SetNumber(const std::string& id, double value)
+{
+ return m_settingsManager->SetNumber(id, value);
+}
+
+std::string CSettingsBase::GetString(const std::string& id) const
+{
+ return m_settingsManager->GetString(id);
+}
+
+bool CSettingsBase::SetString(const std::string& id, const std::string& value)
+{
+ return m_settingsManager->SetString(id, value);
+}
+
+std::vector<CVariant> CSettingsBase::GetList(const std::string& id) const
+{
+ std::shared_ptr<CSetting> setting = m_settingsManager->GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return std::vector<CVariant>();
+
+ return CSettingUtils::GetList(std::static_pointer_cast<CSettingList>(setting));
+}
+
+bool CSettingsBase::SetList(const std::string& id, const std::vector<CVariant>& value)
+{
+ std::shared_ptr<CSetting> setting = m_settingsManager->GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return false;
+
+ return CSettingUtils::SetList(std::static_pointer_cast<CSettingList>(setting), value);
+}
+
+bool CSettingsBase::SetDefault(const std::string &id)
+{
+ return m_settingsManager->SetDefault(id);
+}
+
+void CSettingsBase::SetDefaults()
+{
+ m_settingsManager->SetDefaults();
+}
+
+bool CSettingsBase::InitializeDefinitionsFromXml(const CXBMCTinyXML& xml)
+{
+ const TiXmlElement* root = xml.RootElement();
+ if (root == nullptr)
+ return false;
+
+ return m_settingsManager->Initialize(root);
+}
diff --git a/xbmc/settings/SettingsBase.h b/xbmc/settings/SettingsBase.h
new file mode 100644
index 0000000..209b6cc
--- /dev/null
+++ b/xbmc/settings/SettingsBase.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016-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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingCallback.h"
+#include "threads/CriticalSection.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+class CSetting;
+class CSettingSection;
+class CSettingsManager;
+class CVariant;
+class CXBMCTinyXML;
+class TiXmlElement;
+
+/*!
+ \brief Basic wrapper around CSettingsManager providing the framework for
+ properly setting up the settings manager and registering all the callbacks,
+ handlers and custom setting types.
+ \sa CSettingsManager
+ */
+class CSettingsBase
+{
+public:
+ virtual ~CSettingsBase();
+
+ CSettingsManager* GetSettingsManager() const { return m_settingsManager; }
+
+ /*!
+ \brief Initializes the setting system with the generic
+ settings definition and platform specific setting definitions.
+
+ \return True if the initialization was successful, false otherwise
+ */
+ virtual bool Initialize();
+ /*!
+ \brief Returns whether the settings system has been initialized or not.
+ */
+ virtual bool IsInitialized() const;
+ /*!
+ \brief Loads the setting values.
+
+ \return True if the setting values are successfully loaded, false otherwise
+ */
+ virtual bool Load() = 0;
+ /*!
+ \brief Tells the settings system that all setting values
+ have been loaded.
+
+ This manual trigger is necessary to enable the ISettingCallback methods
+ being executed.
+ */
+ virtual void SetLoaded();
+ /*!
+ \brief Returns whether the settings system has been loaded or not.
+ */
+ virtual bool IsLoaded() const;
+ /*!
+ \brief Saves the setting values.
+
+ \return True if the setting values were successfully saved, false otherwise
+ */
+ virtual bool Save() = 0;
+ /*!
+ \brief Unloads the previously loaded setting values.
+
+ The values of all the settings are reset to their default values.
+ */
+ virtual void Unload();
+ /*!
+ \brief Uninitializes the settings system.
+
+ Unregisters all previously registered callbacks and destroys all setting
+ objects.
+ */
+ virtual void Uninitialize();
+
+ /*!
+ \brief Registers the given ISettingCallback implementation for the given
+ set of settings.
+
+ \param callback ISettingCallback implementation
+ \param settingList List of setting identifiers for which the given callback shall be triggered
+ */
+ void RegisterCallback(ISettingCallback* callback, const std::set<std::string>& settingList);
+ /*!
+ \brief Unregisters the given ISettingCallback implementation.
+
+ \param callback ISettingCallback implementation
+ */
+ void UnregisterCallback(ISettingCallback* callback);
+
+ /*!
+ \brief Gets the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Setting object with the given identifier or NULL if the identifier is unknown
+ */
+ std::shared_ptr<CSetting> GetSetting(const std::string& id) const;
+ /*!
+ \brief Gets the full list of setting sections.
+
+ \return List of setting sections
+ */
+ std::vector<std::shared_ptr<CSettingSection>> GetSections() const;
+ /*!
+ \brief Gets the setting section with the given identifier.
+
+ \param section Setting section identifier
+ \return Setting section with the given identifier or NULL if the identifier is unknown
+ */
+ std::shared_ptr<CSettingSection> GetSection(const std::string& section) const;
+
+ /*!
+ \brief Gets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Boolean value of the setting with the given identifier
+ */
+ bool GetBool(const std::string& id) const;
+ /*!
+ \brief Gets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Integer value of the setting with the given identifier
+ */
+ int GetInt(const std::string& id) const;
+ /*!
+ \brief Gets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Real number value of the setting with the given identifier
+ */
+ double GetNumber(const std::string& id) const;
+ /*!
+ \brief Gets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return String value of the setting with the given identifier
+ */
+ std::string GetString(const std::string& id) const;
+ /*!
+ \brief Gets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \return List of values of the setting with the given identifier
+ */
+ std::vector<CVariant> GetList(const std::string& id) const;
+
+ /*!
+ \brief Sets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Boolean value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetBool(const std::string& id, bool value);
+ /*!
+ \brief Toggles the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return True if toggling the boolean value was successful, false otherwise
+ */
+ bool ToggleBool(const std::string& id);
+ /*!
+ \brief Sets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Integer value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetInt(const std::string& id, int value);
+ /*!
+ \brief Sets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Real number value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetNumber(const std::string& id, double value);
+ /*!
+ \brief Sets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value String value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetString(const std::string& id, const std::string& value);
+ /*!
+ \brief Sets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Values to set
+ \return True if setting the values was successful, false otherwise
+ */
+ bool SetList(const std::string& id, const std::vector<CVariant>& value);
+
+ /*!
+ \brief Sets the value of the setting with the given identifier to its default.
+
+ \param id Setting identifier
+ \return True if setting the value to its default was successful, false otherwise
+ */
+ bool SetDefault(const std::string &id);
+ /*!
+ \brief Sets the value of all settings to their default.
+ */
+ void SetDefaults();
+
+protected:
+ CSettingsBase();
+
+ virtual void InitializeSettingTypes() { }
+ virtual void InitializeControls() { }
+ virtual void InitializeOptionFillers() { }
+ virtual void UninitializeOptionFillers() { }
+ virtual void InitializeConditions() { }
+ virtual void UninitializeConditions() { }
+ virtual bool InitializeDefinitions() = 0;
+ virtual void InitializeVisibility() { }
+ virtual void InitializeDefaults() { }
+ virtual void InitializeISettingsHandlers() { }
+ virtual void UninitializeISettingsHandlers() { }
+ virtual void InitializeISubSettings() { }
+ virtual void UninitializeISubSettings() { }
+ virtual void InitializeISettingCallbacks() { }
+ virtual void UninitializeISettingCallbacks() { }
+
+ bool InitializeDefinitionsFromXml(const CXBMCTinyXML& xml);
+
+ /*!
+ \brief Loads setting values from the given document in XML format
+
+ \param xml Document in XML format from which the settings are loaded
+ \param updated Output parameter indicating whether setting values had to be updated
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool LoadValuesFromXml(const CXBMCTinyXML& xml, bool& updated);
+ /*!
+ \brief Saves the setting values in XML format to the given document.
+
+ \param xml Document to save the setting values in XML format into
+ \return True if the setting values were successfully saved, false otherwise
+ */
+ bool SaveValuesToXml(CXBMCTinyXML& xml) const;
+
+ /*!
+ \brief Loads setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \param updated Output parameter indicating whether setting values had to be updated
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool LoadValuesFromXml(const TiXmlElement* root, bool& updated);
+
+ /*!
+ \brief Loads hidden setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool LoadHiddenValuesFromXml(const TiXmlElement* root);
+
+ bool m_initialized = false;
+ CSettingsManager* m_settingsManager;
+ mutable CCriticalSection m_critical;
+private:
+ CSettingsBase(const CSettingsBase&) = delete;
+ CSettingsBase& operator=(const CSettingsBase&) = delete;
+};
diff --git a/xbmc/settings/SettingsComponent.cpp b/xbmc/settings/SettingsComponent.cpp
new file mode 100644
index 0000000..0f5f7ae
--- /dev/null
+++ b/xbmc/settings/SettingsComponent.cpp
@@ -0,0 +1,400 @@
+/*
+ * 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 "SettingsComponent.h"
+
+#include "CompileInfo.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "application/AppParams.h"
+#include "filesystem/Directory.h"
+#include "filesystem/SpecialProtocol.h"
+#ifdef TARGET_DARWIN_EMBEDDED
+#include "platform/darwin/ios-common/DarwinEmbedUtils.h"
+#endif
+#ifdef TARGET_WINDOWS
+#include "platform/Environment.h"
+#endif
+#include "profiles/ProfileManager.h"
+#include "settings/AdvancedSettings.h"
+#include "settings/Settings.h"
+#include "settings/SubtitlesSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+#ifdef TARGET_WINDOWS
+#include "win32util.h"
+#endif
+
+CSettingsComponent::CSettingsComponent()
+ : m_settings(new CSettings()),
+ m_advancedSettings(new CAdvancedSettings()),
+ m_subtitlesSettings(new KODI::SUBTITLES::CSubtitlesSettings(m_settings)),
+ m_profileManager(new CProfileManager())
+{
+}
+
+CSettingsComponent::~CSettingsComponent()
+{
+}
+
+void CSettingsComponent::Initialize()
+{
+ if (m_state == State::DEINITED)
+ {
+ const auto params = CServiceBroker::GetAppParams();
+
+ // only the InitDirectories* for the current platform should return true
+ bool inited = InitDirectoriesLinux(params->HasPlatformDirectories());
+ if (!inited)
+ inited = InitDirectoriesOSX(params->HasPlatformDirectories());
+ if (!inited)
+ inited = InitDirectoriesWin32(params->HasPlatformDirectories());
+
+ m_settings->Initialize();
+
+ m_advancedSettings->Initialize(*m_settings->GetSettingsManager());
+ URIUtils::RegisterAdvancedSettings(*m_advancedSettings);
+
+ m_profileManager->Initialize(m_settings);
+
+ m_state = State::INITED;
+ }
+}
+
+bool CSettingsComponent::Load()
+{
+ if (m_state == State::INITED)
+ {
+ if (!m_profileManager->Load())
+ {
+ CLog::Log(LOGFATAL, "unable to load profile");
+ return false;
+ }
+
+ CSpecialProtocol::RegisterProfileManager(*m_profileManager);
+ XFILE::IDirectory::RegisterProfileManager(*m_profileManager);
+
+ if (!m_settings->Load())
+ {
+ CLog::Log(LOGFATAL, "unable to load settings");
+ return false;
+ }
+
+ m_settings->SetLoaded();
+
+ m_state = State::LOADED;
+ return true;
+ }
+ else if (m_state == State::LOADED)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void CSettingsComponent::Deinitialize()
+{
+ if (m_state >= State::INITED)
+ {
+ if (m_state == State::LOADED)
+ {
+ m_subtitlesSettings.reset();
+
+ m_settings->Unload();
+
+ XFILE::IDirectory::UnregisterProfileManager();
+ CSpecialProtocol::UnregisterProfileManager();
+ }
+ m_profileManager->Uninitialize();
+
+ URIUtils::UnregisterAdvancedSettings();
+ m_advancedSettings->Uninitialize(*m_settings->GetSettingsManager());
+
+ m_settings->Uninitialize();
+ }
+ m_state = State::DEINITED;
+}
+
+std::shared_ptr<CSettings> CSettingsComponent::GetSettings()
+{
+ return m_settings;
+}
+
+std::shared_ptr<CAdvancedSettings> CSettingsComponent::GetAdvancedSettings()
+{
+ return m_advancedSettings;
+}
+
+std::shared_ptr<KODI::SUBTITLES::CSubtitlesSettings> CSettingsComponent::GetSubtitlesSettings()
+{
+ return m_subtitlesSettings;
+}
+
+std::shared_ptr<CProfileManager> CSettingsComponent::GetProfileManager()
+{
+ return m_profileManager;
+}
+
+bool CSettingsComponent::InitDirectoriesLinux(bool bPlatformDirectories)
+{
+ /*
+ The following is the directory mapping for Platform Specific Mode:
+
+ special://xbmc/ => [read-only] system directory (/usr/share/kodi)
+ special://home/ => [read-write] user's directory that will override special://kodi/ system-wide
+ installations like skins, screensavers, etc.
+ ($HOME/.kodi)
+ NOTE: XBMC will look in both special://xbmc/addons and special://home/addons for addons.
+ special://masterprofile/ => [read-write] userdata of master profile. It will by default be
+ mapped to special://home/userdata ($HOME/.kodi/userdata)
+ special://profile/ => [read-write] current profile's userdata directory.
+ Generally special://masterprofile for the master profile or
+ special://masterprofile/profiles/<profile_name> for other profiles.
+
+ NOTE: All these root directories are lowercase. Some of the sub-directories
+ might be mixed case.
+ */
+
+#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN)
+ std::string appPath;
+ std::string appName = CCompileInfo::GetAppName();
+ std::string dotLowerAppName = "." + appName;
+ StringUtils::ToLower(dotLowerAppName);
+ const char* envAppHome = "KODI_HOME";
+ const char* envAppBinHome = "KODI_BIN_HOME";
+ const char* envAppTemp = "KODI_TEMP";
+
+ std::string userHome;
+ if (getenv("KODI_DATA"))
+ userHome = getenv("KODI_DATA");
+ else if (getenv("HOME"))
+ {
+ userHome = getenv("HOME");
+ userHome.append("/" + dotLowerAppName);
+ }
+ else
+ {
+ userHome = "/root";
+ userHome.append("/" + dotLowerAppName);
+ }
+
+ std::string strTempPath;
+ if (getenv(envAppTemp))
+ strTempPath = getenv(envAppTemp);
+ else
+ strTempPath = userHome + "/temp";
+
+
+ std::string binaddonAltDir;
+ if (getenv("KODI_BINADDON_PATH"))
+ binaddonAltDir = getenv("KODI_BINADDON_PATH");
+
+ auto appBinPath = CUtil::GetHomePath(envAppBinHome);
+ // overridden by user
+ if (getenv(envAppHome))
+ appPath = getenv(envAppHome);
+ else
+ {
+ // use build time default
+ appPath = INSTALL_PATH;
+ /* Check if binaries and arch independent data files are being kept in
+ * separate locations. */
+ if (!XFILE::CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "userdata")))
+ {
+ /* Attempt to locate arch independent data files. */
+ appPath = CUtil::GetHomePath(appBinPath);
+ if (!XFILE::CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "userdata")))
+ {
+ fprintf(stderr, "Unable to find path to %s data files!\n", appName.c_str());
+ exit(1);
+ }
+ }
+ }
+
+ /* Set some environment variables */
+ setenv(envAppBinHome, appBinPath.c_str(), 0);
+ setenv(envAppHome, appPath.c_str(), 0);
+
+ if (bPlatformDirectories)
+ {
+ // map our special drives
+ CSpecialProtocol::SetXBMCBinPath(appBinPath);
+ CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
+ CSpecialProtocol::SetXBMCPath(appPath);
+ CSpecialProtocol::SetHomePath(userHome);
+ CSpecialProtocol::SetMasterProfilePath(userHome + "/userdata");
+ CSpecialProtocol::SetTempPath(strTempPath);
+ CSpecialProtocol::SetLogPath(strTempPath);
+
+ CreateUserDirs();
+
+ }
+ else
+ {
+ URIUtils::AddSlashAtEnd(appPath);
+
+ CSpecialProtocol::SetXBMCBinPath(appBinPath);
+ CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
+ CSpecialProtocol::SetXBMCPath(appPath);
+ CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data"));
+ CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata"));
+
+ std::string strTempPath = appPath;
+ strTempPath = URIUtils::AddFileToFolder(strTempPath, "portable_data/temp");
+ if (getenv(envAppTemp))
+ strTempPath = getenv(envAppTemp);
+ CSpecialProtocol::SetTempPath(strTempPath);
+ CSpecialProtocol::SetLogPath(strTempPath);
+ CreateUserDirs();
+ }
+ CSpecialProtocol::SetXBMCBinAddonPath(appBinPath + "/addons");
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool CSettingsComponent::InitDirectoriesOSX(bool bPlatformDirectories)
+{
+#if defined(TARGET_DARWIN)
+ std::string userHome;
+ if (getenv("HOME"))
+ userHome = getenv("HOME");
+ else
+ userHome = "/root";
+
+ std::string binaddonAltDir;
+ if (getenv("KODI_BINADDON_PATH"))
+ binaddonAltDir = getenv("KODI_BINADDON_PATH");
+
+ std::string appPath = CUtil::GetHomePath();
+ setenv("KODI_HOME", appPath.c_str(), 0);
+
+#if defined(TARGET_DARWIN_EMBEDDED)
+ std::string fontconfigPath;
+ fontconfigPath = appPath + "/system/players/VideoPlayer/etc/fonts/fonts.conf";
+ setenv("FONTCONFIG_FILE", fontconfigPath.c_str(), 0);
+#endif
+
+ // setup path to our internal dylibs so loader can find them
+ std::string frameworksPath = CUtil::GetFrameworksPath();
+ CSpecialProtocol::SetXBMCFrameworksPath(frameworksPath);
+
+ if (bPlatformDirectories)
+ {
+ // map our special drives
+ CSpecialProtocol::SetXBMCBinPath(appPath);
+ CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
+ CSpecialProtocol::SetXBMCPath(appPath);
+#if defined(TARGET_DARWIN_EMBEDDED)
+ std::string appName = CCompileInfo::GetAppName();
+ CSpecialProtocol::SetHomePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() + "/" +
+ appName);
+ CSpecialProtocol::SetMasterProfilePath(userHome + "/" + CDarwinEmbedUtils::GetAppRootFolder() +
+ "/" + appName + "/userdata");
+#else
+ std::string appName = CCompileInfo::GetAppName();
+ CSpecialProtocol::SetHomePath(userHome + "/Library/Application Support/" + appName);
+ CSpecialProtocol::SetMasterProfilePath(userHome + "/Library/Application Support/" + appName + "/userdata");
+#endif
+
+ std::string dotLowerAppName = "." + appName;
+ StringUtils::ToLower(dotLowerAppName);
+ // location for temp files
+#if defined(TARGET_DARWIN_EMBEDDED)
+ std::string strTempPath = URIUtils::AddFileToFolder(
+ userHome, std::string(CDarwinEmbedUtils::GetAppRootFolder()) + "/" + appName + "/temp");
+#else
+ std::string strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/");
+ XFILE::CDirectory::Create(strTempPath);
+ strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/temp");
+#endif
+ CSpecialProtocol::SetTempPath(strTempPath);
+
+ // xbmc.log file location
+#if defined(TARGET_DARWIN_EMBEDDED)
+ strTempPath = userHome + "/" + std::string(CDarwinEmbedUtils::GetAppRootFolder());
+#else
+ strTempPath = userHome + "/Library/Logs";
+#endif
+ CSpecialProtocol::SetLogPath(strTempPath);
+ CreateUserDirs();
+ }
+ else
+ {
+ URIUtils::AddSlashAtEnd(appPath);
+
+ CSpecialProtocol::SetXBMCBinPath(appPath);
+ CSpecialProtocol::SetXBMCAltBinAddonPath(binaddonAltDir);
+ CSpecialProtocol::SetXBMCPath(appPath);
+ CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data"));
+ CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata"));
+
+ std::string strTempPath = URIUtils::AddFileToFolder(appPath, "portable_data/temp");
+ CSpecialProtocol::SetTempPath(strTempPath);
+ CSpecialProtocol::SetLogPath(strTempPath);
+ CreateUserDirs();
+ }
+ CSpecialProtocol::SetXBMCBinAddonPath(appPath + "/addons");
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool CSettingsComponent::InitDirectoriesWin32(bool bPlatformDirectories)
+{
+#ifdef TARGET_WINDOWS
+ std::string xbmcPath = CUtil::GetHomePath();
+ CEnvironment::setenv("KODI_HOME", xbmcPath);
+ CSpecialProtocol::SetXBMCBinPath(xbmcPath);
+ CSpecialProtocol::SetXBMCPath(xbmcPath);
+ CSpecialProtocol::SetXBMCBinAddonPath(xbmcPath + "/addons");
+
+ std::string strWin32UserFolder = CWIN32Util::GetProfilePath(bPlatformDirectories);
+ CSpecialProtocol::SetLogPath(strWin32UserFolder);
+ CSpecialProtocol::SetHomePath(strWin32UserFolder);
+ CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(strWin32UserFolder, "userdata"));
+ CSpecialProtocol::SetTempPath(URIUtils::AddFileToFolder(strWin32UserFolder,"cache"));
+
+ CEnvironment::setenv("KODI_PROFILE_USERDATA", CSpecialProtocol::TranslatePath("special://masterprofile/"));
+
+ CreateUserDirs();
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+void CSettingsComponent::CreateUserDirs() const
+{
+ XFILE::CDirectory::Create("special://home/");
+ XFILE::CDirectory::Create("special://home/addons");
+ XFILE::CDirectory::Create("special://home/addons/packages");
+ XFILE::CDirectory::Create("special://home/addons/temp");
+ XFILE::CDirectory::Create("special://home/media");
+ XFILE::CDirectory::Create("special://home/system");
+ XFILE::CDirectory::Create("special://masterprofile/");
+ XFILE::CDirectory::Create("special://temp/");
+ XFILE::CDirectory::Create("special://logpath");
+ XFILE::CDirectory::Create("special://temp/temp"); // temp directory for python and dllGetTempPathA
+
+ //Let's clear our archive cache before starting up anything more
+ auto archiveCachePath = CSpecialProtocol::TranslatePath("special://temp/archive_cache/");
+ if (XFILE::CDirectory::Exists(archiveCachePath))
+ if (!XFILE::CDirectory::RemoveRecursive(archiveCachePath))
+ CLog::Log(LOGWARNING, "Failed to remove the archive cache at {}", archiveCachePath);
+ XFILE::CDirectory::Create(archiveCachePath);
+
+}
diff --git a/xbmc/settings/SettingsComponent.h b/xbmc/settings/SettingsComponent.h
new file mode 100644
index 0000000..42bb9a8
--- /dev/null
+++ b/xbmc/settings/SettingsComponent.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <memory>
+
+class CAdvancedSettings;
+class CProfileManager;
+class CSettings;
+
+namespace KODI
+{
+namespace SUBTITLES
+{
+class CSubtitlesSettings;
+} // namespace SUBTITLES
+} // namespace KODI
+
+class CSettingsComponent
+{
+public:
+ CSettingsComponent();
+ virtual ~CSettingsComponent();
+
+ /*!
+ * @brief Initialize all subcomponents with system default values (loaded from code, system settings files, ...).
+ */
+ void Initialize();
+
+ /*!
+ * @brief Initialize all subcomponents with user values (loaded from user settings files, according to active profile).
+ * @return true on success, false otherwise.
+ */
+ bool Load();
+
+ /*!
+ * @brief Deinitialize all subcomponents.
+ */
+ void Deinitialize();
+
+ /*!
+ * @brief Get access to the settings subcomponent.
+ * @return the settings subcomponent.
+ */
+ std::shared_ptr<CSettings> GetSettings();
+
+ /*!
+ * @brief Get access to the advanced settings subcomponent.
+ * @return the advanced settings subcomponent.
+ */
+ std::shared_ptr<CAdvancedSettings> GetAdvancedSettings();
+
+ /*!
+ * @brief Get access to the subtitles settings subcomponent.
+ * @return the subtiltles settings subcomponent.
+ */
+ std::shared_ptr<KODI::SUBTITLES::CSubtitlesSettings> GetSubtitlesSettings();
+
+ /*!
+ * @brief Get access to the profiles manager subcomponent.
+ * @return the profiles manager subcomponent.
+ */
+ std::shared_ptr<CProfileManager> GetProfileManager();
+
+private:
+ bool InitDirectoriesLinux(bool bPlatformDirectories);
+ bool InitDirectoriesOSX(bool bPlatformDirectories);
+ bool InitDirectoriesWin32(bool bPlatformDirectories);
+ void CreateUserDirs() const;
+
+ enum class State
+ {
+ DEINITED,
+ INITED,
+ LOADED
+ };
+ State m_state = State::DEINITED;
+
+ std::shared_ptr<CSettings> m_settings;
+ std::shared_ptr<CAdvancedSettings> m_advancedSettings;
+ std::shared_ptr<KODI::SUBTITLES::CSubtitlesSettings> m_subtitlesSettings;
+ std::shared_ptr<CProfileManager> m_profileManager;
+};
diff --git a/xbmc/settings/SettingsValueFlatJsonSerializer.cpp b/xbmc/settings/SettingsValueFlatJsonSerializer.cpp
new file mode 100644
index 0000000..736c37d
--- /dev/null
+++ b/xbmc/settings/SettingsValueFlatJsonSerializer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 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 "SettingsValueFlatJsonSerializer.h"
+
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/lib/SettingType.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/JSONVariantWriter.h"
+#include "utils/log.h"
+
+CSettingsValueFlatJsonSerializer::CSettingsValueFlatJsonSerializer(bool compact /* = true */)
+ : m_compact(compact)
+{ }
+
+std::string CSettingsValueFlatJsonSerializer::SerializeValues(
+ const CSettingsManager* settingsManager) const
+{
+ if (settingsManager == nullptr)
+ return "";
+
+ CVariant root(CVariant::VariantTypeObject);
+
+ const auto sections = settingsManager->GetSections();
+ for (const auto& section : sections)
+ SerializeSection(root, section);
+
+ std::string result;
+ if (!CJSONVariantWriter::Write(root, result, m_compact))
+ {
+ CLog::Log(LOGWARNING,
+ "CSettingsValueFlatJsonSerializer: failed to serialize settings into JSON");
+ return "";
+ }
+
+ return result;
+}
+
+void CSettingsValueFlatJsonSerializer::SerializeSection(
+ CVariant& parent, const std::shared_ptr<CSettingSection>& section) const
+{
+ if (section == nullptr)
+ return;
+
+ const auto categories = section->GetCategories();
+ for (const auto& category : categories)
+ SerializeCategory(parent, category);
+}
+
+void CSettingsValueFlatJsonSerializer::SerializeCategory(
+ CVariant& parent, const std::shared_ptr<CSettingCategory>& category) const
+{
+ if (category == nullptr)
+ return;
+
+ const auto groups = category->GetGroups();
+ for (const auto& group : groups)
+ SerializeGroup(parent, group);
+}
+
+void CSettingsValueFlatJsonSerializer::SerializeGroup(
+ CVariant& parent, const std::shared_ptr<CSettingGroup>& group) const
+{
+ if (group == nullptr)
+ return;
+
+ const auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ SerializeSetting(parent, setting);
+}
+
+void CSettingsValueFlatJsonSerializer::SerializeSetting(
+ CVariant& parent, const std::shared_ptr<CSetting>& setting) const
+{
+ if (setting == nullptr)
+ return;
+
+ // ignore references and action settings (which don't have a value)
+ if (setting->IsReference() || setting->GetType() == SettingType::Action)
+ return;
+
+ const auto valueObj = SerializeSettingValue(setting);
+ if (valueObj.isNull())
+ return;
+
+ parent[setting->GetId()] = valueObj;
+}
+
+CVariant CSettingsValueFlatJsonSerializer::SerializeSettingValue(
+ const std::shared_ptr<CSetting>& setting) const
+{
+ switch (setting->GetType())
+ {
+ case SettingType::Action:
+ return CVariant::ConstNullVariant;
+
+ case SettingType::Boolean:
+ return CVariant(std::static_pointer_cast<CSettingBool>(setting)->GetValue());
+
+ case SettingType::Integer:
+ return CVariant(std::static_pointer_cast<CSettingInt>(setting)->GetValue());
+
+ case SettingType::Number:
+ return CVariant(std::static_pointer_cast<CSettingNumber>(setting)->GetValue());
+
+ case SettingType::String:
+ return CVariant(std::static_pointer_cast<CSettingString>(setting)->GetValue());
+
+ case SettingType::List:
+ {
+ const auto settingList = std::static_pointer_cast<CSettingList>(setting);
+
+ CVariant settingListValuesObj(CVariant::VariantTypeArray);
+ const auto settingListValues = settingList->GetValue();
+ for (const auto& settingListValue : settingListValues)
+ {
+ const auto valueObj = SerializeSettingValue(settingListValue);
+ if (!valueObj.isNull())
+ settingListValuesObj.push_back(valueObj);
+ }
+
+ return settingListValuesObj;
+ }
+
+ case SettingType::Unknown:
+ default:
+ CLog::Log(LOGWARNING,
+ "CSettingsValueFlatJsonSerializer: failed to serialize setting \"{}\" with value \"{}\" " \
+ "of unknown type", setting->GetId(), setting->ToString());
+ return CVariant::ConstNullVariant;
+ }
+}
diff --git a/xbmc/settings/SettingsValueFlatJsonSerializer.h b/xbmc/settings/SettingsValueFlatJsonSerializer.h
new file mode 100644
index 0000000..05214ab
--- /dev/null
+++ b/xbmc/settings/SettingsValueFlatJsonSerializer.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingsValueSerializer.h"
+#include "utils/Variant.h"
+
+#include <memory>
+
+class CSetting;
+class CSettingCategory;
+class CSettingGroup;
+class CSettingSection;
+
+class CSettingsValueFlatJsonSerializer : public ISettingsValueSerializer
+{
+public:
+ explicit CSettingsValueFlatJsonSerializer(bool compact = true);
+ ~CSettingsValueFlatJsonSerializer() = default;
+
+ void SetCompact(bool compact = true) { m_compact = compact; }
+
+ // implementation of ISettingsValueSerializer
+ std::string SerializeValues(const CSettingsManager* settingsManager) const override;
+
+private:
+ void SerializeSection(CVariant& parent, const std::shared_ptr<CSettingSection>& section) const;
+ void SerializeCategory(CVariant& parent, const std::shared_ptr<CSettingCategory>& category) const;
+ void SerializeGroup(CVariant& parent, const std::shared_ptr<CSettingGroup>& group) const;
+ void SerializeSetting(CVariant& parent, const std::shared_ptr<CSetting>& setting) const;
+ CVariant SerializeSettingValue(const std::shared_ptr<CSetting>& setting) const;
+
+ bool m_compact;
+};
diff --git a/xbmc/settings/SettingsValueXmlSerializer.cpp b/xbmc/settings/SettingsValueXmlSerializer.cpp
new file mode 100644
index 0000000..42f83e3
--- /dev/null
+++ b/xbmc/settings/SettingsValueXmlSerializer.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 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 "SettingsValueXmlSerializer.h"
+
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+static constexpr char SETTINGS_XML_ROOT[] = "settings";
+
+std::string CSettingsValueXmlSerializer::SerializeValues(
+ const CSettingsManager* settingsManager) const
+{
+ if (settingsManager == nullptr)
+ return "";
+
+ CXBMCTinyXML xmlDoc;
+ TiXmlElement rootElement(SETTINGS_XML_ROOT);
+ rootElement.SetAttribute(SETTING_XML_ROOT_VERSION, settingsManager->GetVersion());
+ TiXmlNode* xmlRoot = xmlDoc.InsertEndChild(rootElement);
+ if (xmlRoot == nullptr)
+ return "";
+
+ const auto sections = settingsManager->GetSections();
+ for (const auto& section : sections)
+ SerializeSection(xmlRoot, section);
+
+ std::stringstream stream;
+ stream << *xmlDoc.RootElement();
+
+ return stream.str();
+}
+
+void CSettingsValueXmlSerializer::SerializeSection(
+ TiXmlNode* parent, const std::shared_ptr<CSettingSection>& section) const
+{
+ if (section == nullptr)
+ return;
+
+ const auto categories = section->GetCategories();
+ for (const auto& category : categories)
+ SerializeCategory(parent, category);
+}
+
+void CSettingsValueXmlSerializer::SerializeCategory(
+ TiXmlNode* parent, const std::shared_ptr<CSettingCategory>& category) const
+{
+ if (category == nullptr)
+ return;
+
+ const auto groups = category->GetGroups();
+ for (const auto& group : groups)
+ SerializeGroup(parent, group);
+}
+
+void CSettingsValueXmlSerializer::SerializeGroup(TiXmlNode* parent,
+ const std::shared_ptr<CSettingGroup>& group) const
+{
+ if (group == nullptr)
+ return;
+
+ const auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ SerializeSetting(parent, setting);
+}
+
+void CSettingsValueXmlSerializer::SerializeSetting(TiXmlNode* parent,
+ const std::shared_ptr<CSetting>& setting) const
+{
+ if (setting == nullptr)
+ return;
+
+ // ignore references and action settings (which don't have a value)
+ if (setting->IsReference() || setting->GetType() == SettingType::Action)
+ return;
+
+ TiXmlElement settingElement(SETTING_XML_ELM_SETTING);
+ settingElement.SetAttribute(SETTING_XML_ATTR_ID, setting->GetId());
+
+ // add the default attribute
+ if (setting->IsDefault())
+ settingElement.SetAttribute(SETTING_XML_ELM_DEFAULT, "true");
+
+ // add the value
+ TiXmlText value(setting->ToString());
+ settingElement.InsertEndChild(value);
+
+ if (parent->InsertEndChild(settingElement) == nullptr)
+ CLog::Log(LOGWARNING,
+ "CSettingsValueXmlSerializer: unable to write <" SETTING_XML_ELM_SETTING " id=\"{}\"> tag",
+ setting->GetId());
+}
diff --git a/xbmc/settings/SettingsValueXmlSerializer.h b/xbmc/settings/SettingsValueXmlSerializer.h
new file mode 100644
index 0000000..010c036
--- /dev/null
+++ b/xbmc/settings/SettingsValueXmlSerializer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingsValueSerializer.h"
+
+#include <memory>
+
+class CSetting;
+class CSettingCategory;
+class CSettingGroup;
+class CSettingSection;
+class TiXmlNode;
+
+class CSettingsValueXmlSerializer : public ISettingsValueSerializer
+{
+public:
+ CSettingsValueXmlSerializer() = default;
+ ~CSettingsValueXmlSerializer() = default;
+
+ // implementation of ISettingsValueSerializer
+ std::string SerializeValues(const CSettingsManager* settingsManager) const override;
+
+private:
+ void SerializeSection(TiXmlNode* parent, const std::shared_ptr<CSettingSection>& section) const;
+ void SerializeCategory(TiXmlNode* parent,
+ const std::shared_ptr<CSettingCategory>& category) const;
+ void SerializeGroup(TiXmlNode* parent, const std::shared_ptr<CSettingGroup>& group) const;
+ void SerializeSetting(TiXmlNode* parent, const std::shared_ptr<CSetting>& setting) const;
+};
diff --git a/xbmc/settings/SkinSettings.cpp b/xbmc/settings/SkinSettings.cpp
new file mode 100644
index 0000000..71ff616
--- /dev/null
+++ b/xbmc/settings/SkinSettings.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013-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 "SkinSettings.h"
+
+#include "GUIInfoManager.h"
+#include "ServiceBroker.h"
+#include "addons/Skin.h"
+#include "guilib/GUIComponent.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#define XML_SKINSETTINGS "skinsettings"
+
+CSkinSettings::CSkinSettings()
+{
+ Clear();
+}
+
+CSkinSettings::~CSkinSettings() = default;
+
+CSkinSettings& CSkinSettings::GetInstance()
+{
+ static CSkinSettings sSkinSettings;
+ return sSkinSettings;
+}
+
+int CSkinSettings::TranslateString(const std::string &setting)
+{
+ return g_SkinInfo->TranslateString(setting);
+}
+
+const std::string& CSkinSettings::GetString(int setting) const
+{
+ return g_SkinInfo->GetString(setting);
+}
+
+void CSkinSettings::SetString(int setting, const std::string &label)
+{
+ g_SkinInfo->SetString(setting, label);
+}
+
+int CSkinSettings::TranslateBool(const std::string &setting)
+{
+ return g_SkinInfo->TranslateBool(setting);
+}
+
+bool CSkinSettings::GetBool(int setting) const
+{
+ return g_SkinInfo->GetBool(setting);
+}
+
+int CSkinSettings::GetInt(int setting) const
+{
+ return g_SkinInfo->GetInt(setting);
+}
+
+void CSkinSettings::SetBool(int setting, bool set)
+{
+ g_SkinInfo->SetBool(setting, set);
+}
+
+void CSkinSettings::Reset(const std::string &setting)
+{
+ g_SkinInfo->Reset(setting);
+}
+
+std::set<ADDON::CSkinSettingPtr> CSkinSettings::GetSettings() const
+{
+ return g_SkinInfo->GetSkinSettings();
+}
+
+ADDON::CSkinSettingPtr CSkinSettings::GetSetting(const std::string& settingId)
+{
+ return g_SkinInfo->GetSkinSetting(settingId);
+}
+
+std::shared_ptr<const ADDON::CSkinSetting> CSkinSettings::GetSetting(
+ const std::string& settingId) const
+{
+ return g_SkinInfo->GetSkinSetting(settingId);
+}
+
+void CSkinSettings::Reset()
+{
+ g_SkinInfo->Reset();
+
+ CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager();
+ infoMgr.ResetCache();
+ infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache();
+}
+
+bool CSkinSettings::Load(const TiXmlNode *settings)
+{
+ if (settings == nullptr)
+ return false;
+
+ const TiXmlElement *rootElement = settings->FirstChildElement(XML_SKINSETTINGS);
+
+ // return true in the case skinsettings is missing. It just means that
+ // it's been migrated and it's not an error
+ if (rootElement == nullptr)
+ {
+ CLog::Log(LOGDEBUG, "CSkinSettings: no <skinsettings> tag found");
+ return true;
+ }
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_settings.clear();
+ m_settings = ADDON::CSkinInfo::ParseSettings(rootElement);
+
+ return true;
+}
+
+bool CSkinSettings::Save(TiXmlNode *settings) const
+{
+ if (settings == nullptr)
+ return false;
+
+ // nothing to do here because skin settings saving has been migrated to CSkinInfo
+
+ return true;
+}
+
+void CSkinSettings::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_settings.clear();
+}
+
+void CSkinSettings::MigrateSettings(const std::shared_ptr<ADDON::CSkinInfo>& skin)
+{
+ if (skin == nullptr)
+ return;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ bool settingsMigrated = false;
+ const std::string& skinId = skin->ID();
+ std::set<ADDON::CSkinSettingPtr> settingsCopy(m_settings.begin(), m_settings.end());
+ for (const auto& setting : settingsCopy)
+ {
+ if (!StringUtils::StartsWith(setting->name, skinId + "."))
+ continue;
+
+ std::string settingName = setting->name.substr(skinId.size() + 1);
+
+ if (setting->GetType() == "string")
+ {
+ int settingNumber = skin->TranslateString(settingName);
+ if (settingNumber >= 0)
+ skin->SetString(settingNumber, std::dynamic_pointer_cast<ADDON::CSkinSettingString>(setting)->value);
+ }
+ else if (setting->GetType() == "bool")
+ {
+ int settingNumber = skin->TranslateBool(settingName);
+ if (settingNumber >= 0)
+ skin->SetBool(settingNumber, std::dynamic_pointer_cast<ADDON::CSkinSettingBool>(setting)->value);
+ }
+
+ m_settings.erase(setting);
+ settingsMigrated = true;
+ }
+
+ if (settingsMigrated)
+ {
+ // save the skin's settings
+ skin->SaveSettings();
+
+ // save the guisettings.xml
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ }
+}
+
diff --git a/xbmc/settings/SkinSettings.h b/xbmc/settings/SkinSettings.h
new file mode 100644
index 0000000..9ade245
--- /dev/null
+++ b/xbmc/settings/SkinSettings.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "addons/Skin.h"
+#include "settings/ISubSettings.h"
+#include "threads/CriticalSection.h"
+
+#include <memory>
+#include <set>
+#include <string>
+
+class TiXmlNode;
+
+class CSkinSettings : public ISubSettings
+{
+public:
+ static CSkinSettings& GetInstance();
+
+ bool Load(const TiXmlNode *settings) override;
+ bool Save(TiXmlNode *settings) const override;
+ void Clear() override;
+
+ void MigrateSettings(const std::shared_ptr<ADDON::CSkinInfo>& skin);
+
+ int TranslateString(const std::string &setting);
+ const std::string& GetString(int setting) const;
+ void SetString(int setting, const std::string &label);
+
+ int TranslateBool(const std::string &setting);
+ bool GetBool(int setting) const;
+ void SetBool(int setting, bool set);
+
+ /*! \brief Get the skin setting value as an integer value
+ * \param setting - the setting id
+ * \return the setting value as an integer, -1 if no conversion is possible
+ */
+ int GetInt(int setting) const;
+
+ std::set<ADDON::CSkinSettingPtr> GetSettings() const;
+ ADDON::CSkinSettingPtr GetSetting(const std::string& settingId);
+ std::shared_ptr<const ADDON::CSkinSetting> GetSetting(const std::string& settingId) const;
+
+ void Reset(const std::string &setting);
+ void Reset();
+
+protected:
+ CSkinSettings();
+ CSkinSettings(const CSkinSettings&) = delete;
+ CSkinSettings& operator=(CSkinSettings const&) = delete;
+ ~CSkinSettings() override;
+
+private:
+ CCriticalSection m_critical;
+ std::set<ADDON::CSkinSettingPtr> m_settings;
+};
diff --git a/xbmc/settings/SubtitlesSettings.cpp b/xbmc/settings/SubtitlesSettings.cpp
new file mode 100644
index 0000000..66ef636
--- /dev/null
+++ b/xbmc/settings/SubtitlesSettings.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012-2021 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 "SubtitlesSettings.h"
+
+#include "guilib/GUIFontManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/Settings.h"
+#include "settings/lib/Setting.h"
+#include "utils/FileUtils.h"
+#include "utils/FontUtils.h"
+#include "utils/URIUtils.h"
+
+using namespace KODI;
+using namespace SUBTITLES;
+
+CSubtitlesSettings::CSubtitlesSettings(const std::shared_ptr<CSettings>& settings)
+ : m_settings(settings)
+{
+ m_settings->RegisterCallback(
+ this,
+ {CSettings::SETTING_LOCALE_SUBTITLELANGUAGE, CSettings::SETTING_SUBTITLES_PARSECAPTIONS,
+ CSettings::SETTING_SUBTITLES_ALIGN, CSettings::SETTING_SUBTITLES_STEREOSCOPICDEPTH,
+ CSettings::SETTING_SUBTITLES_FONTNAME, CSettings::SETTING_SUBTITLES_FONTSIZE,
+ CSettings::SETTING_SUBTITLES_STYLE, CSettings::SETTING_SUBTITLES_COLOR,
+ CSettings::SETTING_SUBTITLES_BORDERSIZE, CSettings::SETTING_SUBTITLES_BORDERCOLOR,
+ CSettings::SETTING_SUBTITLES_OPACITY, CSettings::SETTING_SUBTITLES_BGCOLOR,
+ CSettings::SETTING_SUBTITLES_BGOPACITY, CSettings::SETTING_SUBTITLES_BLUR,
+ CSettings::SETTING_SUBTITLES_BACKGROUNDTYPE, CSettings::SETTING_SUBTITLES_SHADOWCOLOR,
+ CSettings::SETTING_SUBTITLES_SHADOWOPACITY, CSettings::SETTING_SUBTITLES_SHADOWSIZE,
+ CSettings::SETTING_SUBTITLES_MARGINVERTICAL, CSettings::SETTING_SUBTITLES_CHARSET,
+ CSettings::SETTING_SUBTITLES_OVERRIDEFONTS, CSettings::SETTING_SUBTITLES_OVERRIDESTYLES,
+ CSettings::SETTING_SUBTITLES_LANGUAGES, CSettings::SETTING_SUBTITLES_STORAGEMODE,
+ CSettings::SETTING_SUBTITLES_CUSTOMPATH, CSettings::SETTING_SUBTITLES_PAUSEONSEARCH,
+ CSettings::SETTING_SUBTITLES_DOWNLOADFIRST, CSettings::SETTING_SUBTITLES_TV,
+ CSettings::SETTING_SUBTITLES_MOVIE});
+}
+
+CSubtitlesSettings::~CSubtitlesSettings()
+{
+ m_settings->UnregisterCallback(this);
+}
+
+void CSubtitlesSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ SetChanged();
+ NotifyObservers(ObservableMessageSettingsChanged);
+ if (setting->GetId() == CSettings::SETTING_SUBTITLES_ALIGN)
+ {
+ SetChanged();
+ NotifyObservers(ObservableMessagePositionChanged);
+ }
+}
+
+Align CSubtitlesSettings::GetAlignment()
+{
+ return static_cast<Align>(m_settings->GetInt(CSettings::SETTING_SUBTITLES_ALIGN));
+}
+
+void CSubtitlesSettings::SetAlignment(Align align)
+{
+ m_settings->SetInt(CSettings::SETTING_SUBTITLES_ALIGN, static_cast<int>(align));
+}
+
+HorizontalAlign CSubtitlesSettings::GetHorizontalAlignment()
+{
+ return static_cast<HorizontalAlign>(
+ m_settings->GetInt(CSettings::SETTING_SUBTITLES_CAPTIONSALIGN));
+}
+
+std::string CSubtitlesSettings::GetFontName()
+{
+ return m_settings->GetString(CSettings::SETTING_SUBTITLES_FONTNAME);
+}
+
+FontStyle CSubtitlesSettings::GetFontStyle()
+{
+ return static_cast<FontStyle>(m_settings->GetInt(CSettings::SETTING_SUBTITLES_STYLE));
+}
+
+int CSubtitlesSettings::GetFontSize()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_FONTSIZE);
+}
+
+UTILS::COLOR::Color CSubtitlesSettings::GetFontColor()
+{
+ return UTILS::COLOR::ConvertHexToColor(m_settings->GetString(CSettings::SETTING_SUBTITLES_COLOR));
+}
+
+int CSubtitlesSettings::GetFontOpacity()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_OPACITY);
+}
+
+int CSubtitlesSettings::GetBorderSize()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_BORDERSIZE);
+}
+
+UTILS::COLOR::Color CSubtitlesSettings::GetBorderColor()
+{
+ return UTILS::COLOR::ConvertHexToColor(
+ m_settings->GetString(CSettings::SETTING_SUBTITLES_BORDERCOLOR));
+}
+
+int CSubtitlesSettings::GetShadowSize()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_SHADOWSIZE);
+}
+
+UTILS::COLOR::Color CSubtitlesSettings::GetShadowColor()
+{
+ return UTILS::COLOR::ConvertHexToColor(
+ m_settings->GetString(CSettings::SETTING_SUBTITLES_SHADOWCOLOR));
+}
+
+int CSubtitlesSettings::GetShadowOpacity()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_SHADOWOPACITY);
+}
+
+int CSubtitlesSettings::GetBlurSize()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_BLUR);
+}
+
+BackgroundType CSubtitlesSettings::GetBackgroundType()
+{
+ return static_cast<BackgroundType>(
+ m_settings->GetInt(CSettings::SETTING_SUBTITLES_BACKGROUNDTYPE));
+}
+
+UTILS::COLOR::Color CSubtitlesSettings::GetBackgroundColor()
+{
+ return UTILS::COLOR::ConvertHexToColor(
+ m_settings->GetString(CSettings::SETTING_SUBTITLES_BGCOLOR));
+}
+
+int CSubtitlesSettings::GetBackgroundOpacity()
+{
+ return m_settings->GetInt(CSettings::SETTING_SUBTITLES_BGOPACITY);
+}
+
+bool CSubtitlesSettings::IsOverrideFonts()
+{
+ return m_settings->GetBool(CSettings::SETTING_SUBTITLES_OVERRIDEFONTS);
+}
+
+OverrideStyles CSubtitlesSettings::GetOverrideStyles()
+{
+ return static_cast<OverrideStyles>(
+ m_settings->GetInt(CSettings::SETTING_SUBTITLES_OVERRIDESTYLES));
+}
+
+float CSubtitlesSettings::GetVerticalMarginPerc()
+{
+ // We return the vertical margin as percentage
+ // to fit the current screen resolution
+ return static_cast<float>(m_settings->GetNumber(CSettings::SETTING_SUBTITLES_MARGINVERTICAL));
+}
+
+void CSubtitlesSettings::SettingOptionsSubtitleFontsFiller(const SettingConstPtr& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data)
+{
+ // From application system fonts folder we add the default font only
+ std::string defaultFontPath =
+ URIUtils::AddFileToFolder("special://xbmc/media/Fonts/", UTILS::FONT::FONT_DEFAULT_FILENAME);
+ if (CFileUtils::Exists(defaultFontPath))
+ {
+ std::string familyName = UTILS::FONT::GetFontFamily(defaultFontPath);
+ if (!familyName.empty())
+ {
+ list.emplace_back(g_localizeStrings.Get(571) + " " + familyName, FONT_DEFAULT_FAMILYNAME);
+ }
+ }
+ // Add additionals fonts from the user fonts folder
+ for (const std::string& familyName : g_fontManager.GetUserFontsFamilyNames())
+ {
+ list.emplace_back(familyName, familyName);
+ }
+}
diff --git a/xbmc/settings/SubtitlesSettings.h b/xbmc/settings/SubtitlesSettings.h
new file mode 100644
index 0000000..3927de1
--- /dev/null
+++ b/xbmc/settings/SubtitlesSettings.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2012-2021 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.
+ */
+
+#pragma once
+
+#include "settings/lib/ISettingCallback.h"
+#include "utils/ColorUtils.h"
+#include "utils/Observer.h"
+
+#include <memory>
+#include <string>
+
+class CSetting;
+class CSettings;
+struct StringSettingOption;
+
+namespace KODI
+{
+namespace SUBTITLES
+{
+// This is a placeholder to keep the fontname setting valid
+// even if the default app font could be changed
+constexpr const char* FONT_DEFAULT_FAMILYNAME = "DEFAULT";
+
+enum class Align
+{
+ MANUAL = 0,
+ BOTTOM_INSIDE,
+ BOTTOM_OUTSIDE,
+ TOP_INSIDE,
+ TOP_OUTSIDE
+};
+
+enum class HorizontalAlign
+{
+ LEFT = 0,
+ CENTER,
+ RIGHT
+};
+
+enum class BackgroundType
+{
+ NONE = 0,
+ SHADOW,
+ BOX,
+ SQUAREBOX
+};
+
+enum class FontStyle
+{
+ NORMAL = 0,
+ BOLD,
+ ITALIC,
+ BOLD_ITALIC
+};
+
+enum class OverrideStyles
+{
+ DISABLED = 0,
+ POSITIONS,
+ STYLES,
+ STYLES_POSITIONS
+};
+
+class CSubtitlesSettings : public ISettingCallback, public Observable
+{
+public:
+ explicit CSubtitlesSettings(const std::shared_ptr<CSettings>& settings);
+ ~CSubtitlesSettings() override;
+
+ // Inherited from ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+
+ /*!
+ * \brief Get subtitle alignment
+ * \return The alignment
+ */
+ Align GetAlignment();
+
+ /*!
+ * \brief Set the subtitle alignment
+ * \param align The alignment
+ */
+ void SetAlignment(Align align);
+
+ /*!
+ * \brief Get horizontal text alignment
+ * \return The alignment
+ */
+ HorizontalAlign GetHorizontalAlignment();
+
+ /*!
+ * \brief Get font name
+ * \return The font name
+ */
+ std::string GetFontName();
+
+ /*!
+ * \brief Get font style
+ * \return The font style
+ */
+ FontStyle GetFontStyle();
+
+ /*!
+ * \brief Get font size
+ * \return The font size in PX
+ */
+ int GetFontSize();
+
+ /*!
+ * \brief Get font color
+ * \return The font color
+ */
+ UTILS::COLOR::Color GetFontColor();
+
+ /*!
+ * \brief Get font opacity
+ * \return The font opacity in %
+ */
+ int GetFontOpacity();
+
+ /*!
+ * \brief Get border size
+ * \return The border size in %
+ */
+ int GetBorderSize();
+
+ /*!
+ * \brief Get border color
+ * \return The border color
+ */
+ UTILS::COLOR::Color GetBorderColor();
+
+ /*!
+ * \brief Get shadow size
+ * \return The shadow size in %
+ */
+ int GetShadowSize();
+
+ /*!
+ * \brief Get shadow color
+ * \return The shadow color
+ */
+ UTILS::COLOR::Color GetShadowColor();
+
+ /*!
+ * \brief Get shadow opacity
+ * \return The shadow opacity in %
+ */
+ int GetShadowOpacity();
+
+ /*!
+ * \brief Get blur size
+ * \return The blur size in %
+ */
+ int GetBlurSize();
+
+ /*!
+ * \brief Get background type
+ * \return The background type
+ */
+ BackgroundType GetBackgroundType();
+
+ /*!
+ * \brief Get background color
+ * \return The background color
+ */
+ UTILS::COLOR::Color GetBackgroundColor();
+
+ /*!
+ * \brief Get background opacity
+ * \return The background opacity in %
+ */
+ int GetBackgroundOpacity();
+
+ /*!
+ * \brief Check if font override is enabled
+ * \return True if fonts must be overriden, otherwise false
+ */
+ bool IsOverrideFonts();
+
+ /*!
+ * \brief Get override styles
+ * \return The styles to be overriden
+ */
+ OverrideStyles GetOverrideStyles();
+
+ /*!
+ * \brief Get the subtitle vertical margin
+ * \return The vertical margin in %
+ */
+ float GetVerticalMarginPerc();
+
+ static void SettingOptionsSubtitleFontsFiller(const std::shared_ptr<const CSetting>& setting,
+ std::vector<StringSettingOption>& list,
+ std::string& current,
+ void* data);
+
+private:
+ CSubtitlesSettings() = delete;
+
+ const std::shared_ptr<CSettings> m_settings;
+};
+
+} // namespace SUBTITLES
+} // namespace KODI
diff --git a/xbmc/settings/dialogs/CMakeLists.txt b/xbmc/settings/dialogs/CMakeLists.txt
new file mode 100644
index 0000000..631e86c
--- /dev/null
+++ b/xbmc/settings/dialogs/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(SOURCES GUIDialogContentSettings.cpp
+ GUIDialogLibExportSettings.cpp
+ GUIDialogSettingsBase.cpp
+ GUIDialogSettingsManagerBase.cpp
+ GUIDialogSettingsManualBase.cpp)
+
+set(HEADERS GUIDialogContentSettings.h
+ GUIDialogLibExportSettings.h
+ GUIDialogSettingsBase.h
+ GUIDialogSettingsManagerBase.h
+ GUIDialogSettingsManualBase.h)
+
+core_add_library(settings_dialogs)
diff --git a/xbmc/settings/dialogs/GUIDialogContentSettings.cpp b/xbmc/settings/dialogs/GUIDialogContentSettings.cpp
new file mode 100644
index 0000000..da4caf1
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogContentSettings.cpp
@@ -0,0 +1,428 @@
+/*
+ * 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 "GUIDialogContentSettings.h"
+
+#include "ServiceBroker.h"
+#include "addons/AddonManager.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "filesystem/AddonsDirectory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingsManager.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "utils/log.h"
+#include "video/VideoInfoScanner.h"
+
+#include <limits.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#define SETTING_CONTENT_TYPE "contenttype"
+#define SETTING_SCRAPER_LIST "scraperlist"
+#define SETTING_SCRAPER_SETTINGS "scrapersettings"
+#define SETTING_SCAN_RECURSIVE "scanrecursive"
+#define SETTING_USE_DIRECTORY_NAMES "usedirectorynames"
+#define SETTING_CONTAINS_SINGLE_ITEM "containssingleitem"
+#define SETTING_EXCLUDE "exclude"
+#define SETTING_NO_UPDATING "noupdating"
+#define SETTING_ALL_EXTERNAL_AUDIO "allexternalaudio"
+
+using namespace ADDON;
+
+
+CGUIDialogContentSettings::CGUIDialogContentSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_CONTENT_SETTINGS, "DialogSettings.xml")
+{ }
+
+void CGUIDialogContentSettings::SetContent(CONTENT_TYPE content)
+{
+ m_content = m_originalContent = content;
+}
+
+void CGUIDialogContentSettings::ResetContent()
+{
+ SetContent(CONTENT_NONE);
+}
+
+void CGUIDialogContentSettings::SetScanSettings(const VIDEO::SScanSettings &scanSettings)
+{
+ m_scanRecursive = (scanSettings.recurse > 0 && !scanSettings.parent_name) ||
+ (scanSettings.recurse > 1 && scanSettings.parent_name);
+ m_useDirectoryNames = scanSettings.parent_name;
+ m_exclude = scanSettings.exclude;
+ m_containsSingleItem = scanSettings.parent_name_root;
+ m_noUpdating = scanSettings.noupdate;
+ m_allExternalAudio = scanSettings.m_allExtAudio;
+}
+
+bool CGUIDialogContentSettings::Show(ADDON::ScraperPtr& scraper, CONTENT_TYPE content /* = CONTENT_NONE */)
+{
+ VIDEO::SScanSettings dummy;
+ return Show(scraper, dummy, content);
+}
+
+bool CGUIDialogContentSettings::Show(ADDON::ScraperPtr& scraper, VIDEO::SScanSettings& settings, CONTENT_TYPE content /* = CONTENT_NONE */)
+{
+ CGUIDialogContentSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContentSettings>(WINDOW_DIALOG_CONTENT_SETTINGS);
+ if (dialog == NULL)
+ return false;
+
+ if (scraper != NULL)
+ {
+ dialog->SetContent(content != CONTENT_NONE ? content : scraper->Content());
+ dialog->SetScraper(scraper);
+ // toast selected but disabled scrapers
+ if (CServiceBroker::GetAddonMgr().IsAddonDisabled(scraper->ID()))
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(24024), scraper->Name(), 2000, true);
+ }
+
+ dialog->SetScanSettings(settings);
+ dialog->Open();
+
+ bool confirmed = dialog->IsConfirmed();
+ if (confirmed)
+ {
+ scraper = dialog->GetScraper();
+ content = dialog->GetContent();
+
+ settings.m_allExtAudio = dialog->GetUseAllExternalAudio();
+
+ if (scraper == NULL || content == CONTENT_NONE)
+ settings.exclude = dialog->GetExclude();
+ else
+ {
+ settings.exclude = false;
+ settings.noupdate = dialog->GetNoUpdating();
+ scraper->SetPathSettings(content, "");
+
+ if (content == CONTENT_TVSHOWS)
+ {
+ settings.parent_name = settings.parent_name_root = dialog->GetContainsSingleItem();
+ settings.recurse = 0;
+ }
+ else if (content == CONTENT_MOVIES || content == CONTENT_MUSICVIDEOS)
+ {
+ if (dialog->GetUseDirectoryNames())
+ {
+ settings.parent_name = true;
+ settings.parent_name_root = false;
+ settings.recurse = dialog->GetScanRecursive() ? INT_MAX : 1;
+
+ if (dialog->GetContainsSingleItem())
+ {
+ settings.parent_name_root = true;
+ settings.recurse = 0;
+ }
+ }
+ else
+ {
+ settings.parent_name = false;
+ settings.parent_name_root = false;
+ settings.recurse = dialog->GetScanRecursive() ? INT_MAX : 0;
+ }
+ }
+ }
+ }
+
+ // now that we have evaluated all settings we need to reset the content
+ dialog->ResetContent();
+
+ return confirmed;
+}
+
+void CGUIDialogContentSettings::OnInitWindow()
+{
+ CGUIDialogSettingsManualBase::OnInitWindow();
+}
+
+void CGUIDialogContentSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_CONTAINS_SINGLE_ITEM)
+ m_containsSingleItem = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_NO_UPDATING)
+ m_noUpdating = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_USE_DIRECTORY_NAMES)
+ m_useDirectoryNames = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_SCAN_RECURSIVE)
+ {
+ m_scanRecursive = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ GetSettingsManager()->SetBool(SETTING_CONTAINS_SINGLE_ITEM, false);
+ }
+ else if (settingId == SETTING_EXCLUDE)
+ m_exclude = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_ALL_EXTERNAL_AUDIO)
+ m_allExternalAudio = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+}
+
+void CGUIDialogContentSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == SETTING_CONTENT_TYPE)
+ {
+ std::vector<std::pair<std::string, int>> labels;
+ if (m_content == CONTENT_ALBUMS || m_content == CONTENT_ARTISTS)
+ {
+ labels.emplace_back(ADDON::TranslateContent(m_content, true), m_content);
+ }
+ else
+ {
+ labels.emplace_back(ADDON::TranslateContent(CONTENT_NONE, true), CONTENT_NONE);
+ labels.emplace_back(ADDON::TranslateContent(CONTENT_MOVIES, true), CONTENT_MOVIES);
+ labels.emplace_back(ADDON::TranslateContent(CONTENT_TVSHOWS, true), CONTENT_TVSHOWS);
+ labels.emplace_back(ADDON::TranslateContent(CONTENT_MUSICVIDEOS, true), CONTENT_MUSICVIDEOS);
+ }
+ std::sort(labels.begin(), labels.end());
+
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (dialog)
+ {
+ dialog->SetHeading(CVariant{ 20344 }); //Label "This directory contains"
+
+ int iIndex = 0;
+ int iSelected = 0;
+ for (const auto &label : labels)
+ {
+ dialog->Add(label.first);
+
+ if (m_content == label.second)
+ iSelected = iIndex;
+ iIndex++;
+ }
+
+ dialog->SetSelected(iSelected);
+
+ dialog->Open();
+ // Selected item has not changes - in case of cancel or the user selecting the same item
+ int newSelected = dialog->GetSelectedItem();
+ if (!dialog->IsConfirmed() || newSelected < 0 || newSelected == iSelected)
+ return;
+
+ auto selected = labels.at(newSelected);
+ m_content = static_cast<CONTENT_TYPE>(selected.second);
+
+ AddonPtr scraperAddon;
+ if (!CAddonSystemSettings::GetInstance().GetActive(ADDON::ScraperTypeFromContent(m_content), scraperAddon) && m_content != CONTENT_NONE)
+ return;
+
+ m_scraper = std::dynamic_pointer_cast<CScraper>(scraperAddon);
+
+ SetupView();
+ SetFocusToSetting(SETTING_CONTENT_TYPE);
+ }
+ }
+ else if (settingId == SETTING_SCRAPER_LIST)
+ {
+ ADDON::AddonType type = ADDON::ScraperTypeFromContent(m_content);
+ std::string currentScraperId;
+ if (m_scraper != nullptr)
+ currentScraperId = m_scraper->ID();
+ std::string selectedAddonId = currentScraperId;
+
+ if (CGUIWindowAddonBrowser::SelectAddonID(type, selectedAddonId, false) == 1
+ && selectedAddonId != currentScraperId)
+ {
+ AddonPtr scraperAddon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(selectedAddonId, scraperAddon,
+ ADDON::OnlyEnabled::CHOICE_YES))
+ {
+ m_scraper = std::dynamic_pointer_cast<CScraper>(scraperAddon);
+ SetupView();
+ SetFocusToSetting(SETTING_SCRAPER_LIST);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} - Could not get settings for addon: {}", __FUNCTION__,
+ selectedAddonId);
+ }
+ }
+ }
+ else if (settingId == SETTING_SCRAPER_SETTINGS)
+ CGUIDialogAddonSettings::ShowForAddon(m_scraper, false);
+}
+
+bool CGUIDialogContentSettings::Save()
+{
+ //Should be saved by caller of ::Show
+ return true;
+}
+
+void CGUIDialogContentSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+ SetHeading(20333);
+
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+
+ SetLabel2(SETTING_CONTENT_TYPE, ADDON::TranslateContent(m_content, true));
+
+ if (m_content == CONTENT_NONE)
+ {
+ ToggleState(SETTING_SCRAPER_LIST, false);
+ ToggleState(SETTING_SCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ ToggleState(SETTING_SCRAPER_LIST, true);
+ if (m_scraper != NULL && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_scraper->ID()))
+ {
+ SetLabel2(SETTING_SCRAPER_LIST, m_scraper->Name());
+ if (m_scraper && m_scraper->Supports(m_content) && m_scraper->HasSettings())
+ ToggleState(SETTING_SCRAPER_SETTINGS, true);
+ else
+ ToggleState(SETTING_SCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ SetLabel2(SETTING_SCRAPER_LIST, g_localizeStrings.Get(231)); //Set label2 to "None"
+ ToggleState(SETTING_SCRAPER_SETTINGS, false);
+ }
+ }
+}
+
+void CGUIDialogContentSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ if (m_content == CONTENT_NONE)
+ m_showScanSettings = false;
+ else if (m_scraper != NULL && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_scraper->ID()))
+ m_showScanSettings = true;
+
+ std::shared_ptr<CSettingCategory> category = AddCategory("contentsettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogContentSettings: unable to setup settings");
+ return;
+ }
+
+ std::shared_ptr<CSettingGroup> group = AddGroup(category);
+ if (group == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogContentSettings: unable to setup settings");
+ return;
+ }
+
+ AddButton(group, SETTING_CONTENT_TYPE, 20344, SettingLevel::Basic);
+ AddButton(group, SETTING_SCRAPER_LIST, 38025, SettingLevel::Basic);
+ std::shared_ptr<CSettingAction> subsetting = AddButton(group, SETTING_SCRAPER_SETTINGS, 10004, SettingLevel::Basic);
+ if (subsetting != NULL)
+ subsetting->SetParent(SETTING_SCRAPER_LIST);
+
+ std::shared_ptr<CSettingGroup> groupDetails = AddGroup(category, 20322);
+ if (groupDetails == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogContentSettings: unable to setup scanning settings");
+ return;
+ }
+ switch (m_content)
+ {
+ case CONTENT_TVSHOWS:
+ {
+ AddToggle(groupDetails, SETTING_CONTAINS_SINGLE_ITEM, 20379, SettingLevel::Basic, m_containsSingleItem, false, m_showScanSettings);
+ AddToggle(groupDetails, SETTING_NO_UPDATING, 20432, SettingLevel::Basic, m_noUpdating, false, m_showScanSettings);
+ AddToggle(groupDetails, SETTING_ALL_EXTERNAL_AUDIO, 39120, SettingLevel::Basic,
+ m_allExternalAudio, false, m_showScanSettings);
+ break;
+ }
+
+ case CONTENT_MOVIES:
+ case CONTENT_MUSICVIDEOS:
+ {
+ AddToggle(groupDetails, SETTING_USE_DIRECTORY_NAMES, m_content == CONTENT_MOVIES ? 20329 : 20330, SettingLevel::Basic, m_useDirectoryNames, false, m_showScanSettings);
+ std::shared_ptr<CSettingBool> settingScanRecursive = AddToggle(groupDetails, SETTING_SCAN_RECURSIVE, 20346, SettingLevel::Basic, m_scanRecursive, false, m_showScanSettings);
+ std::shared_ptr<CSettingBool> settingContainsSingleItem = AddToggle(groupDetails, SETTING_CONTAINS_SINGLE_ITEM, 20383, SettingLevel::Basic, m_containsSingleItem, false, m_showScanSettings);
+ AddToggle(groupDetails, SETTING_NO_UPDATING, 20432, SettingLevel::Basic, m_noUpdating, false, m_showScanSettings);
+ AddToggle(groupDetails, SETTING_ALL_EXTERNAL_AUDIO, 39120, SettingLevel::Basic,
+ m_allExternalAudio, false, m_showScanSettings);
+
+ // define an enable dependency with (m_useDirectoryNames && !m_containsSingleItem) || !m_useDirectoryNames
+ CSettingDependency dependencyScanRecursive(SettingDependencyType::Enable, GetSettingsManager());
+ dependencyScanRecursive.Or()
+ ->Add(CSettingDependencyConditionCombinationPtr((new CSettingDependencyConditionCombination(BooleanLogicOperationAnd, GetSettingsManager())) // m_useDirectoryNames && !m_containsSingleItem
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false, GetSettingsManager()))) // m_useDirectoryNames
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_CONTAINS_SINGLE_ITEM, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))))) // !m_containsSingleItem
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))); // !m_useDirectoryNames
+
+ // define an enable dependency with m_useDirectoryNames && !m_scanRecursive
+ CSettingDependency dependencyContainsSingleItem(SettingDependencyType::Enable, GetSettingsManager());
+ dependencyContainsSingleItem.And()
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_USE_DIRECTORY_NAMES, "true", SettingDependencyOperator::Equals, false, GetSettingsManager()))) // m_useDirectoryNames
+ ->Add(CSettingDependencyConditionPtr(new CSettingDependencyCondition(SETTING_SCAN_RECURSIVE, "false", SettingDependencyOperator::Equals, false, GetSettingsManager()))); // !m_scanRecursive
+
+ SettingDependencies deps;
+ deps.push_back(dependencyScanRecursive);
+ settingScanRecursive->SetDependencies(deps);
+
+ deps.clear();
+ deps.push_back(dependencyContainsSingleItem);
+ settingContainsSingleItem->SetDependencies(deps);
+ break;
+ }
+
+ case CONTENT_ALBUMS:
+ case CONTENT_ARTISTS:
+ break;
+
+ case CONTENT_NONE:
+ default:
+ AddToggle(groupDetails, SETTING_EXCLUDE, 20380, SettingLevel::Basic, m_exclude, false, !m_showScanSettings);
+ AddToggle(groupDetails, SETTING_ALL_EXTERNAL_AUDIO, 39120, SettingLevel::Basic,
+ m_allExternalAudio, false, !m_showScanSettings);
+ break;
+ }
+}
+
+void CGUIDialogContentSettings::SetLabel2(const std::string &settingid, const std::string &label)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_LABEL2(settingControl->GetID(), label);
+}
+
+void CGUIDialogContentSettings::ToggleState(const std::string &settingid, bool enabled)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ {
+ if (enabled)
+ CONTROL_ENABLE(settingControl->GetID());
+ else
+ CONTROL_DISABLE(settingControl->GetID());
+ }
+}
+
+void CGUIDialogContentSettings::SetFocusToSetting(const std::string& settingid)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_FOCUS(settingControl->GetID(), 0);
+}
diff --git a/xbmc/settings/dialogs/GUIDialogContentSettings.h b/xbmc/settings/dialogs/GUIDialogContentSettings.h
new file mode 100644
index 0000000..c0d7c81
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogContentSettings.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "addons/Scraper.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <map>
+#include <utility>
+
+namespace VIDEO
+{
+ struct SScanSettings;
+}
+class CFileItemList;
+
+class CGUIDialogContentSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogContentSettings();
+
+ // specialization of CGUIWindow
+ bool HasListItems() const override { return true; }
+
+ CONTENT_TYPE GetContent() const { return m_content; }
+ void SetContent(CONTENT_TYPE content);
+ void ResetContent();
+
+ const ADDON::ScraperPtr& GetScraper() const { return m_scraper; }
+ void SetScraper(ADDON::ScraperPtr scraper) { m_scraper = std::move(scraper); }
+
+ void SetScanSettings(const VIDEO::SScanSettings &scanSettings);
+ bool GetScanRecursive() const { return m_scanRecursive; }
+ bool GetUseDirectoryNames() const { return m_useDirectoryNames; }
+ bool GetContainsSingleItem() const { return m_containsSingleItem; }
+ bool GetExclude() const { return m_exclude; }
+ bool GetNoUpdating() const { return m_noUpdating; }
+ bool GetUseAllExternalAudio() const { return m_allExternalAudio; }
+
+ static bool Show(ADDON::ScraperPtr& scraper, CONTENT_TYPE content = CONTENT_NONE);
+ static bool Show(ADDON::ScraperPtr& scraper, VIDEO::SScanSettings& settings, CONTENT_TYPE content = CONTENT_NONE);
+
+protected:
+ // specializations of CGUIWindow
+ void OnInitWindow() override;
+
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool AllowResettingSettings() const override { return false; }
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+private:
+ void SetLabel2(const std::string &settingid, const std::string &label);
+ void ToggleState(const std::string &settingid, bool enabled);
+ using CGUIDialogSettingsManualBase::SetFocus;
+ void SetFocusToSetting(const std::string& settingid);
+
+ /*!
+ * @brief The currently selected content type
+ */
+ CONTENT_TYPE m_content = CONTENT_NONE;
+ /*!
+ * @brief The selected content type at dialog creation
+ */
+ CONTENT_TYPE m_originalContent = CONTENT_NONE;
+ /*!
+ * @brief The currently selected scraper
+ */
+ ADDON::ScraperPtr m_scraper;
+
+ bool m_showScanSettings = false;
+ bool m_scanRecursive = false;
+ bool m_useDirectoryNames = false;
+ bool m_containsSingleItem = false;
+ bool m_exclude = false;
+ bool m_noUpdating = false;
+ bool m_allExternalAudio = false;
+};
diff --git a/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
new file mode 100644
index 0000000..7e88553
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogLibExportSettings.cpp
@@ -0,0 +1,452 @@
+/*
+ * 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 "GUIDialogLibExportSettings.h"
+
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "messaging/helpers/DialogHelper.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "settings/SettingUtils.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "storage/MediaManager.h"
+#include "utils/URIUtils.h"
+#include "utils/log.h"
+
+#include <limits.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+using namespace ADDON;
+using namespace KODI::MESSAGING;
+
+using KODI::MESSAGING::HELPERS::DialogResponse;
+
+CGUIDialogLibExportSettings::CGUIDialogLibExportSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_LIBEXPORT_SETTINGS, "DialogSettings.xml")
+{ }
+
+bool CGUIDialogLibExportSettings::Show(CLibExportSettings& settings)
+{
+ CGUIDialogLibExportSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogLibExportSettings>(WINDOW_DIALOG_LIBEXPORT_SETTINGS);
+ if (!dialog)
+ return false;
+
+ // Get current export settings from service broker
+ const std::shared_ptr<CSettings> pSettings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ dialog->m_settings.SetExportType(pSettings->GetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE));
+ dialog->m_settings.m_strPath = pSettings->GetString(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+ dialog->m_settings.SetItemsToExport(pSettings->GetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS));
+ dialog->m_settings.m_unscraped = pSettings->GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED);
+ dialog->m_settings.m_artwork = pSettings->GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK);
+ dialog->m_settings.m_skipnfo = pSettings->GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO);
+ dialog->m_settings.m_overwrite = pSettings->GetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE);
+
+ // Ensure NFO or art output enabled when albums exported (adjust old saved settings)
+ if (dialog->m_settings.IsItemExported(ELIBEXPORT_ALBUMS) && (dialog->m_settings.m_skipnfo && !dialog->m_settings.m_artwork))
+ dialog->m_settings.m_skipnfo = false;
+
+ dialog->m_destinationChecked = false;
+ dialog->Open();
+
+ bool confirmed = dialog->IsConfirmed();
+ if (confirmed)
+ {
+ // Return the new settings (saved by service broker but avoids re-reading)
+ settings = dialog->m_settings;
+ }
+ return confirmed;
+}
+
+void CGUIDialogLibExportSettings::OnInitWindow()
+{
+ CGUIDialogSettingsManualBase::OnInitWindow();
+}
+
+void CGUIDialogLibExportSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (!setting)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE)
+ {
+ m_settings.SetExportType(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ SetupView();
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE);
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER)
+ {
+ m_settings.m_strPath = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ UpdateButtons();
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE)
+ m_settings.m_overwrite = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS)
+ {
+ m_settings.SetItemsToExport(GetExportItemsFromSetting(setting));
+ if (m_settings.IsItemExported(ELIBEXPORT_ALBUMS) && (m_settings.m_skipnfo && !m_settings.m_artwork))
+ {
+ m_settings.m_skipnfo = false;
+ m_settingNFO->SetValue(true);
+ UpdateToggles();
+ }
+ UpdateDescription();
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK)
+ {
+ m_settings.m_artwork = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ if (m_settings.IsItemExported(ELIBEXPORT_ALBUMS) && (m_settings.m_skipnfo && !m_settings.m_artwork))
+ {
+ m_settings.m_skipnfo = false;
+ m_settingNFO->SetValue(true);
+ }
+ UpdateToggles();
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED)
+ m_settings.m_unscraped = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO)
+ {
+ m_settings.m_skipnfo = !std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ if (m_settings.IsItemExported(ELIBEXPORT_ALBUMS) && (m_settings.m_skipnfo && !m_settings.m_artwork))
+ {
+ m_settings.m_artwork = true;
+ m_settingArt->SetValue(true);
+ }
+ UpdateToggles();
+ }
+}
+
+void CGUIDialogLibExportSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER && !m_settings.IsToLibFolders() &&
+ !m_settings.IsArtistFoldersOnly())
+ {
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+ CServiceBroker::GetMediaManager().GetRemovableDrives(shares);
+ std::string strDirectory = m_settings.m_strPath;
+ if (!strDirectory.empty())
+ {
+ URIUtils::AddSlashAtEnd(strDirectory);
+ bool bIsSource;
+ if (CUtil::GetMatchingSource(strDirectory, shares, bIsSource) < 0) // path is outside shares - add it as a separate one
+ {
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13278);
+ share.strPath = strDirectory;
+ shares.push_back(share);
+ }
+ }
+ else
+ strDirectory = "default location";
+
+ if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(661), strDirectory, true))
+ {
+ if (!strDirectory.empty())
+ {
+ m_destinationChecked = true;
+ m_settings.m_strPath = strDirectory;
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, strDirectory);
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+ }
+ }
+ UpdateButtons();
+ }
+}
+
+bool CGUIDialogLibExportSettings::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_SETTINGS_OKAY_BUTTON)
+ {
+ OnOK();
+ return true;
+ }
+ }
+ break;
+ }
+ return CGUIDialogSettingsManualBase::OnMessage(message);
+}
+
+void CGUIDialogLibExportSettings::OnOK()
+{
+ // Validate destination folder
+ if (m_settings.IsToLibFolders() || m_settings.IsArtistFoldersOnly())
+ {
+ // Check artist info folder setting
+ std::string path = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (path.empty())
+ {
+ //"Unable to export to library folders as the system artist information folder setting is empty"
+ //Settings (YES) button takes user to enter the artist info folder setting
+ if (HELPERS::ShowYesNoDialogText(20223, 38317, 186, 10004) == DialogResponse::CHOICE_YES)
+ {
+ m_confirmed = false;
+ Close();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SETTINGS_MEDIA, CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ }
+ return;
+ }
+ }
+ else if (!m_destinationChecked)
+ {
+ // ELIBEXPORT_SINGLEFILE or LIBEXPORT_SEPARATEFILES
+ // Check that destination folder exists
+ if (!XFILE::CDirectory::Exists(m_settings.m_strPath))
+ {
+ HELPERS::ShowOKDialogText(CVariant{ 38300 }, CVariant{ 38318 });
+ return;
+ }
+ }
+ m_confirmed = true;
+ Save();
+ Close();
+}
+
+bool CGUIDialogLibExportSettings::Save()
+{
+ CLog::Log(LOGINFO, "CGUIDialogMusicExportSettings: Save() called");
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ settings->SetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE, m_settings.GetExportType());
+ settings->SetString(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, m_settings.m_strPath);
+ settings->SetInt(CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS, m_settings.GetItemsToExport());
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED, m_settings.m_unscraped);
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, m_settings.m_overwrite);
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, m_settings.m_artwork);
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, m_settings.m_skipnfo);
+ settings->Save();
+
+ return true;
+}
+
+void CGUIDialogLibExportSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+ SetHeading(38300);
+
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 38319);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+
+ UpdateButtons();
+ UpdateToggles();
+ UpdateDescription();
+}
+
+void CGUIDialogLibExportSettings::UpdateButtons()
+{
+ // Enable Export button when destination folder has a path (but may not exist)
+ bool enableExport(true);
+ if (m_settings.IsSingleFile() ||
+ m_settings.IsSeparateFiles())
+ enableExport = !m_settings.m_strPath.empty();
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_SETTINGS_OKAY_BUTTON, enableExport);
+ if (!enableExport)
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER);
+}
+
+void CGUIDialogLibExportSettings::UpdateToggles()
+{
+ if (m_settings.IsSeparateFiles())
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED, !m_settings.m_skipnfo);
+
+ if (!m_settings.IsItemExported(ELIBEXPORT_ALBUMS) && m_settings.m_skipnfo && !m_settings.m_artwork)
+ {
+ //"Output information to NFO files (currently exporting artist folders only)"
+ SetLabel(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, g_localizeStrings.Get(38310));
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, false);
+ }
+ else
+ {
+ //"Output information to NFO files"
+ SetLabel(CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, g_localizeStrings.Get(38309));
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, true);
+ }
+}
+
+void CGUIDialogLibExportSettings::UpdateDescription()
+{
+ if (m_settings.IsToLibFolders())
+ {
+ // Destination button is description of what to library means
+ SetLabel(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, "");
+ if (m_settings.IsItemExported(ELIBEXPORT_ALBUMS))
+ if (m_settings.IsArtists())
+ //"Artists exported to Artist Information Folder and albums to music folders"
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, g_localizeStrings.Get(38322));
+ else
+ //"Albums exported to music folders"
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, g_localizeStrings.Get(38323));
+ else
+ // "Artists exported to Artist Information Folder"
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, g_localizeStrings.Get(38324));
+ }
+ else if (m_settings.IsArtistFoldersOnly())
+ {
+ // Destination button is description of what artist folders means
+ SetLabel(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, "");
+ //"Artists folders created in Artist Information Folder"
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, g_localizeStrings.Get(38325));
+ }
+ else
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, m_settings.m_strPath);
+ SetLabel(CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, g_localizeStrings.Get(38305));
+ }
+}
+
+void CGUIDialogLibExportSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ std::shared_ptr<CSettingCategory> category = AddCategory("exportsettings", -1);
+ if (!category)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings: unable to setup settings");
+ return;
+ }
+
+ std::shared_ptr<CSettingGroup> groupDetails = AddGroup(category);
+ if (!groupDetails)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings: unable to setup settings");
+ return;
+ }
+
+ TranslatableIntegerSettingOptions entries;
+
+ entries.push_back(TranslatableIntegerSettingOption(38301, ELIBEXPORT_SINGLEFILE));
+ entries.push_back(TranslatableIntegerSettingOption(38303, ELIBEXPORT_TOLIBRARYFOLDER));
+ entries.push_back(TranslatableIntegerSettingOption(38302, ELIBEXPORT_SEPARATEFILES));
+ entries.push_back(TranslatableIntegerSettingOption(38321, ELIBEXPORT_ARTISTFOLDERS));
+ AddList(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FILETYPE, 38304, SettingLevel::Basic, m_settings.GetExportType(), entries, 38304); // "Choose kind of export output"
+ AddButton(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_FOLDER, 38305, SettingLevel::Basic);
+
+ entries.clear();
+ if (!m_settings.IsArtistFoldersOnly())
+ entries.push_back(TranslatableIntegerSettingOption(132, ELIBEXPORT_ALBUMS)); //ablums
+ if (m_settings.IsSingleFile())
+ entries.push_back(TranslatableIntegerSettingOption(134, ELIBEXPORT_SONGS)); //songs
+ entries.push_back(
+ TranslatableIntegerSettingOption(38043, ELIBEXPORT_ALBUMARTISTS)); //album artists
+ entries.push_back(TranslatableIntegerSettingOption(38312, ELIBEXPORT_SONGARTISTS)); //song artists
+ entries.push_back(
+ TranslatableIntegerSettingOption(38313, ELIBEXPORT_OTHERARTISTS)); //other artists
+
+ std::vector<int> items;
+ if (m_settings.IsArtistFoldersOnly())
+ {
+ // Only artists, not albums, at least album artists
+ items = m_settings.GetLimitedItems(ELIBEXPORT_ALBUMARTISTS + ELIBEXPORT_SONGARTISTS + ELIBEXPORT_OTHERARTISTS);
+ if (items.size() == 0)
+ items.emplace_back(ELIBEXPORT_ALBUMARTISTS);
+ }
+ else if (!m_settings.IsSingleFile())
+ {
+ // No songs unless single file export, at least album artists
+ items = m_settings.GetLimitedItems(ELIBEXPORT_ALBUMS + ELIBEXPORT_ALBUMARTISTS + ELIBEXPORT_SONGARTISTS + ELIBEXPORT_OTHERARTISTS);
+ if (items.size() == 0)
+ items.emplace_back(ELIBEXPORT_ALBUMARTISTS);
+ }
+ else
+ items = m_settings.GetExportItems();
+
+ AddList(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_ITEMS, 38306, SettingLevel::Basic, items, entries, 133, 1);
+
+ if (m_settings.IsToLibFolders() || m_settings.IsSeparateFiles())
+ {
+ m_settingNFO = AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_SKIPNFO, 38309, SettingLevel::Basic, !m_settings.m_skipnfo);
+ if (m_settings.IsSeparateFiles())
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_UNSCRAPED, 38308, SettingLevel::Basic, m_settings.m_unscraped);
+ m_settingArt = AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_ARTWORK, 38307, SettingLevel::Basic, m_settings.m_artwork);
+ AddToggle(groupDetails, CSettings::SETTING_MUSICLIBRARY_EXPORT_OVERWRITE, 38311, SettingLevel::Basic, m_settings.m_overwrite);
+ }
+}
+
+void CGUIDialogLibExportSettings::SetLabel2(const std::string &settingid, const std::string &label)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != nullptr && settingControl->GetControl() != nullptr)
+ SET_CONTROL_LABEL2(settingControl->GetID(), label);
+}
+
+void CGUIDialogLibExportSettings::SetLabel(const std::string &settingid, const std::string &label)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != nullptr && settingControl->GetControl() != nullptr)
+ SetControlLabel(settingControl->GetID(), label);
+}
+
+void CGUIDialogLibExportSettings::ToggleState(const std::string & settingid, bool enabled)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != nullptr && settingControl->GetControl() != nullptr)
+ {
+ if (enabled)
+ CONTROL_ENABLE(settingControl->GetID());
+ else
+ CONTROL_DISABLE(settingControl->GetID());
+ }
+}
+
+void CGUIDialogLibExportSettings::SetFocus(const std::string &settingid)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_FOCUS(settingControl->GetID(), 0);
+}
+
+int CGUIDialogLibExportSettings::GetExportItemsFromSetting(const SettingConstPtr& setting)
+{
+ std::shared_ptr<const CSettingList> settingList = std::static_pointer_cast<const CSettingList>(setting);
+ if (settingList->GetElementType() != SettingType::Integer)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings::{} - wrong items element type", __FUNCTION__);
+ return 0;
+ }
+ int exportitems = 0;
+ std::vector<CVariant> list = CSettingUtils::GetList(settingList);
+ for (const auto &value : list)
+ {
+ if (!value.isInteger())
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLibExportSettings::{} - wrong items value type", __FUNCTION__);
+ return 0;
+ }
+ exportitems += static_cast<int>(value.asInteger());
+ }
+ return exportitems;
+}
diff --git a/xbmc/settings/dialogs/GUIDialogLibExportSettings.h b/xbmc/settings/dialogs/GUIDialogLibExportSettings.h
new file mode 100644
index 0000000..4c861ba
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogLibExportSettings.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+#include "settings/LibExportSettings.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <map>
+
+class CGUIDialogLibExportSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogLibExportSettings();
+
+ // specialization of CGUIWindow
+ bool HasListItems() const override { return true; }
+ static bool Show(CLibExportSettings& settings);
+
+protected:
+ // specializations of CGUIWindow
+ void OnInitWindow() override;
+
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ // specialization of CGUIDialogSettingsBase
+ bool OnMessage(CGUIMessage& message) override;
+ bool AllowResettingSettings() const override { return false; }
+ bool Save() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+ void OnOK();
+ void UpdateButtons();
+
+private:
+ void SetLabel2(const std::string &settingid, const std::string &label);
+ void SetLabel(const std::string &settingid, const std::string &label);
+ void ToggleState(const std::string &settingid, bool enabled);
+
+ using CGUIDialogSettingsManualBase::SetFocus;
+ void SetFocus(const std::string &settingid);
+ static int GetExportItemsFromSetting(const SettingConstPtr& setting);
+ void UpdateToggles();
+ void UpdateDescription();
+
+ CLibExportSettings m_settings;
+ bool m_destinationChecked = false;
+ std::shared_ptr<CSettingBool> m_settingNFO;
+ std::shared_ptr<CSettingBool> m_settingArt;
+};
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp b/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp
new file mode 100644
index 0000000..32d3958
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsBase.cpp
@@ -0,0 +1,972 @@
+/*
+ * Copyright (C) 2014-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 "GUIDialogSettingsBase.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIColorButtonControl.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIControlGroupList.h"
+#include "guilib/GUIEditControl.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUILabelControl.h"
+#include "guilib/GUIRadioButtonControl.h"
+#include "guilib/GUISettingsSliderControl.h"
+#include "guilib/GUISpinControlEx.h"
+#include "guilib/GUIToggleButtonControl.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "settings/SettingControl.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#if defined(TARGET_WINDOWS) // disable 4355: 'this' used in base member initializer list
+#pragma warning(push)
+#pragma warning(disable : 4355)
+#endif // defined(TARGET_WINDOWS)
+
+#define CATEGORY_GROUP_ID 3
+#define SETTINGS_GROUP_ID 5
+
+#define CONTROL_DEFAULT_BUTTON 7
+#define CONTROL_DEFAULT_RADIOBUTTON 8
+#define CONTROL_DEFAULT_SPIN 9
+#define CONTROL_DEFAULT_CATEGORY_BUTTON 10
+#define CONTROL_DEFAULT_SEPARATOR 11
+#define CONTROL_DEFAULT_EDIT 12
+#define CONTROL_DEFAULT_SLIDER 13
+#define CONTROL_DEFAULT_SETTING_LABEL 14
+#define CONTROL_DEFAULT_COLORBUTTON 15
+
+CGUIDialogSettingsBase::CGUIDialogSettingsBase(int windowId, const std::string& xmlFile)
+ : CGUIDialog(windowId, xmlFile),
+ m_iSetting(0),
+ m_iCategory(0),
+ m_resetSetting(NULL),
+ m_dummyCategory(NULL),
+ m_pOriginalSpin(NULL),
+ m_pOriginalSlider(NULL),
+ m_pOriginalRadioButton(NULL),
+ m_pOriginalColorButton(nullptr),
+ m_pOriginalCategoryButton(NULL),
+ m_pOriginalButton(NULL),
+ m_pOriginalEdit(NULL),
+ m_pOriginalImage(NULL),
+ m_pOriginalGroupTitle(NULL),
+ m_newOriginalEdit(false),
+ m_delayedTimer(this),
+ m_confirmed(false),
+ m_focusedControl(0),
+ m_fadedControl(0)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogSettingsBase::~CGUIDialogSettingsBase()
+{
+ FreeControls();
+ DeleteControls();
+}
+
+bool CGUIDialogSettingsBase::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_INIT:
+ {
+ m_delayedSetting.reset();
+ if (message.GetParam1() != WINDOW_INVALID)
+ { // coming to this window first time (ie not returning back from some other window)
+ // so we reset our section and control states
+ m_iCategory = 0;
+ ResetControlStates();
+ }
+
+ if (AllowResettingSettings())
+ {
+ m_resetSetting = std::make_shared<CSettingAction>(SETTINGS_RESET_SETTING_ID);
+ m_resetSetting->SetLabel(10041);
+ m_resetSetting->SetHelp(10045);
+ m_resetSetting->SetControl(CreateControl("button"));
+ }
+
+ m_dummyCategory = std::make_shared<CSettingCategory>(SETTINGS_EMPTY_CATEGORY_ID);
+ m_dummyCategory->SetLabel(10046);
+ m_dummyCategory->SetHelp(10047);
+ break;
+ }
+
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ // cancel any delayed changes
+ if (m_delayedSetting != NULL)
+ {
+ m_delayedTimer.Stop();
+ CGUIMessage message(GUI_MSG_UPDATE_ITEM, GetID(), m_delayedSetting->GetID());
+ OnMessage(message);
+ }
+
+ CGUIDialog::OnMessage(message);
+ FreeControls();
+ return true;
+ }
+
+ case GUI_MSG_FOCUSED:
+ {
+ CGUIDialog::OnMessage(message);
+ m_focusedControl = GetFocusedControlID();
+
+ // cancel any delayed changes
+ if (m_delayedSetting != NULL && m_delayedSetting->GetID() != m_focusedControl)
+ {
+ m_delayedTimer.Stop();
+ // param1 = 1 for "reset the control if it's invalid"
+ CGUIMessage message(GUI_MSG_UPDATE_ITEM, GetID(), m_delayedSetting->GetID(), 1);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message, GetID());
+ }
+ // update the value of the previous setting (in case it was invalid)
+ else if (m_iSetting >= CONTROL_SETTINGS_START_CONTROL &&
+ m_iSetting < (int)(CONTROL_SETTINGS_START_CONTROL + m_settingControls.size()))
+ {
+ BaseSettingControlPtr control = GetSettingControl(m_iSetting);
+ if (control != NULL && control->GetSetting() != NULL && !control->IsValid())
+ {
+ // param1 = 1 for "reset the control if it's invalid"
+ // param2 = 1 for "only update the current value"
+ CGUIMessage message(GUI_MSG_UPDATE_ITEM, GetID(), m_iSetting, 1, 1);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message, GetID());
+ }
+ }
+
+ CVariant description;
+
+ // check if we have changed the category and need to create new setting controls
+ if (m_focusedControl >= CONTROL_SETTINGS_START_BUTTONS &&
+ m_focusedControl < (int)(CONTROL_SETTINGS_START_BUTTONS + m_categories.size()))
+ {
+ int categoryIndex = m_focusedControl - CONTROL_SETTINGS_START_BUTTONS;
+ SettingCategoryPtr category = m_categories.at(categoryIndex);
+ if (categoryIndex != m_iCategory)
+ {
+ if (!category->CanAccess())
+ {
+ // unable to go to this category - focus the previous one
+ SET_CONTROL_FOCUS(CONTROL_SETTINGS_START_BUTTONS + m_iCategory, 0);
+ return false;
+ }
+
+ m_iCategory = categoryIndex;
+ CreateSettings();
+ }
+
+ description = category->GetHelp();
+ }
+ else if (m_focusedControl >= CONTROL_SETTINGS_START_CONTROL &&
+ m_focusedControl < (int)(CONTROL_SETTINGS_START_CONTROL + m_settingControls.size()))
+ {
+ m_iSetting = m_focusedControl;
+ std::shared_ptr<CSetting> setting = GetSettingControl(m_focusedControl)->GetSetting();
+ if (setting != NULL)
+ description = setting->GetHelp();
+ }
+
+ // set the description of the currently focused category/setting
+ if (description.isInteger() || (description.isString() && !description.empty()))
+ SetDescription(description);
+
+ return true;
+ }
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_SETTINGS_OKAY_BUTTON)
+ {
+ if (OnOkay())
+ {
+ Close();
+ return true;
+ }
+
+ return false;
+ }
+
+ if (iControl == CONTROL_SETTINGS_CANCEL_BUTTON)
+ {
+ OnCancel();
+ Close();
+ return true;
+ }
+
+ BaseSettingControlPtr control = GetSettingControl(iControl);
+ if (control != NULL)
+ OnClick(control);
+
+ break;
+ }
+
+ case GUI_MSG_UPDATE_ITEM:
+ {
+ if (m_delayedSetting != NULL && m_delayedSetting->GetID() == message.GetControlId())
+ {
+ // first get the delayed setting and reset its member variable
+ // to avoid handling the delayed setting twice in case the OnClick()
+ // performed later causes the window to be deinitialized (e.g. when
+ // changing the language)
+ BaseSettingControlPtr delayedSetting = m_delayedSetting;
+ m_delayedSetting.reset();
+
+ // if updating the setting fails and param1 has been specifically set
+ // we need to call OnSettingChanged() to restore a valid value in the
+ // setting control
+ if (!delayedSetting->OnClick() && message.GetParam1() != 0)
+ OnSettingChanged(delayedSetting->GetSetting());
+ return true;
+ }
+
+ if (message.GetControlId() >= CONTROL_SETTINGS_START_CONTROL &&
+ message.GetControlId() < (int)(CONTROL_SETTINGS_START_CONTROL + m_settingControls.size()))
+ {
+ BaseSettingControlPtr settingControl = GetSettingControl(message.GetControlId());
+ if (settingControl.get() != NULL && settingControl->GetSetting() != NULL)
+ {
+ settingControl->UpdateFromSetting(message.GetParam2() != 0);
+ return true;
+ }
+ }
+ break;
+ }
+
+ case GUI_MSG_UPDATE:
+ {
+ if (IsActive() && HasID(message.GetSenderId()))
+ {
+ int focusedControl = GetFocusedControlID();
+ CreateSettings();
+ SET_CONTROL_FOCUS(focusedControl, 0);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogSettingsBase::OnAction(const CAction& action)
+{
+ switch (action.GetID())
+ {
+ case ACTION_SETTINGS_RESET:
+ {
+ OnResetSettings();
+ return true;
+ }
+
+ case ACTION_DELETE_ITEM:
+ {
+ if (m_iSetting >= CONTROL_SETTINGS_START_CONTROL &&
+ m_iSetting < (int)(CONTROL_SETTINGS_START_CONTROL + m_settingControls.size()))
+ {
+ auto settingControl = GetSettingControl(m_iSetting);
+ if (settingControl != nullptr)
+ {
+ std::shared_ptr<CSetting> setting = settingControl->GetSetting();
+ if (setting != nullptr)
+ {
+ setting->Reset();
+ return true;
+ }
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogSettingsBase::OnBack(int actionID)
+{
+ m_lastControlID = 0; // don't save the control as we go to a different window each time
+
+ // if the setting dialog is not a window but a dialog we need to close differently
+ if (!IsDialog())
+ return CGUIWindow::OnBack(actionID);
+
+ return CGUIDialog::OnBack(actionID);
+}
+
+void CGUIDialogSettingsBase::DoProcess(unsigned int currentTime, CDirtyRegionList& dirtyregions)
+{
+ // update alpha status of current button
+ CGUIControl* control = GetFirstFocusableControl(CONTROL_SETTINGS_START_BUTTONS + m_iCategory);
+ if (control)
+ {
+ if (m_fadedControl &&
+ (m_fadedControl != control->GetID() || m_fadedControl == m_focusedControl))
+ {
+ if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
+ static_cast<CGUIButtonControl*>(control)->SetAlpha(0xFF);
+ else
+ static_cast<CGUIButtonControl*>(control)->SetSelected(false);
+ m_fadedControl = 0;
+ }
+
+ if (!control->HasFocus())
+ {
+ m_fadedControl = control->GetID();
+ if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
+ static_cast<CGUIButtonControl*>(control)->SetAlpha(0x80);
+ else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON)
+ static_cast<CGUIButtonControl*>(control)->SetSelected(true);
+ else
+ m_fadedControl = 0;
+
+ if (m_fadedControl)
+ control->SetFocus(true);
+ }
+ }
+ CGUIDialog::DoProcess(currentTime, dirtyregions);
+}
+
+void CGUIDialogSettingsBase::OnInitWindow()
+{
+ m_confirmed = false;
+ SetupView();
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogSettingsBase::SetupControls(bool createSettings /* = true */)
+{
+ // cleanup first, if necessary
+ FreeControls();
+
+ // get all controls
+ m_pOriginalSpin = dynamic_cast<CGUISpinControlEx*>(GetControl(CONTROL_DEFAULT_SPIN));
+ m_pOriginalSlider = dynamic_cast<CGUISettingsSliderControl*>(GetControl(CONTROL_DEFAULT_SLIDER));
+ m_pOriginalRadioButton =
+ dynamic_cast<CGUIRadioButtonControl*>(GetControl(CONTROL_DEFAULT_RADIOBUTTON));
+ m_pOriginalCategoryButton =
+ dynamic_cast<CGUIButtonControl*>(GetControl(CONTROL_DEFAULT_CATEGORY_BUTTON));
+ m_pOriginalButton = dynamic_cast<CGUIButtonControl*>(GetControl(CONTROL_DEFAULT_BUTTON));
+ m_pOriginalImage = dynamic_cast<CGUIImage*>(GetControl(CONTROL_DEFAULT_SEPARATOR));
+ m_pOriginalEdit = dynamic_cast<CGUIEditControl*>(GetControl(CONTROL_DEFAULT_EDIT));
+ m_pOriginalGroupTitle =
+ dynamic_cast<CGUILabelControl*>(GetControl(CONTROL_DEFAULT_SETTING_LABEL));
+ m_pOriginalColorButton =
+ dynamic_cast<CGUIColorButtonControl*>(GetControl(CONTROL_DEFAULT_COLORBUTTON));
+
+ // if there's no edit control but there's a button control use that instead
+ if (m_pOriginalEdit == nullptr && m_pOriginalButton != nullptr)
+ {
+ m_pOriginalEdit = new CGUIEditControl(*m_pOriginalButton);
+ m_newOriginalEdit = true;
+ }
+
+ // hide all default controls by default
+ if (m_pOriginalSpin != nullptr)
+ m_pOriginalSpin->SetVisible(false);
+ if (m_pOriginalSlider != nullptr)
+ m_pOriginalSlider->SetVisible(false);
+ if (m_pOriginalRadioButton != nullptr)
+ m_pOriginalRadioButton->SetVisible(false);
+ if (m_pOriginalButton != nullptr)
+ m_pOriginalButton->SetVisible(false);
+ if (m_pOriginalCategoryButton != nullptr)
+ m_pOriginalCategoryButton->SetVisible(false);
+ if (m_pOriginalEdit != nullptr)
+ m_pOriginalEdit->SetVisible(false);
+ if (m_pOriginalImage != nullptr)
+ m_pOriginalImage->SetVisible(false);
+ if (m_pOriginalGroupTitle != nullptr)
+ m_pOriginalGroupTitle->SetVisible(false);
+ if (m_pOriginalColorButton != nullptr)
+ m_pOriginalColorButton->SetVisible(false);
+
+ // get the section
+ SettingSectionPtr section = GetSection();
+ if (section == NULL)
+ return;
+
+ // update the screen string
+ if (section->GetLabel() >= 0)
+ SetHeading(section->GetLabel());
+
+ // get the categories we need
+ m_categories = section->GetCategories((SettingLevel)GetSettingLevel());
+ if (m_categories.empty())
+ m_categories.push_back(m_dummyCategory);
+
+ if (m_pOriginalCategoryButton != NULL)
+ {
+ // setup our control groups...
+ CGUIControlGroupList* group =
+ dynamic_cast<CGUIControlGroupList*>(GetControl(CATEGORY_GROUP_ID));
+ if (!group)
+ return;
+
+ // go through the categories and create the necessary buttons
+ int buttonIdOffset = 0;
+ for (SettingCategoryList::const_iterator category = m_categories.begin();
+ category != m_categories.end(); ++category)
+ {
+ CGUIButtonControl* pButton = NULL;
+ if (m_pOriginalCategoryButton->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON)
+ pButton = new CGUIToggleButtonControl(
+ *static_cast<CGUIToggleButtonControl*>(m_pOriginalCategoryButton));
+ else if (m_pOriginalCategoryButton->GetControlType() == CGUIControl::GUICONTROL_COLORBUTTON)
+ pButton = new CGUIColorButtonControl(
+ *static_cast<CGUIColorButtonControl*>(m_pOriginalCategoryButton));
+ else
+ pButton = new CGUIButtonControl(*m_pOriginalCategoryButton);
+ pButton->SetLabel(GetSettingsLabel(*category));
+ pButton->SetID(CONTROL_SETTINGS_START_BUTTONS + buttonIdOffset);
+ pButton->SetVisible(true);
+ pButton->AllocResources();
+
+ group->AddControl(pButton);
+ buttonIdOffset++;
+ }
+ }
+
+ if (createSettings)
+ CreateSettings();
+
+ // set focus correctly depending on whether there are categories visible or not
+ if (m_pOriginalCategoryButton == NULL &&
+ (m_defaultControl <= 0 || m_defaultControl == CATEGORY_GROUP_ID))
+ m_defaultControl = SETTINGS_GROUP_ID;
+ else if (m_pOriginalCategoryButton != NULL && m_defaultControl <= 0)
+ m_defaultControl = CATEGORY_GROUP_ID;
+}
+
+void CGUIDialogSettingsBase::FreeControls()
+{
+ // clear the category group
+ CGUIControlGroupList* control =
+ dynamic_cast<CGUIControlGroupList*>(GetControl(CATEGORY_GROUP_ID));
+ if (control)
+ {
+ control->FreeResources();
+ control->ClearAll();
+ }
+ m_categories.clear();
+ FreeSettingsControls();
+}
+
+void CGUIDialogSettingsBase::DeleteControls()
+{
+ if (m_newOriginalEdit)
+ {
+ delete m_pOriginalEdit;
+ m_pOriginalEdit = NULL;
+ }
+
+ m_resetSetting.reset();
+ m_dummyCategory.reset();
+}
+
+void CGUIDialogSettingsBase::FreeSettingsControls()
+{
+ // clear the settings group
+ CGUIControlGroupList* control =
+ dynamic_cast<CGUIControlGroupList*>(GetControl(SETTINGS_GROUP_ID));
+ if (control)
+ {
+ control->FreeResources();
+ control->ClearAll();
+ }
+
+ for (std::vector<BaseSettingControlPtr>::iterator control = m_settingControls.begin();
+ control != m_settingControls.end(); ++control)
+ (*control)->Clear();
+
+ m_settingControls.clear();
+}
+
+void CGUIDialogSettingsBase::OnTimeout()
+{
+ UpdateSettingControl(m_delayedSetting, true);
+}
+
+void CGUIDialogSettingsBase::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL || setting->GetType() == SettingType::Unknown ||
+ setting->GetType() == SettingType::Action)
+ return;
+
+ UpdateSettingControl(setting->GetId(), true);
+}
+
+void CGUIDialogSettingsBase::OnSettingPropertyChanged(
+ const std::shared_ptr<const CSetting>& setting, const char* propertyName)
+{
+ if (setting == NULL || propertyName == NULL)
+ return;
+
+ UpdateSettingControl(setting->GetId());
+}
+
+std::string CGUIDialogSettingsBase::GetLocalizedString(uint32_t labelId) const
+{
+ return g_localizeStrings.Get(labelId);
+}
+
+void CGUIDialogSettingsBase::SetupView()
+{
+ SetupControls();
+}
+
+std::set<std::string> CGUIDialogSettingsBase::CreateSettings()
+{
+ FreeSettingsControls();
+
+ std::set<std::string> settingMap;
+
+ if (m_categories.size() <= 0)
+ return settingMap;
+
+ if (m_iCategory < 0 || m_iCategory >= (int)m_categories.size())
+ m_iCategory = 0;
+
+ CGUIControlGroupList* group = dynamic_cast<CGUIControlGroupList*>(GetControl(SETTINGS_GROUP_ID));
+ if (group == NULL)
+ return settingMap;
+
+ SettingCategoryPtr category = m_categories.at(m_iCategory);
+ if (category == NULL)
+ return settingMap;
+
+ // set the description of the current category
+ SetDescription(category->GetHelp());
+
+ const SettingGroupList& groups = category->GetGroups((SettingLevel)GetSettingLevel());
+ int iControlID = CONTROL_SETTINGS_START_CONTROL;
+ bool first = true;
+ for (SettingGroupList::const_iterator groupIt = groups.begin(); groupIt != groups.end();
+ ++groupIt)
+ {
+ if (*groupIt == NULL)
+ continue;
+
+ const SettingList& settings = (*groupIt)->GetSettings((SettingLevel)GetSettingLevel());
+ if (settings.size() <= 0)
+ continue;
+
+ std::shared_ptr<const CSettingControlTitle> title =
+ std::dynamic_pointer_cast<const CSettingControlTitle>((*groupIt)->GetControl());
+ bool hideSeparator = title ? title->IsSeparatorHidden() : false;
+ bool separatorBelowGroupLabel = title ? title->IsSeparatorBelowLabel() : false;
+ int groupLabel = (*groupIt)->GetLabel();
+
+ // hide the separator for the first settings grouplist if it
+ // is the very first item in the list (also above the label)
+ if (first)
+ {
+ first = false;
+ if (groupLabel <= 0)
+ hideSeparator = true;
+ }
+ else if (!separatorBelowGroupLabel && !hideSeparator)
+ AddSeparator(group->GetWidth(), iControlID);
+
+ if (groupLabel > 0)
+ AddGroupLabel(*groupIt, group->GetWidth(), iControlID);
+
+ if (separatorBelowGroupLabel && !hideSeparator)
+ AddSeparator(group->GetWidth(), iControlID);
+
+ for (SettingList::const_iterator settingIt = settings.begin(); settingIt != settings.end();
+ ++settingIt)
+ {
+ const std::shared_ptr<CSetting>& pSetting = *settingIt;
+ settingMap.insert(pSetting->GetId());
+ AddSetting(pSetting, group->GetWidth(), iControlID);
+ }
+ }
+
+ if (AllowResettingSettings() && !settingMap.empty())
+ {
+ // add "Reset" control
+ AddSeparator(group->GetWidth(), iControlID);
+ AddSetting(m_resetSetting, group->GetWidth(), iControlID);
+ }
+
+ // update our settings (turns controls on/off as appropriate)
+ UpdateSettings();
+
+ group->SetInvalid();
+
+ return settingMap;
+}
+
+std::string CGUIDialogSettingsBase::GetSettingsLabel(const std::shared_ptr<ISetting>& pSetting)
+{
+ return GetLocalizedString(pSetting->GetLabel());
+}
+
+void CGUIDialogSettingsBase::UpdateSettings()
+{
+ for (std::vector<BaseSettingControlPtr>::iterator it = m_settingControls.begin();
+ it != m_settingControls.end(); ++it)
+ {
+ BaseSettingControlPtr pSettingControl = *it;
+ std::shared_ptr<CSetting> pSetting = pSettingControl->GetSetting();
+ CGUIControl* pControl = pSettingControl->GetControl();
+ if (pSetting == NULL || pControl == NULL)
+ continue;
+
+ pSettingControl->UpdateFromSetting();
+ }
+}
+
+CGUIControl* CGUIDialogSettingsBase::AddSetting(const std::shared_ptr<CSetting>& pSetting,
+ float width,
+ int& iControlID)
+{
+ if (pSetting == NULL)
+ return NULL;
+
+ BaseSettingControlPtr pSettingControl;
+ CGUIControl* pControl = NULL;
+
+ // determine the label and any possible indentation in case of sub settings
+ std::string label = GetSettingsLabel(pSetting);
+ int parentLevels = 0;
+ std::shared_ptr<CSetting> parentSetting = GetSetting(pSetting->GetParent());
+ while (parentSetting != NULL)
+ {
+ parentLevels++;
+ parentSetting = GetSetting(parentSetting->GetParent());
+ }
+
+ if (parentLevels > 0)
+ {
+ // add additional 2 spaces indentation for anything past one level
+ std::string indentation;
+ for (int index = 1; index < parentLevels; index++)
+ indentation.append(" ");
+ label = StringUtils::Format(g_localizeStrings.Get(168), indentation, label);
+ }
+
+ // create the proper controls
+ if (!pSetting->GetControl())
+ return NULL;
+
+ std::string controlType = pSetting->GetControl()->GetType();
+ if (controlType == "toggle")
+ {
+ if (m_pOriginalRadioButton != NULL)
+ pControl = m_pOriginalRadioButton->Clone();
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUIRadioButtonControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlRadioButtonSetting(
+ static_cast<CGUIRadioButtonControl*>(pControl), iControlID, pSetting, this));
+ }
+ else if (controlType == "spinner")
+ {
+ if (m_pOriginalSpin != NULL)
+ pControl = new CGUISpinControlEx(*m_pOriginalSpin);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUISpinControlEx*>(pControl)->SetText(label);
+ pSettingControl.reset(new CGUIControlSpinExSetting(static_cast<CGUISpinControlEx*>(pControl),
+ iControlID, pSetting, this));
+ }
+ else if (controlType == "edit")
+ {
+ if (m_pOriginalEdit != NULL)
+ pControl = new CGUIEditControl(*m_pOriginalEdit);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUIEditControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlEditSetting(static_cast<CGUIEditControl*>(pControl),
+ iControlID, pSetting, this));
+ }
+ else if (controlType == "list")
+ {
+ if (m_pOriginalButton != NULL)
+ pControl = new CGUIButtonControl(*m_pOriginalButton);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlListSetting(static_cast<CGUIButtonControl*>(pControl),
+ iControlID, pSetting, this));
+ }
+ else if (controlType == "button" || controlType == "slider")
+ {
+ if (controlType == "button" ||
+ std::static_pointer_cast<const CSettingControlSlider>(pSetting->GetControl())->UsePopup())
+ {
+ if (m_pOriginalButton != NULL)
+ pControl = new CGUIButtonControl(*m_pOriginalButton);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlButtonSetting(static_cast<CGUIButtonControl*>(pControl),
+ iControlID, pSetting, this));
+ }
+ else
+ {
+ if (m_pOriginalSlider != NULL)
+ pControl = m_pOriginalSlider->Clone();
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUISettingsSliderControl*>(pControl)->SetText(label);
+ pSettingControl.reset(new CGUIControlSliderSetting(
+ static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this));
+ }
+ }
+ else if (controlType == "range")
+ {
+ if (m_pOriginalSlider != NULL)
+ pControl = m_pOriginalSlider->Clone();
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUISettingsSliderControl*>(pControl)->SetText(label);
+ pSettingControl.reset(new CGUIControlRangeSetting(
+ static_cast<CGUISettingsSliderControl*>(pControl), iControlID, pSetting, this));
+ }
+ else if (controlType == "label")
+ {
+ if (m_pOriginalButton != NULL)
+ pControl = new CGUIButtonControl(*m_pOriginalButton);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUIButtonControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlLabelSetting(static_cast<CGUIButtonControl*>(pControl),
+ iControlID, pSetting, this));
+ }
+ else if (controlType == "colorbutton")
+ {
+ if (m_pOriginalColorButton)
+ pControl = m_pOriginalColorButton->Clone();
+ if (pControl == nullptr)
+ return nullptr;
+
+ static_cast<CGUIColorButtonControl*>(pControl)->SetLabel(label);
+ pSettingControl.reset(new CGUIControlColorButtonSetting(
+ static_cast<CGUIColorButtonControl*>(pControl), iControlID, pSetting, this));
+ }
+ else
+ return nullptr;
+
+ if (pSetting->GetControl()->GetDelayed())
+ pSettingControl->SetDelayed();
+
+ return AddSettingControl(pControl, pSettingControl, width, iControlID);
+}
+
+CGUIControl* CGUIDialogSettingsBase::AddSeparator(float width, int& iControlID)
+{
+ if (m_pOriginalImage == NULL)
+ return NULL;
+
+ CGUIControl* pControl = new CGUIImage(*m_pOriginalImage);
+ if (pControl == NULL)
+ return NULL;
+
+ return AddSettingControl(pControl,
+ BaseSettingControlPtr(new CGUIControlSeparatorSetting(
+ static_cast<CGUIImage*>(pControl), iControlID, this)),
+ width, iControlID);
+}
+
+CGUIControl* CGUIDialogSettingsBase::AddGroupLabel(const std::shared_ptr<CSettingGroup>& group,
+ float width,
+ int& iControlID)
+{
+ if (m_pOriginalGroupTitle == NULL)
+ return NULL;
+
+ CGUIControl* pControl = new CGUILabelControl(*m_pOriginalGroupTitle);
+ if (pControl == NULL)
+ return NULL;
+
+ static_cast<CGUILabelControl*>(pControl)->SetLabel(GetSettingsLabel(group));
+
+ return AddSettingControl(pControl,
+ BaseSettingControlPtr(new CGUIControlGroupTitleSetting(
+ static_cast<CGUILabelControl*>(pControl), iControlID, this)),
+ width, iControlID);
+}
+
+CGUIControl* CGUIDialogSettingsBase::AddSettingControl(CGUIControl* pControl,
+ BaseSettingControlPtr pSettingControl,
+ float width,
+ int& iControlID)
+{
+ if (pControl == NULL)
+ {
+ pSettingControl.reset();
+ return NULL;
+ }
+
+ pControl->SetID(iControlID++);
+ pControl->SetVisible(true);
+ pControl->SetWidth(width);
+
+ CGUIControlGroupList* group = dynamic_cast<CGUIControlGroupList*>(GetControl(SETTINGS_GROUP_ID));
+ if (group != NULL)
+ {
+ pControl->AllocResources();
+ group->AddControl(pControl);
+ }
+ m_settingControls.push_back(pSettingControl);
+
+ return pControl;
+}
+
+void CGUIDialogSettingsBase::SetHeading(const CVariant& label)
+{
+ SetControlLabel(CONTROL_SETTINGS_LABEL, label);
+}
+
+void CGUIDialogSettingsBase::SetDescription(const CVariant& label)
+{
+ SetControlLabel(CONTROL_SETTINGS_DESCRIPTION, label);
+}
+
+void CGUIDialogSettingsBase::OnResetSettings()
+{
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{10041}, CVariant{10042}))
+ {
+ for (std::vector<BaseSettingControlPtr>::iterator it = m_settingControls.begin();
+ it != m_settingControls.end(); ++it)
+ {
+ std::shared_ptr<CSetting> setting = (*it)->GetSetting();
+ if (setting != NULL)
+ setting->Reset();
+ }
+ }
+}
+
+void CGUIDialogSettingsBase::OnClick(const BaseSettingControlPtr& pSettingControl)
+{
+ if (AllowResettingSettings() &&
+ pSettingControl->GetSetting()->GetId() == SETTINGS_RESET_SETTING_ID)
+ {
+ OnAction(CAction(ACTION_SETTINGS_RESET));
+ return;
+ }
+
+ // we need to first set the delayed setting and then execute OnClick()
+ // because OnClick() triggers OnSettingChanged() and there we need to
+ // know if the changed setting is delayed or not
+ if (pSettingControl->IsDelayed())
+ {
+ m_delayedSetting = pSettingControl;
+ // for some controls we need to update its displayed data/text before
+ // OnClick() is called after the delay timer has expired because
+ // otherwise the displayed value of the control does not match with
+ // the user's interaction
+ pSettingControl->UpdateFromControl();
+
+ // either start or restart the delay timer which will result in a call to
+ // the control's OnClick() method to update the setting's value
+ if (m_delayedTimer.IsRunning())
+ m_delayedTimer.Restart();
+ else
+ m_delayedTimer.Start(GetDelayMs());
+
+ return;
+ }
+
+ // if changing the setting fails
+ // we need to restore the proper state
+ if (!pSettingControl->OnClick())
+ pSettingControl->UpdateFromSetting();
+}
+
+void CGUIDialogSettingsBase::UpdateSettingControl(const std::string& settingId,
+ bool updateDisplayOnly /* = false */)
+{
+ if (settingId.empty())
+ return;
+
+ return UpdateSettingControl(GetSettingControl(settingId), updateDisplayOnly);
+}
+
+void CGUIDialogSettingsBase::UpdateSettingControl(const BaseSettingControlPtr& pSettingControl,
+ bool updateDisplayOnly /* = false */)
+{
+ if (pSettingControl == NULL)
+ return;
+
+ // we send a thread message so that it's processed the following frame (some settings won't
+ // like being changed during Render())
+ // param2 = 1 for "only update the current value"
+ CGUIMessage message(GUI_MSG_UPDATE_ITEM, GetID(), pSettingControl->GetID(), 0,
+ updateDisplayOnly ? 1 : 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message, GetID());
+}
+
+void CGUIDialogSettingsBase::SetControlLabel(int controlId, const CVariant& label)
+{
+ if (GetControl(controlId) == NULL)
+ return;
+
+ if (label.isString())
+ SET_CONTROL_LABEL(controlId, label.asString());
+ else if (label.isInteger() && label.asInteger() >= 0)
+ {
+ int labelId = static_cast<uint32_t>(label.asInteger());
+ std::string localizedString = GetLocalizedString(labelId);
+ if (!localizedString.empty())
+ SET_CONTROL_LABEL(controlId, localizedString);
+ else
+ SET_CONTROL_LABEL(controlId, labelId);
+ }
+ else
+ SET_CONTROL_LABEL(controlId, "");
+}
+
+BaseSettingControlPtr CGUIDialogSettingsBase::GetSettingControl(const std::string& strSetting)
+{
+ for (std::vector<BaseSettingControlPtr>::iterator control = m_settingControls.begin();
+ control != m_settingControls.end(); ++control)
+ {
+ if ((*control)->GetSetting() != NULL && (*control)->GetSetting()->GetId() == strSetting)
+ return *control;
+ }
+
+ return BaseSettingControlPtr();
+}
+
+BaseSettingControlPtr CGUIDialogSettingsBase::GetSettingControl(int controlId)
+{
+ if (controlId < CONTROL_SETTINGS_START_CONTROL ||
+ controlId >= (int)(CONTROL_SETTINGS_START_CONTROL + m_settingControls.size()))
+ return BaseSettingControlPtr();
+
+ return m_settingControls[controlId - CONTROL_SETTINGS_START_CONTROL];
+}
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsBase.h b/xbmc/settings/dialogs/GUIDialogSettingsBase.h
new file mode 100644
index 0000000..a633c65
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsBase.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#pragma once
+
+#include "guilib/GUIDialog.h"
+#include "settings/SettingControl.h"
+#include "settings/lib/ISettingCallback.h"
+#include "threads/Timer.h"
+#include "utils/ILocalizer.h"
+
+#include <set>
+#include <vector>
+
+#define CONTROL_SETTINGS_LABEL 2
+#define CONTROL_SETTINGS_DESCRIPTION 6
+
+#define CONTROL_SETTINGS_OKAY_BUTTON 28
+#define CONTROL_SETTINGS_CANCEL_BUTTON 29
+#define CONTROL_SETTINGS_CUSTOM_BUTTON 30
+
+#define CONTROL_SETTINGS_START_BUTTONS -100
+#define CONTROL_SETTINGS_START_CONTROL -80
+
+#define SETTINGS_RESET_SETTING_ID "settings.reset"
+#define SETTINGS_EMPTY_CATEGORY_ID "categories.empty"
+
+class CGUIControl;
+class CGUIControlBaseSetting;
+class CGUIImage;
+class CGUISpinControlEx;
+class CGUIEditControl;
+class CGUIButtonControl;
+class CGUIRadioButtonControl;
+class CGUISettingsSliderControl;
+class CGUILabelControl;
+class CGUIColorButtonControl;
+
+class CSetting;
+class CSettingAction;
+class CSettingCategory;
+class CSettingGroup;
+class CSettingSection;
+
+class CVariant;
+
+class ISetting;
+
+typedef std::shared_ptr<CGUIControlBaseSetting> BaseSettingControlPtr;
+
+class CGUIDialogSettingsBase : public CGUIDialog,
+ public CSettingControlCreator,
+ public ILocalizer,
+ protected ITimerCallback,
+ protected ISettingCallback
+{
+public:
+ CGUIDialogSettingsBase(int windowId, const std::string& xmlFile);
+ ~CGUIDialogSettingsBase() override;
+
+ // specializations of CGUIControl
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction& action) override;
+ bool OnBack(int actionID) override;
+ void DoProcess(unsigned int currentTime, CDirtyRegionList& dirtyregions) override;
+
+ virtual bool IsConfirmed() const { return m_confirmed; }
+
+ // implementation of ILocalizer
+ std::string Localize(std::uint32_t code) const override { return GetLocalizedString(code); }
+
+protected:
+ // specializations of CGUIWindow
+ void OnInitWindow() override;
+
+ // implementations of ITimerCallback
+ void OnTimeout() override;
+
+ // implementations of ISettingCallback
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName) override;
+
+ // new virtual methods
+ virtual bool AllowResettingSettings() const { return true; }
+ virtual int GetSettingLevel() const { return 0; }
+ virtual std::shared_ptr<CSettingSection> GetSection() = 0;
+ virtual std::shared_ptr<CSetting> GetSetting(const std::string& settingId) = 0;
+ virtual std::chrono::milliseconds GetDelayMs() const { return std::chrono::milliseconds(1500); }
+ virtual std::string GetLocalizedString(uint32_t labelId) const;
+
+ virtual bool OnOkay()
+ {
+ m_confirmed = true;
+ return true;
+ }
+ virtual void OnCancel() {}
+
+ virtual void SetupView();
+ virtual std::set<std::string> CreateSettings();
+ virtual void UpdateSettings();
+
+ /*!
+ \brief Get the name for the setting entry
+
+ Used as virtual to allow related settings dialog to give a std::string name of the setting.
+ If not used on own dialog class it handle the string from int CSetting::GetLabel(),
+ This must also be used if on related dialog no special entry is wanted.
+
+ \param pSetting Base settings class which need the name
+ \return Name used on settings dialog
+ */
+ virtual std::string GetSettingsLabel(const std::shared_ptr<ISetting>& pSetting);
+
+ virtual CGUIControl* AddSetting(const std::shared_ptr<CSetting>& pSetting,
+ float width,
+ int& iControlID);
+ virtual CGUIControl* AddSettingControl(CGUIControl* pControl,
+ BaseSettingControlPtr pSettingControl,
+ float width,
+ int& iControlID);
+
+ virtual void SetupControls(bool createSettings = true);
+ virtual void FreeControls();
+ virtual void DeleteControls();
+ virtual void FreeSettingsControls();
+
+ virtual void SetHeading(const CVariant& label);
+ virtual void SetDescription(const CVariant& label);
+
+ virtual void OnResetSettings();
+
+ /*!
+ \brief A setting control has been interacted with by the user
+
+ This method is called when the user manually interacts (clicks,
+ edits) with a setting control. It contains handling for both
+ delayed and undelayed settings and either starts the delay timer
+ or triggers the setting change which, on success, results in a
+ callback to OnSettingChanged().
+
+ \param pSettingControl Setting control that has been interacted with
+ */
+ virtual void OnClick(const BaseSettingControlPtr& pSettingControl);
+
+ void UpdateSettingControl(const std::string& settingId, bool updateDisplayOnly = false);
+ void UpdateSettingControl(const BaseSettingControlPtr& pSettingControl,
+ bool updateDisplayOnly = false);
+ void SetControlLabel(int controlId, const CVariant& label);
+
+ BaseSettingControlPtr GetSettingControl(const std::string& setting);
+ BaseSettingControlPtr GetSettingControl(int controlId);
+
+ CGUIControl* AddSeparator(float width, int& iControlID);
+ CGUIControl* AddGroupLabel(const std::shared_ptr<CSettingGroup>& group,
+ float width,
+ int& iControlID);
+
+ std::vector<std::shared_ptr<CSettingCategory>> m_categories;
+ std::vector<BaseSettingControlPtr> m_settingControls;
+
+ int m_iSetting;
+ int m_iCategory;
+ std::shared_ptr<CSettingAction> m_resetSetting;
+ std::shared_ptr<CSettingCategory> m_dummyCategory;
+
+ CGUISpinControlEx* m_pOriginalSpin;
+ CGUISettingsSliderControl* m_pOriginalSlider;
+ CGUIRadioButtonControl* m_pOriginalRadioButton;
+ CGUIColorButtonControl* m_pOriginalColorButton;
+ CGUIButtonControl* m_pOriginalCategoryButton;
+ CGUIButtonControl* m_pOriginalButton;
+ CGUIEditControl* m_pOriginalEdit;
+ CGUIImage* m_pOriginalImage;
+ CGUILabelControl* m_pOriginalGroupTitle;
+ bool m_newOriginalEdit;
+
+ BaseSettingControlPtr
+ m_delayedSetting; ///< Current delayed setting \sa CBaseSettingControl::SetDelayed()
+ CTimer m_delayedTimer; ///< Delayed setting timer
+
+ bool m_confirmed;
+ int m_focusedControl, m_fadedControl;
+};
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.cpp b/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.cpp
new file mode 100644
index 0000000..e01edfb
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014-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 "GUIDialogSettingsManagerBase.h"
+
+#include "settings/lib/SettingsManager.h"
+
+#include <cassert>
+
+CGUIDialogSettingsManagerBase::CGUIDialogSettingsManagerBase(int windowId, const std::string &xmlFile)
+ : CGUIDialogSettingsBase(windowId, xmlFile)
+{ }
+
+CGUIDialogSettingsManagerBase::~CGUIDialogSettingsManagerBase() = default;
+
+std::shared_ptr<CSetting> CGUIDialogSettingsManagerBase::GetSetting(const std::string &settingId)
+{
+ assert(GetSettingsManager() != nullptr);
+
+ return GetSettingsManager()->GetSetting(settingId);
+}
+
+bool CGUIDialogSettingsManagerBase::OnOkay()
+{
+ if (Save())
+ {
+ CGUIDialogSettingsBase::OnOkay();
+ return true;
+ }
+
+ return false;
+}
+
+std::set<std::string> CGUIDialogSettingsManagerBase::CreateSettings()
+{
+ assert(GetSettingsManager() != nullptr);
+
+ std::set<std::string> settings = CGUIDialogSettingsBase::CreateSettings();
+
+ if (!settings.empty())
+ GetSettingsManager()->RegisterCallback(this, settings);
+
+ return settings;
+}
+
+void CGUIDialogSettingsManagerBase::FreeSettingsControls()
+{
+ CGUIDialogSettingsBase::FreeSettingsControls();
+
+ if (GetSettingsManager() != nullptr)
+ GetSettingsManager()->UnregisterCallback(this);
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManagerBase::CreateControl(const std::string &controlType) const
+{
+ assert(GetSettingsManager() != nullptr);
+
+ return GetSettingsManager()->CreateControl(controlType);
+}
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.h b/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.h
new file mode 100644
index 0000000..42c3c31
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsManagerBase.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014-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.
+ */
+
+#pragma once
+
+#include "settings/dialogs/GUIDialogSettingsBase.h"
+
+class CSettingsManager;
+
+class CGUIDialogSettingsManagerBase : public CGUIDialogSettingsBase
+{
+public:
+ CGUIDialogSettingsManagerBase(int windowId, const std::string &xmlFile);
+ ~CGUIDialogSettingsManagerBase() override;
+
+protected:
+ virtual bool Save() = 0;
+ virtual CSettingsManager* GetSettingsManager() const = 0;
+
+ // implementation of CGUIDialogSettingsBase
+ std::shared_ptr<CSetting> GetSetting(const std::string &settingId) override;
+ bool OnOkay() override;
+
+ std::set<std::string> CreateSettings() override;
+ void FreeSettingsControls() override;
+
+ // implementation of ISettingControlCreator
+ std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const override;
+};
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsManualBase.cpp b/xbmc/settings/dialogs/GUIDialogSettingsManualBase.cpp
new file mode 100644
index 0000000..84114db
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsManualBase.cpp
@@ -0,0 +1,1617 @@
+/*
+ * 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 "GUIDialogSettingsManualBase.h"
+
+#include "settings/SettingAddon.h"
+#include "settings/SettingDateTime.h"
+#include "settings/SettingPath.h"
+#include "settings/SettingUtils.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/lib/SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+#include <string>
+#include <vector>
+
+CGUIDialogSettingsManualBase::CGUIDialogSettingsManualBase(int windowId, const std::string &xmlFile)
+ : CGUIDialogSettingsManagerBase(windowId, xmlFile)
+ , m_settingsManager(nullptr)
+{ }
+
+CGUIDialogSettingsManualBase::~CGUIDialogSettingsManualBase()
+{
+ if (GetSettingsManager() != nullptr)
+ {
+ GetSettingsManager()->Clear();
+ m_section = nullptr;
+ delete GetSettingsManager();
+ }
+}
+
+void CGUIDialogSettingsManualBase::SetupView()
+{
+ InitializeSettings();
+
+ if (GetSettingsManager() != nullptr)
+ {
+ // add the created setting section to the settings manager and mark it as ready
+ GetSettingsManager()->AddSection(m_section);
+ GetSettingsManager()->SetInitialized();
+ GetSettingsManager()->SetLoaded();
+ }
+
+ CGUIDialogSettingsBase::SetupView();
+}
+
+CSettingsManager* CGUIDialogSettingsManualBase::GetSettingsManager() const
+{
+ if (m_settingsManager == nullptr)
+ m_settingsManager = new CSettingsManager();
+
+ return m_settingsManager;
+}
+
+void CGUIDialogSettingsManualBase::InitializeSettings()
+{
+ if (GetSettingsManager() != nullptr)
+ {
+ GetSettingsManager()->Clear();
+ m_section = NULL;
+
+ // create a std::make_shared<section
+ m_section = std::make_shared<CSettingSection>(GetProperty("xmlfile").asString(), GetSettingsManager());
+ }
+}
+
+SettingCategoryPtr CGUIDialogSettingsManualBase::AddCategory(const std::string &id, int label, int help /* = -1 */)
+{
+ if (id.empty())
+ return NULL;
+
+ SettingCategoryPtr category = std::make_shared<CSettingCategory>(id, GetSettingsManager());
+ if (category == NULL)
+ return NULL;
+
+ category->SetLabel(label);
+ if (help >= 0)
+ category->SetHelp(help);
+
+ m_section->AddCategory(category);
+ return category;
+}
+
+SettingGroupPtr CGUIDialogSettingsManualBase::AddGroup(const SettingCategoryPtr& category,
+ int label /* = -1 */,
+ int help /* = -1 */,
+ bool separatorBelowLabel /* = true */,
+ bool hideSeparator /* = false */)
+{
+ if (category == NULL)
+ return NULL;
+
+ size_t groups = category->GetGroups().size();
+
+ SettingGroupPtr group = std::make_shared<CSettingGroup>(StringUtils::Format("{0}", groups + 1), GetSettingsManager());
+ if (group == NULL)
+ return NULL;
+
+ if (label >= 0)
+ group->SetLabel(label);
+ if (help >= 0)
+ group->SetHelp(help);
+ group->SetControl(GetTitleControl(separatorBelowLabel, hideSeparator));
+
+ category->AddGroup(group);
+ return group;
+}
+
+std::shared_ptr<CSettingBool> CGUIDialogSettingsManualBase::AddToggle(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ bool value,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingBool> setting = std::make_shared<CSettingBool>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetCheckmarkControl(delayed));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddEdit(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum /* = 0 */,
+ int step /* = 1 */,
+ int maximum /* = 0 */,
+ bool verifyNewValue /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, minimum, step, maximum, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetEditControl("integer", delayed, false, verifyNewValue, heading));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingNumber> CGUIDialogSettingsManualBase::AddEdit(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum /* = 0.0f */,
+ float step /* = 1.0f */,
+ float maximum /* = 0.0f */,
+ bool verifyNewValue /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> setting = std::make_shared<CSettingNumber>(id, label, value, minimum, step, maximum, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetEditControl("number", delayed, false, verifyNewValue, heading));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddEdit(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty /* = false */,
+ bool hidden /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetEditControl("string", delayed, hidden, false, heading));
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddIp(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetEditControl("ip", delayed, false, false, heading));
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddPasswordMd5(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetEditControl("md5", delayed, false, false, heading));
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingAction> CGUIDialogSettingsManualBase::AddButton(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& data /* = "" */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingAction> setting = std::make_shared<CSettingAction>(id, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("action", delayed));
+ setting->SetData(data);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddInfoLabelButton(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& info,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, info, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("infolabel", false));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingAddon> CGUIDialogSettingsManualBase::AddAddon(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ ADDON::AddonType addonType,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool hideValue /* = false */,
+ bool showInstalledAddons /* = true */,
+ bool showInstallableAddons /* = false */,
+ bool showMoreAddons /* = true */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingAddon> setting = std::make_shared<CSettingAddon>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("addon", delayed, heading, hideValue, showInstalledAddons, showInstallableAddons, showMoreAddons));
+ setting->SetAddonType(addonType);
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingPath> CGUIDialogSettingsManualBase::AddPath(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool writable /* = true */,
+ const std::vector<std::string>& sources /* = std::vector<std::string>() */,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool hideValue /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingPath> setting = std::make_shared<CSettingPath>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("path", delayed, heading, hideValue));
+ setting->SetWritable(writable);
+ setting->SetSources(sources);
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingDate> CGUIDialogSettingsManualBase::AddDate(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingDate> setting = std::make_shared<CSettingDate>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("date", delayed, heading));
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingTime> CGUIDialogSettingsManualBase::AddTime(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty /* = false */,
+ int heading /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingTime> setting = std::make_shared<CSettingTime>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetButtonControl("time", delayed, heading));
+ setting->SetAllowEmpty(allowEmpty);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ StringSettingOptionsFiller filler,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed));
+ setting->SetOptionsFiller(filler, this);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSpinner(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ int formatLabel /* = -1 */,
+ int minimumLabel /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed, minimumLabel, formatLabel));
+ setting->SetMinimum(minimum);
+ setting->SetStep(step);
+ setting->SetMaximum(maximum);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& formatString,
+ int minimumLabel /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed, minimumLabel, -1, formatString));
+ setting->SetMinimum(minimum);
+ setting->SetStep(step);
+ setting->SetMaximum(maximum);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const TranslatableIntegerSettingOptions& entries,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed));
+ setting->SetTranslatableOptions(entries);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const IntegerSettingOptions& entries,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed));
+ setting->SetOptions(entries);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ IntegerSettingOptionsFiller filler,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("string", delayed));
+ setting->SetOptionsFiller(filler, this);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingNumber> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ int formatLabel /* = -1 */,
+ int minimumLabel /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> setting = std::make_shared<CSettingNumber>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("number", delayed, minimumLabel, formatLabel));
+ setting->SetMinimum(static_cast<double>(minimum));
+ setting->SetStep(static_cast<double>(step));
+ setting->SetMaximum(static_cast<double>(maximum));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingNumber> CGUIDialogSettingsManualBase::AddSpinner(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& formatString,
+ int minimumLabel /* = -1 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> setting = std::make_shared<CSettingNumber>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSpinnerControl("number", delayed, minimumLabel, -1, formatString));
+ setting->SetMinimum(static_cast<double>(minimum));
+ setting->SetStep(static_cast<double>(step));
+ setting->SetMaximum(static_cast<double>(maximum));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingString> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ StringSettingOptionsFiller filler,
+ int heading,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> setting = std::make_shared<CSettingString>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetListControl("string", false, heading, false, nullptr, details));
+ setting->SetOptionsFiller(filler, this);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const TranslatableIntegerSettingOptions& entries,
+ int heading,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetListControl("integer", false, heading, false, nullptr, details));
+ setting->SetTranslatableOptions(entries);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const IntegerSettingOptions& entries,
+ int heading,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetListControl("integer", false, heading, false, nullptr, details));
+ setting->SetOptions(entries);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ IntegerSettingOptionsFiller filler,
+ int heading,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetListControl("integer", false, heading, false, nullptr, details));
+ setting->SetOptionsFiller(filler, this);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<std::string> values,
+ StringSettingOptionsFiller filler,
+ int heading,
+ int minimumItems /* = 0 */,
+ int maximumItems /* = -1 */,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingString> settingDefinition = std::make_shared<CSettingString>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetOptionsFiller(filler, this);
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ for (std::vector<std::string>::const_iterator itValue = values.begin(); itValue != values.end(); ++itValue)
+ valueList.emplace_back(*itValue);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetListControl("string", false, heading, true, nullptr, details));
+ setting->SetMinimumItems(minimumItems);
+ setting->SetMaximumItems(maximumItems);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ const TranslatableIntegerSettingOptions& entries,
+ int heading,
+ int minimumItems /* = 0 */,
+ int maximumItems /* = -1 */,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> settingDefinition = std::make_shared<CSettingInt>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetTranslatableOptions(entries);
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ for (std::vector<int>::const_iterator itValue = values.begin(); itValue != values.end(); ++itValue)
+ valueList.emplace_back(*itValue);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetListControl("integer", false, heading, true, nullptr, details));
+ setting->SetMinimumItems(minimumItems);
+ setting->SetMaximumItems(maximumItems);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ const IntegerSettingOptions& entries,
+ int heading,
+ int minimumItems /* = 0 */,
+ int maximumItems /* = -1 */,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || entries.empty() ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> settingDefinition = std::make_shared<CSettingInt>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetOptions(entries);
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ for (std::vector<int>::const_iterator itValue = values.begin(); itValue != values.end(); ++itValue)
+ valueList.emplace_back(*itValue);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetListControl("integer", false, heading, true, nullptr, details));
+ setting->SetMinimumItems(minimumItems);
+ setting->SetMaximumItems(maximumItems);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddList(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ IntegerSettingOptionsFiller filler,
+ int heading,
+ int minimumItems /* = 0 */,
+ int maximumItems /* = -1 */,
+ bool visible /* = true */,
+ int help /* = -1 */,
+ SettingControlListValueFormatter formatter /* = NULL */,
+ bool details /* = false */)
+{
+ if (group == NULL || id.empty() || label < 0 || filler == NULL ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> settingDefinition = std::make_shared<CSettingInt>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetOptionsFiller(filler, this);
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ for (std::vector<int>::const_iterator itValue = values.begin(); itValue != values.end(); ++itValue)
+ valueList.emplace_back(*itValue);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetListControl("integer", false, heading, true, formatter, details));
+ setting->SetMinimumItems(minimumItems);
+ setting->SetMaximumItems(maximumItems);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddPercentageSlider(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int formatLabel,
+ int step /* = 1 */,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("percentage", delayed, heading, usePopup, formatLabel));
+ setting->SetMinimum(0);
+ setting->SetStep(step);
+ setting->SetMaximum(100);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddPercentageSlider(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const std::string& formatString,
+ int step /* = 1 */,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("percentage", delayed, heading, usePopup, -1, formatString));
+ setting->SetMinimum(0);
+ setting->SetStep(step);
+ setting->SetMaximum(100);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSlider(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int formatLabel,
+ int minimum,
+ int step,
+ int maximum,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("integer", delayed, heading, usePopup, formatLabel));
+ setting->SetMinimum(minimum);
+ setting->SetStep(step);
+ setting->SetMaximum(maximum);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingInt> CGUIDialogSettingsManualBase::AddSlider(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const std::string& formatString,
+ int minimum,
+ int step,
+ int maximum,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> setting = std::make_shared<CSettingInt>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("integer", delayed, heading, usePopup, -1, formatString));
+ setting->SetMinimum(minimum);
+ setting->SetStep(step);
+ setting->SetMaximum(maximum);
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingNumber> CGUIDialogSettingsManualBase::AddSlider(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ int formatLabel,
+ float minimum,
+ float step,
+ float maximum,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> setting = std::make_shared<CSettingNumber>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("number", delayed, heading, usePopup, formatLabel));
+ setting->SetMinimum(static_cast<double>(minimum));
+ setting->SetStep(static_cast<double>(step));
+ setting->SetMaximum(static_cast<double>(maximum));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingNumber> CGUIDialogSettingsManualBase::AddSlider(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ const std::string& formatString,
+ float minimum,
+ float step,
+ float maximum,
+ int heading /* = -1 */,
+ bool usePopup /* = false */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> setting = std::make_shared<CSettingNumber>(id, label, value, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ setting->SetControl(GetSliderControl("number", delayed, heading, usePopup, -1, formatString));
+ setting->SetMinimum(static_cast<double>(minimum));
+ setting->SetStep(static_cast<double>(step));
+ setting->SetMaximum(static_cast<double>(maximum));
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddPercentageRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int valueFormatLabel,
+ int step /* = 1 */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, 0, step, 100, "percentage", formatLabel, valueFormatLabel, "", delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddPercentageRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ const std::string& valueFormatString /* = "{:d} %" */,
+ int step /* = 1 */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, 0, step, 100, "percentage", formatLabel, -1, valueFormatString, delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "integer", formatLabel, valueFormatLabel, "", delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString /* = "{:d}" */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "integer", formatLabel, -1, valueFormatString, delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ int valueFormatLabel,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "number", formatLabel, valueFormatLabel, "", delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& valueFormatString /* = "{:.1f}" */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "number", formatLabel, -1, valueFormatString, delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddDateRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "date", formatLabel, valueFormatLabel, "", delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddDateRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString /* = "" */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "date", formatLabel, -1, valueFormatString, delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddTimeRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "time", formatLabel, valueFormatLabel, "", delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddTimeRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString /* = "mm:ss" */,
+ int formatLabel /* = 21469 */,
+ bool delayed /* = false */,
+ bool visible /* = true */,
+ int help /* = -1 */)
+{
+ return AddRange(group, id, label, level, valueLower, valueUpper, minimum, step, maximum, "time", formatLabel, -1, valueFormatString, delayed, visible, help);
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& format,
+ int formatLabel,
+ int valueFormatLabel,
+ const std::string& valueFormatString,
+ bool delayed,
+ bool visible,
+ int help)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingInt> settingDefinition = std::make_shared<CSettingInt>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetMinimum(minimum);
+ settingDefinition->SetStep(step);
+ settingDefinition->SetMaximum(maximum);
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ valueList.emplace_back(valueLower);
+ valueList.emplace_back(valueUpper);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetRangeControl(format, delayed, formatLabel, valueFormatLabel, valueFormatString));
+ setting->SetMinimumItems(2);
+ setting->SetMaximumItems(2);
+
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+std::shared_ptr<CSettingList> CGUIDialogSettingsManualBase::AddRange(
+ const SettingGroupPtr& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& format,
+ int formatLabel,
+ int valueFormatLabel,
+ const std::string& valueFormatString,
+ bool delayed,
+ bool visible,
+ int help)
+{
+ if (group == NULL || id.empty() || label < 0 ||
+ GetSetting(id) != NULL)
+ return NULL;
+
+ std::shared_ptr<CSettingNumber> settingDefinition = std::make_shared<CSettingNumber>(id, GetSettingsManager());
+ if (settingDefinition == NULL)
+ return NULL;
+
+ settingDefinition->SetMinimum(static_cast<double>(minimum));
+ settingDefinition->SetStep(static_cast<double>(step));
+ settingDefinition->SetMaximum(static_cast<double>(maximum));
+
+ std::shared_ptr<CSettingList> setting = std::make_shared<CSettingList>(id, settingDefinition, label, GetSettingsManager());
+ if (setting == NULL)
+ return NULL;
+
+ std::vector<CVariant> valueList;
+ valueList.emplace_back(valueLower);
+ valueList.emplace_back(valueUpper);
+ SettingList settingValues;
+ if (!CSettingUtils::ValuesToList(setting, valueList, settingValues))
+ return NULL;
+ // setting the default will also set the actual value on an unchanged setting
+ setting->SetDefault(settingValues);
+
+ setting->SetControl(GetRangeControl(format, delayed, formatLabel, valueFormatLabel, valueFormatString));
+ setting->SetMinimumItems(2);
+ setting->SetMaximumItems(2);
+
+ setSettingDetails(setting, level, visible, help);
+
+ group->AddSetting(setting);
+ return setting;
+}
+
+void CGUIDialogSettingsManualBase::setSettingDetails(const std::shared_ptr<CSetting>& setting,
+ SettingLevel level,
+ bool visible,
+ int help)
+{
+ if (setting == NULL)
+ return;
+
+ if (level < SettingLevel::Basic)
+ level = SettingLevel::Basic;
+ else if (level > SettingLevel::Expert)
+ level = SettingLevel::Expert;
+
+ setting->SetLevel(level);
+ setting->SetVisible(visible);
+ if (help >= 0)
+ setting->SetHelp(help);
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetCheckmarkControl(bool delayed /* = false */)
+{
+ std::shared_ptr<CSettingControlCheckmark> control = std::make_shared<CSettingControlCheckmark>();
+ control->SetDelayed(delayed);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetTitleControl(bool separatorBelowLabel /* = true */, bool hideSeparator /* = false */)
+{
+ std::shared_ptr<CSettingControlTitle> control = std::make_shared<CSettingControlTitle>();
+ control->SetSeparatorBelowLabel(separatorBelowLabel);
+ control->SetSeparatorHidden(hideSeparator);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetEditControl(const std::string &format, bool delayed /* = false */, bool hidden /* = false */, bool verifyNewValue /* = false */, int heading /* = -1 */)
+{
+ std::shared_ptr<CSettingControlEdit> control = std::make_shared<CSettingControlEdit>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ control->SetHidden(hidden);
+ control->SetVerifyNewValue(verifyNewValue);
+ control->SetHeading(heading);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetButtonControl(const std::string &format, bool delayed /* = false */, int heading /* = -1 */, bool hideValue /* = false */,
+ bool showInstalledAddons /* = true */, bool showInstallableAddons /* = false */, bool showMoreAddons /* = true */)
+{
+ std::shared_ptr<CSettingControlButton> control = std::make_shared<CSettingControlButton>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ control->SetHeading(heading);
+ control->SetHideValue(hideValue);
+ control->SetShowInstalledAddons(showInstalledAddons);
+ control->SetShowInstallableAddons(showInstallableAddons);
+ control->SetShowMoreAddons(showMoreAddons);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetSpinnerControl(const std::string &format, bool delayed /* = false */, int minimumLabel /* = -1 */, int formatLabel /* = -1 */, const std::string &formatString /* = "" */)
+{
+ std::shared_ptr<CSettingControlSpinner> control = std::make_shared<CSettingControlSpinner>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ if (formatLabel >= 0)
+ control->SetFormatLabel(formatLabel);
+ if (!formatString.empty())
+ control->SetFormatString(formatString);
+ if (minimumLabel >= 0)
+ control->SetMinimumLabel(minimumLabel);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetListControl(
+ const std::string& format,
+ bool delayed /* = false */,
+ int heading /* = -1 */,
+ bool multiselect /* = false */,
+ SettingControlListValueFormatter formatter /* = NULL */,
+ bool details /* = false */)
+{
+ std::shared_ptr<CSettingControlList> control = std::make_shared<CSettingControlList>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ control->SetHeading(heading);
+ control->SetMultiSelect(multiselect);
+ control->SetFormatter(formatter);
+ control->SetUseDetails(details);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetSliderControl(const std::string &format, bool delayed /* = false */, int heading /* = -1 */, bool usePopup /* = false */,
+ int formatLabel /* = -1 */, const std::string &formatString /* = "" */)
+{
+ std::shared_ptr<CSettingControlSlider> control = std::make_shared<CSettingControlSlider>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ if (heading >= 0)
+ control->SetHeading(heading);
+ control->SetPopup(usePopup);
+ if (formatLabel >= 0)
+ control->SetFormatLabel(formatLabel);
+ if (!formatString.empty())
+ control->SetFormatString(formatString);
+
+ return control;
+}
+
+std::shared_ptr<ISettingControl> CGUIDialogSettingsManualBase::GetRangeControl(const std::string &format, bool delayed /* = false */, int formatLabel /* = -1 */,
+ int valueFormatLabel /* = -1 */, const std::string &valueFormatString /* = "" */)
+{
+ std::shared_ptr<CSettingControlRange> control = std::make_shared<CSettingControlRange>();
+ if (!control->SetFormat(format))
+ return NULL;
+
+ control->SetDelayed(delayed);
+ if (formatLabel >= 0)
+ control->SetFormatLabel(formatLabel);
+ if (valueFormatLabel >= 0)
+ control->SetValueFormatLabel(valueFormatLabel);
+ if (!valueFormatString.empty())
+ control->SetValueFormat(valueFormatString);
+
+ return control;
+}
diff --git a/xbmc/settings/dialogs/GUIDialogSettingsManualBase.h b/xbmc/settings/dialogs/GUIDialogSettingsManualBase.h
new file mode 100644
index 0000000..338259c
--- /dev/null
+++ b/xbmc/settings/dialogs/GUIDialogSettingsManualBase.h
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "addons/IAddon.h"
+#include "settings/dialogs/GUIDialogSettingsManagerBase.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "settings/lib/SettingLevel.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+class CSetting;
+class CSettingAction;
+class CSettingAddon;
+class CSettingBool;
+class CSettingCategory;
+class CSettingDate;
+class CSettingGroup;
+class CSettingInt;
+class CSettingList;
+class CSettingNumber;
+class CSettingPath;
+class CSettingSection;
+class CSettingString;
+class CSettingsManager;
+class CSettingTime;
+
+class CGUIDialogSettingsManualBase : public CGUIDialogSettingsManagerBase
+{
+public:
+ CGUIDialogSettingsManualBase(int windowId, const std::string &xmlFile);
+ ~CGUIDialogSettingsManualBase() override;
+
+protected:
+ // implementation of CGUIDialogSettingsBase
+ std::shared_ptr<CSettingSection> GetSection() override { return m_section; }
+ void SetupView() override;
+
+ // implementation of CGUIDialogSettingsManagerBase
+ CSettingsManager* GetSettingsManager() const override;
+
+ virtual void InitializeSettings();
+
+ std::shared_ptr<CSettingCategory> AddCategory(const std::string &id, int label, int help = -1);
+ std::shared_ptr<CSettingGroup> AddGroup(const std::shared_ptr<CSettingCategory>& category,
+ int label = -1,
+ int help = -1,
+ bool separatorBelowLabel = true,
+ bool hideSeparator = false);
+ // checkmark control
+ std::shared_ptr<CSettingBool> AddToggle(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ bool value,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ // edit controls
+ std::shared_ptr<CSettingInt> AddEdit(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum = 0,
+ int step = 1,
+ int maximum = 0,
+ bool verifyNewValue = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingNumber> AddEdit(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum = 0.0f,
+ float step = 1.0f,
+ float maximum = 0.0f,
+ bool verifyNewValue = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingString> AddEdit(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty = false,
+ bool hidden = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingString> AddIp(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingString> AddPasswordMd5(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ // button controls
+ std::shared_ptr<CSettingAction> AddButton(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& data = "",
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingString> AddInfoLabelButton(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& info,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingAddon> AddAddon(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ ADDON::AddonType addonType,
+ bool allowEmpty = false,
+ int heading = -1,
+ bool hideValue = false,
+ bool showInstalledAddons = true,
+ bool showInstallableAddons = false,
+ bool showMoreAddons = true,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingPath> AddPath(
+ const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool writable = true,
+ const std::vector<std::string>& sources = std::vector<std::string>(),
+ bool allowEmpty = false,
+ int heading = -1,
+ bool hideValue = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingDate> AddDate(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingTime> AddTime(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ bool allowEmpty = false,
+ int heading = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+
+ // spinner controls
+ std::shared_ptr<CSettingString> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ StringSettingOptionsFiller filler,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ int formatLabel = -1,
+ int minimumLabel = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& formatString,
+ int minimumLabel = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const TranslatableIntegerSettingOptions& entries,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const IntegerSettingOptions& entries,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ IntegerSettingOptionsFiller filler,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingNumber> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ int formatLabel = -1,
+ int minimumLabel = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingNumber> AddSpinner(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& formatString,
+ int minimumLabel = -1,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+
+ // list controls
+ std::shared_ptr<CSettingString> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ const std::string& value,
+ StringSettingOptionsFiller filler,
+ int heading,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingInt> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const TranslatableIntegerSettingOptions& entries,
+ int heading,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingInt> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const IntegerSettingOptions& entries,
+ int heading,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingInt> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ IntegerSettingOptionsFiller filler,
+ int heading,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingList> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<std::string> values,
+ StringSettingOptionsFiller filler,
+ int heading,
+ int minimumItems = 0,
+ int maximumItems = -1,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingList> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ const TranslatableIntegerSettingOptions& entries,
+ int heading,
+ int minimumItems = 0,
+ int maximumItems = -1,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingList> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ const IntegerSettingOptions& entries,
+ int heading,
+ int minimumItems = 0,
+ int maximumItems = -1,
+ bool visible = true,
+ int help = -1,
+ bool details = false);
+ std::shared_ptr<CSettingList> AddList(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ std::vector<int> values,
+ IntegerSettingOptionsFiller filler,
+ int heading,
+ int minimumItems = 0,
+ int maximumItems = -1,
+ bool visible = true,
+ int help = -1,
+ SettingControlListValueFormatter formatter = nullptr,
+ bool details = false);
+
+ // slider controls
+ std::shared_ptr<CSettingInt> AddPercentageSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int formatLabel,
+ int step = 1,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddPercentageSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const std::string& formatString,
+ int step = 1,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ int formatLabel,
+ int minimum,
+ int step,
+ int maximum,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingInt> AddSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int value,
+ const std::string& formatString,
+ int minimum,
+ int step,
+ int maximum,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingNumber> AddSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ int formatLabel,
+ float minimum,
+ float step,
+ float maximum,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingNumber> AddSlider(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float value,
+ const std::string& formatString,
+ float minimum,
+ float step,
+ float maximum,
+ int heading = -1,
+ bool usePopup = false,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+
+ // range controls
+ std::shared_ptr<CSettingList> AddPercentageRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int valueFormatLabel,
+ int step = 1,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddPercentageRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ const std::string& valueFormatString = "{:d} %",
+ int step = 1,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString = "{:d}",
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ int valueFormatLabel,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& valueFormatString = "{:.1f}",
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddDateRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddDateRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString = "",
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddTimeRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ int valueFormatLabel,
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+ std::shared_ptr<CSettingList> AddTimeRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& valueFormatString = "mm:ss",
+ int formatLabel = 21469,
+ bool delayed = false,
+ bool visible = true,
+ int help = -1);
+
+ std::shared_ptr<ISettingControl> GetTitleControl(bool separatorBelowLabel = true, bool hideSeparator = false);
+ std::shared_ptr<ISettingControl> GetCheckmarkControl(bool delayed = false);
+ std::shared_ptr<ISettingControl> GetEditControl(const std::string &format, bool delayed = false, bool hidden = false, bool verifyNewValue = false, int heading = -1);
+ std::shared_ptr<ISettingControl> GetButtonControl(const std::string &format, bool delayed = false, int heading = -1, bool hideValue = false, bool showInstalledAddons = true,
+ bool showInstallableAddons = false, bool showMoreAddons = true);
+ std::shared_ptr<ISettingControl> GetSpinnerControl(const std::string &format, bool delayed = false, int minimumLabel = -1, int formatLabel = -1, const std::string &formatString = "");
+ std::shared_ptr<ISettingControl> GetListControl(
+ const std::string& format,
+ bool delayed = false,
+ int heading = -1,
+ bool multiselect = false,
+ SettingControlListValueFormatter formatter = nullptr,
+ bool details = false);
+ std::shared_ptr<ISettingControl> GetSliderControl(const std::string &format, bool delayed = false, int heading = -1, bool usePopup = false, int formatLabel = -1, const std::string &formatString = "");
+ std::shared_ptr<ISettingControl> GetRangeControl(const std::string &format, bool delayed = false, int formatLabel = -1, int valueFormatLabel = -1, const std::string &valueFormatString = "");
+
+private:
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ int valueLower,
+ int valueUpper,
+ int minimum,
+ int step,
+ int maximum,
+ const std::string& format,
+ int formatLabel,
+ int valueFormatLabel,
+ const std::string& valueFormatString,
+ bool delayed,
+ bool visible,
+ int help);
+ std::shared_ptr<CSettingList> AddRange(const std::shared_ptr<CSettingGroup>& group,
+ const std::string& id,
+ int label,
+ SettingLevel level,
+ float valueLower,
+ float valueUpper,
+ float minimum,
+ float step,
+ float maximum,
+ const std::string& format,
+ int formatLabel,
+ int valueFormatLabel,
+ const std::string& valueFormatString,
+ bool delayed,
+ bool visible,
+ int help);
+
+ void setSettingDetails(const std::shared_ptr<CSetting>& setting,
+ SettingLevel level,
+ bool visible,
+ int help);
+
+ mutable CSettingsManager *m_settingsManager;
+ std::shared_ptr<CSettingSection> m_section;
+};
diff --git a/xbmc/settings/lib/CMakeLists.txt b/xbmc/settings/lib/CMakeLists.txt
new file mode 100644
index 0000000..a39a0ca
--- /dev/null
+++ b/xbmc/settings/lib/CMakeLists.txt
@@ -0,0 +1,31 @@
+set(SOURCES ISetting.cpp
+ ISettingControl.cpp
+ Setting.cpp
+ SettingCategoryAccess.cpp
+ SettingConditions.cpp
+ SettingDependency.cpp
+ SettingRequirement.cpp
+ SettingSection.cpp
+ SettingsManager.cpp
+ SettingUpdate.cpp)
+
+set(HEADERS ISetting.h
+ ISettingCallback.h
+ ISettingControl.h
+ ISettingControlCreator.h
+ ISettingCreator.h
+ ISettingsHandler.h
+ ISettingsValueSerializer.h
+ Setting.h
+ SettingCategoryAccess.h
+ SettingConditions.h
+ SettingDefinitions.h
+ SettingDependency.h
+ SettingLevel.h
+ SettingRequirement.h
+ SettingSection.h
+ SettingsManager.h
+ SettingType.h
+ SettingUpdate.h)
+
+core_add_library(settings_lib)
diff --git a/xbmc/settings/lib/ISetting.cpp b/xbmc/settings/lib/ISetting.cpp
new file mode 100644
index 0000000..a836f99
--- /dev/null
+++ b/xbmc/settings/lib/ISetting.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013-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 "ISetting.h"
+
+#include "SettingDefinitions.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+
+#include <string>
+
+ISetting::ISetting(const std::string &id, CSettingsManager *settingsManager /* = nullptr */)
+ : m_id(id)
+ , m_settingsManager(settingsManager)
+ , m_requirementCondition(settingsManager)
+{ }
+
+bool ISetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (node == nullptr)
+ return false;
+
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_VISIBLE, value))
+ m_visible = value;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ int iValue = -1;
+ if (element->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &iValue) == TIXML_SUCCESS && iValue > 0)
+ m_label = iValue;
+ if (element->QueryIntAttribute(SETTING_XML_ATTR_HELP, &iValue) == TIXML_SUCCESS && iValue > 0)
+ m_help = iValue;
+
+ auto requirementNode = node->FirstChild(SETTING_XML_ELM_REQUIREMENT);
+ if (requirementNode == nullptr)
+ return true;
+
+ return m_requirementCondition.Deserialize(requirementNode);
+}
+
+bool ISetting::DeserializeIdentification(const TiXmlNode* node, std::string& identification)
+{
+ return DeserializeIdentificationFromAttribute(node, SETTING_XML_ATTR_ID, identification);
+}
+
+bool ISetting::DeserializeIdentificationFromAttribute(const TiXmlNode* node,
+ const std::string& attribute,
+ std::string& identification)
+{
+ if (node == nullptr)
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ auto idAttribute = element->Attribute(attribute);
+ if (idAttribute == nullptr || idAttribute->empty())
+ return false;
+
+ identification = *idAttribute;
+ return true;
+}
+
+void ISetting::CheckRequirements()
+{
+ m_meetsRequirements = m_requirementCondition.Check();
+}
diff --git a/xbmc/settings/lib/ISetting.h b/xbmc/settings/lib/ISetting.h
new file mode 100644
index 0000000..8a40fe3
--- /dev/null
+++ b/xbmc/settings/lib/ISetting.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "SettingRequirement.h"
+
+#include <string>
+
+class CSettingsManager;
+class TiXmlNode;
+
+/*!
+ \ingroup settings
+ \brief Interface defining the base of all setting objects
+ */
+class ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting object with the given identifier.
+
+ \param id Identifier of the setting object
+ \param settingsManager Reference to the settings manager
+ */
+ ISetting(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ virtual ~ISetting() = default;
+
+ /*!
+ \brief Deserializes the given XML node into the properties of the setting
+ object.
+
+ If the update parameter is true, the checks for mandatory properties are
+ skipped and values are only updated.
+
+ \param node XML node containing the properties of the setting object
+ \param update Whether to perform checks for mandatory properties or not
+ \return True if deserialization was successful, false otherwise
+ */
+ virtual bool Deserialize(const TiXmlNode *node, bool update = false);
+
+ /*!
+ \brief Gets the identifier of the setting object.
+
+ \return Identifier of the setting object
+ */
+ const std::string& GetId() const { return m_id; }
+ /*!
+ \brief Whether the setting object is visible or hidden.
+
+ \return True if the setting object is visible, false otherwise
+ */
+ virtual bool IsVisible() const { return m_visible; }
+ /*!
+ \brief Sets the visibility state of the setting object.
+
+ \param visible Whether the setting object shall be visible or not
+ */
+ virtual void SetVisible(bool visible) { m_visible = visible; }
+ /*!
+ \brief Gets the localizeable label ID of the setting group.
+
+ \return Localizeable label ID of the setting group
+ */
+ int GetLabel() const { return m_label; }
+ /*!
+ \brief Sets the localizeable label ID of the setting group.
+
+ \param label Localizeable label ID of the setting group
+ */
+ void SetLabel(int label) { m_label = label; }
+ /*!
+ \brief Gets the localizeable help ID of the setting group.
+
+ \return Localizeable help ID of the setting group
+ */
+ int GetHelp() const { return m_help; }
+ /*!
+ \brief Sets the localizeable help ID of the setting group.
+
+ \param label Localizeable help ID of the setting group
+ */
+ void SetHelp(int help) { m_help = help; }
+ /*!
+ \brief Whether the setting object meets all necessary requirements.
+
+ \return True if the setting object meets all necessary requirements, false otherwise
+ */
+ virtual bool MeetsRequirements() const { return m_meetsRequirements; }
+ /*!
+ \brief Checks if the setting object meets all necessary requirements.
+ */
+ virtual void CheckRequirements();
+ /*!
+ \brief Sets whether the setting object meets all necessary requirements.
+
+ \param visible Whether the setting object meets all necessary requirements or not
+ */
+ virtual void SetRequirementsMet(bool requirementsMet) { m_meetsRequirements = requirementsMet; }
+
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's
+ identifier.
+
+ \param node XML node containing a setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentification(const TiXmlNode *node, std::string &identification);
+
+protected:
+ static constexpr int DefaultLabel = -1;
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's identifier from the given attribute.
+
+ \param node XML node containing a setting object's identifier
+ \param attribute Attribute which contains the setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentificationFromAttribute(const TiXmlNode* node,
+ const std::string& attribute,
+ std::string& identification);
+
+ std::string m_id;
+ CSettingsManager *m_settingsManager;
+
+private:
+ bool m_visible = true;
+ int m_label = DefaultLabel;
+ int m_help = -1;
+ bool m_meetsRequirements = true;
+ CSettingRequirement m_requirementCondition;
+};
diff --git a/xbmc/settings/lib/ISettingCallback.h b/xbmc/settings/lib/ISettingCallback.h
new file mode 100644
index 0000000..00fc428
--- /dev/null
+++ b/xbmc/settings/lib/ISettingCallback.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include <memory>
+
+class CSetting;
+class TiXmlNode;
+
+class ISettingCallback
+{
+public:
+ virtual ~ISettingCallback() = default;
+
+ /*!
+ \brief The value of the given setting is being changed.
+
+ This callback is triggered whenever the value of a setting is being
+ changed. The given CSetting already contains the new value and the handler
+ of the callback has the possibility to allow or revert changing the value
+ of the setting. In case of a revert OnSettingChanging() is called again to
+ inform all listeners that the value change has been reverted.
+
+ \param setting The setting whose value is being changed (already containing the changed value)
+ \return True if the new value is acceptable otherwise false
+ */
+ virtual bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) { return true; }
+
+ /*!
+ \brief The value of the given setting has changed.
+
+ This callback is triggered whenever the value of a setting has been
+ successfully changed (i.e. none of the OnSettingChanging() handlers)
+ has reverted the change.
+
+ \param setting The setting whose value has been changed
+ */
+ virtual void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) {}
+
+ /*!
+ \brief The given setting has been activated.
+
+ This callback is triggered whenever the given setting has been activated.
+ This callback is only fired for CSettingAction settings.
+
+ \param setting The setting which has been activated.
+ */
+ virtual void OnSettingAction(const std::shared_ptr<const CSetting>& setting) {}
+
+ /*!
+ \brief The given setting needs to be updated.
+
+ This callback is triggered when a setting needs to be updated because its
+ value is outdated. This only happens when initially loading the value of a
+ setting and will not be triggered afterwards.
+
+ \param setting The setting which needs to be updated.
+ \param oldSettingId The id of the previous setting.
+ \param oldSettingNode The old setting node
+ \return True if the setting has been successfully updated otherwise false
+ */
+ virtual bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+ {
+ return false;
+ }
+
+ /*!
+ \brief The given property of the given setting has changed
+
+ This callback is triggered when a property (e.g. enabled or the list of
+ dynamic options) has changed.
+
+ \param setting The setting which has a changed property
+ \param propertyName The string representation of the changed property
+ */
+ virtual void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+ {
+ }
+};
diff --git a/xbmc/settings/lib/ISettingControl.cpp b/xbmc/settings/lib/ISettingControl.cpp
new file mode 100644
index 0000000..3fc51a5
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControl.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013-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 "ISettingControl.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+Logger ISettingControl::s_logger;
+
+ISettingControl::ISettingControl()
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("ISettingControl");
+}
+
+bool ISettingControl::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strTmp = elem->Attribute(SETTING_XML_ATTR_FORMAT);
+ std::string format;
+ if (strTmp != nullptr)
+ format = strTmp;
+ if (!SetFormat(format))
+ {
+ s_logger->error("error reading \"{}\" attribute of <control>", SETTING_XML_ATTR_FORMAT);
+ return false;
+ }
+
+ if ((strTmp = elem->Attribute(SETTING_XML_ATTR_DELAYED)) != nullptr)
+ {
+ if (!StringUtils::EqualsNoCase(strTmp, "false") && !StringUtils::EqualsNoCase(strTmp, "true"))
+ {
+ s_logger->error("error reading \"{}\" attribute of <control>", SETTING_XML_ATTR_DELAYED);
+ return false;
+ }
+ else
+ m_delayed = StringUtils::EqualsNoCase(strTmp, "true");
+ }
+
+ return true;
+}
diff --git a/xbmc/settings/lib/ISettingControl.h b/xbmc/settings/lib/ISettingControl.h
new file mode 100644
index 0000000..293d5ad
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControl.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "utils/logtypes.h"
+
+#include <string>
+
+class TiXmlNode;
+
+class ISettingControl
+{
+public:
+ ISettingControl();
+ virtual ~ISettingControl() = default;
+
+ virtual std::string GetType() const = 0;
+ const std::string& GetFormat() const { return m_format; }
+ bool GetDelayed() const { return m_delayed; }
+ void SetDelayed(bool delayed) { m_delayed = delayed; }
+
+ virtual bool Deserialize(const TiXmlNode *node, bool update = false);
+ virtual bool SetFormat(const std::string &format) { return true; }
+
+protected:
+ bool m_delayed = false;
+ std::string m_format;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/ISettingControlCreator.h b/xbmc/settings/lib/ISettingControlCreator.h
new file mode 100644
index 0000000..2f77e1c
--- /dev/null
+++ b/xbmc/settings/lib/ISettingControlCreator.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class ISettingControl;
+
+/*!
+ \ingroup settings
+ \brief Interface for creating a new setting control of a custom setting control type.
+ */
+class ISettingControlCreator
+{
+public:
+ virtual ~ISettingControlCreator() = default;
+
+ /*!
+ \brief Creates a new setting control of the given custom setting control type.
+
+ \param controlType string representation of the setting control type
+ \return A new setting control object of the given (custom) setting control type or nullptr if the setting control type is unknown
+ */
+ virtual std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const = 0;
+};
diff --git a/xbmc/settings/lib/ISettingCreator.h b/xbmc/settings/lib/ISettingCreator.h
new file mode 100644
index 0000000..e951c85
--- /dev/null
+++ b/xbmc/settings/lib/ISettingCreator.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+class CSetting;
+class CSettingsManager;
+
+/*!
+ \ingroup settings
+ \brief Interface for creating a new setting of a custom setting type.
+ */
+class ISettingCreator
+{
+public:
+ virtual ~ISettingCreator() = default;
+
+ /*!
+ \brief Creates a new setting of the given custom setting type.
+
+ \param settingType string representation of the setting type
+ \param settingId Identifier of the setting to be created
+ \param settingsManager Reference to the settings manager
+ \return A new setting object of the given (custom) setting type or nullptr if the setting type is unknown
+ */
+ virtual std::shared_ptr<CSetting> CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager = nullptr) const = 0;
+};
diff --git a/xbmc/settings/lib/ISettingsHandler.h b/xbmc/settings/lib/ISettingsHandler.h
new file mode 100644
index 0000000..466b55a
--- /dev/null
+++ b/xbmc/settings/lib/ISettingsHandler.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Interface defining methods being called by the settings system if an
+ action is performed on multiple/all settings
+ */
+class ISettingsHandler
+{
+public:
+ virtual ~ISettingsHandler() = default;
+
+ /*!
+ \brief Settings loading has been initiated.
+
+ \return True if the settings should be loaded, false if the loading should be aborted.
+ */
+ virtual bool OnSettingsLoading() { return true; }
+ /*!
+ \brief Settings have been loaded.
+
+ This callback can be used to trigger loading other settings.
+ */
+ virtual void OnSettingsLoaded() { }
+ /*!
+ \brief Settings saving has been initiated.
+
+ \return True if the settings should be saved, false if the saving should be aborted.
+ */
+ virtual bool OnSettingsSaving() const { return true; }
+ /*!
+ \brief Settings have been saved.
+
+ This callback can be used to trigger saving other settings.
+ */
+ virtual void OnSettingsSaved() const { }
+ /*!
+ \brief Setting values have been unloaded.
+
+ This callback can be used to trigger uninitializing any state variables
+ (e.g. before re-loading the settings).
+ */
+ virtual void OnSettingsUnloaded() { }
+ /*!
+ \brief Settings have been cleared.
+
+ This callback can be used to trigger clearing any state variables.
+ */
+ virtual void OnSettingsCleared() { }
+};
diff --git a/xbmc/settings/lib/ISettingsValueSerializer.h b/xbmc/settings/lib/ISettingsValueSerializer.h
new file mode 100644
index 0000000..94bf664
--- /dev/null
+++ b/xbmc/settings/lib/ISettingsValueSerializer.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <string>
+
+class CSettingsManager;
+
+class ISettingsValueSerializer
+{
+public:
+ virtual ~ISettingsValueSerializer() = default;
+
+ virtual std::string SerializeValues(const CSettingsManager* settingsManager) const = 0;
+};
diff --git a/xbmc/settings/lib/Setting.cpp b/xbmc/settings/lib/Setting.cpp
new file mode 100644
index 0000000..0802dd8
--- /dev/null
+++ b/xbmc/settings/lib/Setting.cpp
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) 2013-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 "Setting.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+
+#include <mutex>
+#include <shared_mutex>
+#include <sstream>
+#include <utility>
+
+template<typename TKey, typename TValue>
+bool CheckSettingOptionsValidity(const TValue& value, const std::vector<std::pair<TKey, TValue>>& options)
+{
+ for (const auto& it : options)
+ {
+ if (it.second == value)
+ return true;
+ }
+
+ return false;
+}
+
+template<typename TKey, typename TValue>
+bool CheckSettingOptionsValidity(const TValue& value, const std::vector<TKey>& options)
+{
+ for (const auto& it : options)
+ {
+ if (it.value == value)
+ return true;
+ }
+
+ return false;
+}
+
+bool DeserializeOptionsSort(const TiXmlElement* optionsElement, SettingOptionsSort& optionsSort)
+{
+ optionsSort = SettingOptionsSort::NoSorting;
+
+ std::string sort;
+ if (optionsElement->QueryStringAttribute("sort", &sort) != TIXML_SUCCESS)
+ return true;
+
+ if (StringUtils::EqualsNoCase(sort, "false") || StringUtils::EqualsNoCase(sort, "off") ||
+ StringUtils::EqualsNoCase(sort, "no") || StringUtils::EqualsNoCase(sort, "disabled"))
+ optionsSort = SettingOptionsSort::NoSorting;
+ else if (StringUtils::EqualsNoCase(sort, "asc") || StringUtils::EqualsNoCase(sort, "ascending") ||
+ StringUtils::EqualsNoCase(sort, "true") || StringUtils::EqualsNoCase(sort, "on") ||
+ StringUtils::EqualsNoCase(sort, "yes") || StringUtils::EqualsNoCase(sort, "enabled"))
+ optionsSort = SettingOptionsSort::Ascending;
+ else if (StringUtils::EqualsNoCase(sort, "desc") || StringUtils::EqualsNoCase(sort, "descending"))
+ optionsSort = SettingOptionsSort::Descending;
+ else
+ return false;
+
+ return true;
+}
+
+Logger CSetting::s_logger;
+
+CSetting::CSetting(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSetting");
+}
+
+CSetting::CSetting(const std::string& id, const CSetting& setting)
+ : CSetting(id, setting.m_settingsManager)
+{
+ Copy(setting);
+}
+
+void CSetting::MergeBasics(const CSetting& other)
+{
+ // ISetting
+ SetVisible(other.GetVisible());
+ SetLabel(other.GetLabel());
+ SetHelp(other.GetHelp());
+ SetRequirementsMet(other.MeetsRequirements());
+ // CSetting
+ SetEnabled(other.GetEnabled());
+ SetParent(other.GetParent());
+ SetLevel(other.GetLevel());
+ SetControl(const_cast<CSetting&>(other).GetControl());
+ SetDependencies(other.GetDependencies());
+}
+
+bool CSetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ return false;
+
+ auto parentSetting = element->Attribute(SETTING_XML_ATTR_PARENT);
+ if (parentSetting != nullptr)
+ m_parentSetting = parentSetting;
+
+ // get <enable>
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_ENABLED, value))
+ m_enabled = value;
+
+ // get the <level>
+ int level = -1;
+ if (XMLUtils::GetInt(node, SETTING_XML_ELM_LEVEL, level))
+ m_level = static_cast<SettingLevel>(level);
+
+ if (m_level < SettingLevel::Basic || m_level > SettingLevel::Internal)
+ m_level = SettingLevel::Standard;
+
+ auto dependencies = node->FirstChild(SETTING_XML_ELM_DEPENDENCIES);
+ if (dependencies != nullptr)
+ {
+ auto dependencyNode = dependencies->FirstChild(SETTING_XML_ELM_DEPENDENCY);
+ while (dependencyNode != nullptr)
+ {
+ CSettingDependency dependency(m_settingsManager);
+ if (dependency.Deserialize(dependencyNode))
+ m_dependencies.push_back(dependency);
+ else
+ s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_DEPENDENCY, m_id);
+
+ dependencyNode = dependencyNode->NextSibling(SETTING_XML_ELM_DEPENDENCY);
+ }
+ }
+
+ auto control = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
+ if (control != nullptr)
+ {
+ auto controlType = control->Attribute(SETTING_XML_ATTR_TYPE);
+ if (controlType == nullptr)
+ {
+ s_logger->error("error reading \"{}\" attribute of <control> tag of \"{}\"",
+ SETTING_XML_ATTR_TYPE, m_id);
+ return false;
+ }
+
+ m_control = m_settingsManager->CreateControl(controlType);
+ if (m_control == nullptr || !m_control->Deserialize(control, update))
+ {
+ s_logger->error("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
+ return false;
+ }
+ }
+ else if (!update && m_level < SettingLevel::Internal && !IsReference())
+ {
+ s_logger->error("missing <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
+ return false;
+ }
+
+ auto updates = node->FirstChild(SETTING_XML_ELM_UPDATES);
+ if (updates != nullptr)
+ {
+ auto updateElem = updates->FirstChildElement(SETTING_XML_ELM_UPDATE);
+ while (updateElem != nullptr)
+ {
+ CSettingUpdate settingUpdate;
+ if (settingUpdate.Deserialize(updateElem))
+ {
+ if (!m_updates.insert(settingUpdate).second)
+ s_logger->warn("duplicate <{}> definition for \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
+ }
+ else
+ s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
+
+ updateElem = updateElem->NextSiblingElement(SETTING_XML_ELM_UPDATE);
+ }
+ }
+
+ return true;
+}
+
+bool CSetting::IsEnabled() const
+{
+ if (m_dependencies.empty() && m_parentSetting.empty())
+ return m_enabled;
+
+ // if the setting has a parent setting and that parent setting is disabled
+ // the setting should automatically also be disabled
+ if (!m_parentSetting.empty())
+ {
+ SettingPtr parentSetting = m_settingsManager->GetSetting(m_parentSetting);
+ if (parentSetting != nullptr && !parentSetting->IsEnabled())
+ return false;
+ }
+
+ bool enabled = m_enabled;
+ for (const auto& dep : m_dependencies)
+ {
+ if (dep.GetType() != SettingDependencyType::Enable)
+ continue;
+
+ if (!dep.Check())
+ {
+ enabled = false;
+ break;
+ }
+ }
+
+ return enabled;
+}
+
+void CSetting::SetEnabled(bool enabled)
+{
+ if (!m_dependencies.empty() || m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ OnSettingPropertyChanged(shared_from_this(), "enabled");
+}
+
+void CSetting::MakeReference(const std::string& referencedId /* = "" */)
+{
+ auto tmpReferencedId = referencedId;
+ if (referencedId.empty())
+ tmpReferencedId = m_id;
+
+ m_id = StringUtils::Format("#{}[{}]", tmpReferencedId, StringUtils::CreateUUID());
+ m_referencedId = tmpReferencedId;
+}
+
+bool CSetting::IsVisible() const
+{
+ if (!ISetting::IsVisible())
+ return false;
+
+ bool visible = true;
+ for (const auto& dep : m_dependencies)
+ {
+ if (dep.GetType() != SettingDependencyType::Visible)
+ continue;
+
+ if (!dep.Check())
+ {
+ visible = false;
+ break;
+ }
+ }
+
+ return visible;
+}
+
+bool CSetting::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return true;
+
+ return m_callback->OnSettingChanging(setting);
+}
+
+void CSetting::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingChanged(setting);
+}
+
+void CSetting::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingAction(setting);
+}
+
+bool CSetting::DeserializeIdentification(const TiXmlNode* node,
+ std::string& identification,
+ bool& isReference)
+{
+ isReference = false;
+
+ // first check if we can simply retrieve the setting's identifier
+ if (ISetting::DeserializeIdentification(node, identification))
+ return true;
+
+ // otherwise try to retrieve a reference to another setting's identifier
+ if (!DeserializeIdentificationFromAttribute(node, SETTING_XML_ATTR_REFERENCE, identification))
+ return false;
+
+ isReference = true;
+ return true;
+}
+
+bool CSetting::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ if (m_callback == nullptr)
+ return false;
+
+ return m_callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
+}
+
+void CSetting::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+{
+ if (m_callback == nullptr)
+ return;
+
+ m_callback->OnSettingPropertyChanged(setting, propertyName);
+}
+
+void CSetting::Copy(const CSetting &setting)
+{
+ SetVisible(setting.IsVisible());
+ SetLabel(setting.GetLabel());
+ SetHelp(setting.GetHelp());
+ SetRequirementsMet(setting.MeetsRequirements());
+ m_callback = setting.m_callback;
+ m_level = setting.m_level;
+
+ if (setting.m_control != nullptr)
+ {
+ m_control = m_settingsManager->CreateControl(setting.m_control->GetType());
+ *m_control = *setting.m_control;
+ }
+ else
+ m_control = nullptr;
+
+ m_dependencies = setting.m_dependencies;
+ m_updates = setting.m_updates;
+ m_changed = setting.m_changed;
+}
+
+Logger CSettingList::s_logger;
+
+CSettingList::CSettingList(const std::string& id,
+ std::shared_ptr<CSetting> settingDefinition,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSetting(id, settingsManager), m_definition(std::move(settingDefinition))
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingList");
+}
+
+CSettingList::CSettingList(const std::string& id,
+ std::shared_ptr<CSetting> settingDefinition,
+ int label,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingList(id, std::move(settingDefinition), settingsManager)
+{
+ SetLabel(label);
+}
+
+CSettingList::CSettingList(const std::string &id, const CSettingList &setting)
+ : CSetting(id, setting)
+{
+ copy(setting);
+}
+
+SettingPtr CSettingList::Clone(const std::string &id) const
+{
+ if (m_definition == nullptr)
+ return nullptr;
+
+ return std::make_shared<CSettingList>(id, *this);
+}
+
+void CSettingList::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::List)
+ return;
+
+ const auto& listSetting = static_cast<const CSettingList&>(other);
+ if (m_definition == nullptr && listSetting.m_definition != nullptr)
+ m_definition = listSetting.m_definition;
+ if (m_defaults.empty() && !listSetting.m_defaults.empty())
+ m_defaults = listSetting.m_defaults;
+ if (m_values.empty() && !listSetting.m_values.empty())
+ m_values = listSetting.m_values;
+ if (m_delimiter == "|" && listSetting.m_delimiter != "|")
+ m_delimiter = listSetting.m_delimiter;
+ if (m_minimumItems == 0 && listSetting.m_minimumItems != 0)
+ m_minimumItems = listSetting.m_minimumItems;
+ if (m_maximumItems == -1 && listSetting.m_maximumItems != -1)
+ m_maximumItems = listSetting.m_maximumItems;
+}
+
+bool CSettingList::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (m_definition == nullptr)
+ return false;
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ auto element = node->ToElement();
+ if (element == nullptr)
+ {
+ s_logger->warn("unable to read type of list setting of {}", m_id);
+ return false;
+ }
+
+ // deserialize the setting definition in update mode because we don't care
+ // about an invalid <default> value (which is never used)
+ if (!m_definition->Deserialize(node, true))
+ return false;
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // read the delimiter
+ std::string delimiter;
+ if (XMLUtils::GetString(constraints, SETTING_XML_ELM_DELIMITER, delimiter) && !delimiter.empty())
+ m_delimiter = delimiter;
+
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM_ITEMS, m_minimumItems);
+ if (m_minimumItems < 0)
+ m_minimumItems = 0;
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems);
+ if (m_maximumItems <= 0)
+ m_maximumItems = -1;
+ else if (m_maximumItems < m_minimumItems)
+ {
+ s_logger->warn("invalid <{}> ({}) and/or <{}> ({}) of {}", SETTING_XML_ELM_MINIMUM_ITEMS,
+ m_minimumItems, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems, m_id);
+ return false;
+ }
+ }
+
+ // read the default and initial values
+ std::string values;
+ if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, values))
+ {
+ if (!fromString(values, m_defaults))
+ {
+ s_logger->warn("invalid <{}> definition \"{}\" of {}", SETTING_XML_ELM_DEFAULT, values, m_id);
+ return false;
+ }
+ Reset();
+ }
+
+ return true;
+}
+
+SettingType CSettingList::GetElementType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (m_definition == nullptr)
+ return SettingType::Unknown;
+
+ return m_definition->GetType();
+}
+
+bool CSettingList::FromString(const std::string &value)
+{
+ SettingList values;
+ if (!fromString(value, values))
+ return false;
+
+ return SetValue(values);
+}
+
+std::string CSettingList::ToString() const
+{
+ return toString(m_values);
+}
+
+bool CSettingList::Equals(const std::string &value) const
+{
+ SettingList values;
+ if (!fromString(value, values) || values.size() != m_values.size())
+ return false;
+
+ bool ret = true;
+ for (size_t index = 0; index < values.size(); index++)
+ {
+ if (!m_values[index]->Equals(values[index]->ToString()))
+ {
+ ret = false;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+bool CSettingList::CheckValidity(const std::string &value) const
+{
+ SettingList values;
+ return fromString(value, values);
+}
+
+void CSettingList::Reset()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ SettingList values;
+ for (const auto& it : m_defaults)
+ values.push_back(it->Clone(it->GetId()));
+
+ SetValue(values);
+}
+
+bool CSettingList::FromString(const std::vector<std::string> &value)
+{
+ SettingList values;
+ if (!fromValues(value, values))
+ return false;
+
+ return SetValue(values);
+}
+
+bool CSettingList::SetValue(const SettingList &values)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if ((int)values.size() < m_minimumItems ||
+ (m_maximumItems > 0 && (int)values.size() > m_maximumItems))
+ return false;
+
+ bool equal = values.size() == m_values.size();
+ for (size_t index = 0; index < values.size(); index++)
+ {
+ if (values[index]->GetType() != GetElementType())
+ return false;
+
+ if (equal &&
+ !values[index]->Equals(m_values[index]->ToString()))
+ equal = false;
+ }
+
+ if (equal)
+ return true;
+
+ SettingList oldValues = m_values;
+ m_values.clear();
+ m_values.insert(m_values.begin(), values.begin(), values.end());
+
+ if (!OnSettingChanging(shared_from_base<CSettingList>()))
+ {
+ m_values = oldValues;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingList>());
+ return false;
+ }
+
+ m_changed = toString(m_values) != toString(m_defaults);
+ OnSettingChanged(shared_from_base<CSettingList>());
+ return true;
+}
+
+void CSettingList::SetDefault(const SettingList &values)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_defaults.clear();
+ m_defaults.insert(m_defaults.begin(), values.begin(), values.end());
+
+ if (!m_changed)
+ {
+ m_values.clear();
+ for (const auto& it : m_defaults)
+ m_values.push_back(it->Clone(it->GetId()));
+ }
+}
+
+void CSettingList::copy(const CSettingList &setting)
+{
+ CSetting::Copy(setting);
+
+ copy(setting.m_values, m_values);
+ copy(setting.m_defaults, m_defaults);
+
+ if (setting.m_definition != nullptr)
+ {
+ auto definitionCopy = setting.m_definition->Clone(m_id + ".definition");
+ if (definitionCopy != nullptr)
+ m_definition = definitionCopy;
+ }
+
+ m_delimiter = setting.m_delimiter;
+ m_minimumItems = setting.m_minimumItems;
+ m_maximumItems = setting.m_maximumItems;
+}
+
+void CSettingList::copy(const SettingList &srcValues, SettingList &dstValues)
+{
+ dstValues.clear();
+
+ for (const auto& value : srcValues)
+ {
+ if (value == nullptr)
+ continue;
+
+ SettingPtr valueCopy = value->Clone(value->GetId());
+ if (valueCopy == nullptr)
+ continue;
+
+ dstValues.emplace_back(valueCopy);
+ }
+}
+
+bool CSettingList::fromString(const std::string &strValue, SettingList &values) const
+{
+ return fromValues(StringUtils::Split(strValue, m_delimiter), values);
+}
+
+bool CSettingList::fromValues(const std::vector<std::string> &strValues, SettingList &values) const
+{
+ if ((int)strValues.size() < m_minimumItems ||
+ (m_maximumItems > 0 && (int)strValues.size() > m_maximumItems))
+ return false;
+
+ bool ret = true;
+ int index = 0;
+ for (const auto& value : strValues)
+ {
+ auto settingValue = m_definition->Clone(StringUtils::Format("{}.{}", m_id, index++));
+ if (settingValue == nullptr ||
+ !settingValue->FromString(value))
+ {
+ ret = false;
+ break;
+ }
+
+ values.emplace_back(settingValue);
+ }
+
+ if (!ret)
+ values.clear();
+
+ return ret;
+}
+
+std::string CSettingList::toString(const SettingList &values) const
+{
+ std::vector<std::string> strValues;
+ for (const auto& value : values)
+ {
+ if (value != nullptr)
+ strValues.push_back(value->ToString());
+ }
+
+ return StringUtils::Join(strValues, m_delimiter);
+}
+
+Logger CSettingBool::s_logger;
+
+CSettingBool::CSettingBool(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingBool(id, DefaultLabel, DefaultValue, settingsManager)
+{
+}
+
+CSettingBool::CSettingBool(const std::string& id, const CSettingBool& setting)
+ : CSettingBool(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingBool::CSettingBool(const std::string& id,
+ int label,
+ bool value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager), m_value(value), m_default(value)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingBool");
+}
+
+SettingPtr CSettingBool::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingBool>(id, *this);
+}
+
+void CSettingBool::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Boolean)
+ return;
+
+ const auto& boolSetting = static_cast<const CSettingBool&>(other);
+ if (m_default == false && boolSetting.m_default == true)
+ m_default = boolSetting.m_default;
+ if (m_value == m_default && boolSetting.m_value != m_default)
+ m_value = boolSetting.m_value;
+}
+
+bool CSettingBool::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ bool value;
+ if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingBool::FromString(const std::string &value)
+{
+ bool bValue;
+ if (!fromString(value, bValue))
+ return false;
+
+ return SetValue(bValue);
+}
+
+std::string CSettingBool::ToString() const
+{
+ return m_value ? "true" : "false";
+}
+
+bool CSettingBool::Equals(const std::string &value) const
+{
+ bool bValue;
+ return (fromString(value, bValue) && m_value == bValue);
+}
+
+bool CSettingBool::CheckValidity(const std::string &value) const
+{
+ bool bValue;
+ return fromString(value, bValue);
+}
+
+bool CSettingBool::SetValue(bool value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ bool oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingBool>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingBool>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingBool>());
+ return true;
+}
+
+void CSettingBool::SetDefault(bool value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+void CSettingBool::copy(const CSettingBool &setting)
+{
+ CSetting::Copy(setting);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+}
+
+bool CSettingBool::fromString(const std::string &strValue, bool &value) const
+{
+ if (StringUtils::EqualsNoCase(strValue, "true"))
+ {
+ value = true;
+ return true;
+ }
+ if (StringUtils::EqualsNoCase(strValue, "false"))
+ {
+ value = false;
+ return true;
+ }
+
+ return false;
+}
+
+Logger CSettingInt::s_logger;
+
+CSettingInt::CSettingInt(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingInt::CSettingInt(const std::string& id, const CSettingInt& setting)
+ : CSettingInt(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, label, value, DefaultMin, DefaultStep, DefaultMax, settingsManager)
+{
+ SetLabel(label);
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ int minimum,
+ int step,
+ int maximum,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager),
+ m_value(value),
+ m_default(value),
+ m_min(minimum),
+ m_step(step),
+ m_max(maximum)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingInt");
+}
+
+CSettingInt::CSettingInt(const std::string& id,
+ int label,
+ int value,
+ const TranslatableIntegerSettingOptions& options,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingInt(id, label, value, settingsManager)
+{
+ SetTranslatableOptions(options);
+}
+
+SettingPtr CSettingInt::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingInt>(id, *this);
+}
+
+void CSettingInt::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Integer)
+ return;
+
+ const auto& intSetting = static_cast<const CSettingInt&>(other);
+ if (m_default == 0.0 && intSetting.m_default != 0.0)
+ m_default = intSetting.m_default;
+ if (m_value == m_default && intSetting.m_value != m_default)
+ m_value = intSetting.m_value;
+ if (m_min == 0.0 && intSetting.m_min != 0.0)
+ m_min = intSetting.m_min;
+ if (m_step == 1.0 && intSetting.m_step != 1.0)
+ m_step = intSetting.m_step;
+ if (m_max == 0.0 && intSetting.m_max != 0.0)
+ m_max = intSetting.m_max;
+ if (m_translatableOptions.empty() && !intSetting.m_translatableOptions.empty())
+ m_translatableOptions = intSetting.m_translatableOptions;
+ if (m_options.empty() && !intSetting.m_options.empty())
+ m_options = intSetting.m_options;
+ if (m_optionsFillerName.empty() && !intSetting.m_optionsFillerName.empty())
+ m_optionsFillerName = intSetting.m_optionsFillerName;
+ if (m_optionsFiller == nullptr && intSetting.m_optionsFiller != nullptr)
+ m_optionsFiller = intSetting.m_optionsFiller;
+ if (m_optionsFillerData == nullptr && intSetting.m_optionsFillerData != nullptr)
+ m_optionsFillerData = intSetting.m_optionsFillerData;
+ if (m_dynamicOptions.empty() && !intSetting.m_dynamicOptions.empty())
+ m_dynamicOptions = intSetting.m_dynamicOptions;
+ if (m_optionsSort == SettingOptionsSort::NoSorting &&
+ intSetting.m_optionsSort != SettingOptionsSort::NoSorting)
+ m_optionsSort = intSetting.m_optionsSort;
+}
+
+bool CSettingInt::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ int value;
+ if (XMLUtils::GetInt(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get the entries
+ auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
+ if (options != nullptr && options->FirstChild() != nullptr)
+ {
+ if (!DeserializeOptionsSort(options, m_optionsSort))
+ s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
+ m_id);
+
+ if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ {
+ m_optionsFillerName = options->FirstChild()->ValueStr();
+ if (!m_optionsFillerName.empty())
+ {
+ m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
+ }
+ }
+ else
+ {
+ m_translatableOptions.clear();
+ auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
+ while (optionElement != nullptr)
+ {
+ TranslatableIntegerSettingOption entry;
+ if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.label) ==
+ TIXML_SUCCESS &&
+ entry.label > 0)
+ {
+ entry.value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
+ m_translatableOptions.push_back(entry);
+ }
+ else
+ {
+ std::string label;
+ if (optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label) ==
+ TIXML_SUCCESS)
+ {
+ int value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
+ m_options.emplace_back(label, value);
+ }
+ }
+
+ optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
+ }
+ }
+ }
+
+ // get minimum
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM, m_min);
+ // get step
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_STEP, m_step);
+ // get maximum
+ XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
+ }
+
+ return true;
+}
+
+bool CSettingInt::FromString(const std::string &value)
+{
+ int iValue;
+ if (!fromString(value, iValue))
+ return false;
+
+ return SetValue(iValue);
+}
+
+std::string CSettingInt::ToString() const
+{
+ std::ostringstream oss;
+ oss << m_value;
+
+ return oss.str();
+}
+
+bool CSettingInt::Equals(const std::string &value) const
+{
+ int iValue;
+ return (fromString(value, iValue) && m_value == iValue);
+}
+
+bool CSettingInt::CheckValidity(const std::string &value) const
+{
+ int iValue;
+ if (!fromString(value, iValue))
+ return false;
+
+ return CheckValidity(iValue);
+}
+
+bool CSettingInt::CheckValidity(int value) const
+{
+ if (!m_translatableOptions.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_translatableOptions))
+ return false;
+ }
+ else if (!m_options.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_options))
+ return false;
+ }
+ else if (m_optionsFillerName.empty() && m_optionsFiller == nullptr &&
+ m_min != m_max && (value < m_min || value > m_max))
+ return false;
+
+ return true;
+}
+
+bool CSettingInt::SetValue(int value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ int oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingInt>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingInt>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingInt>());
+ return true;
+}
+
+void CSettingInt::SetDefault(int value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+SettingOptionsType CSettingInt::GetOptionsType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_translatableOptions.empty())
+ return SettingOptionsType::StaticTranslatable;
+ if (!m_options.empty())
+ return SettingOptionsType::Static;
+ if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
+ return SettingOptionsType::Dynamic;
+
+ return SettingOptionsType::Unknown;
+}
+
+IntegerSettingOptions CSettingInt::UpdateDynamicOptions()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ IntegerSettingOptions options;
+ if (m_optionsFiller == nullptr &&
+ (m_optionsFillerName.empty() || m_settingsManager == nullptr))
+ return options;
+
+ if (m_optionsFiller == nullptr)
+ {
+ m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
+ if (m_optionsFiller == nullptr)
+ {
+ s_logger->warn("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
+ return options;
+ }
+ }
+
+ int bestMatchingValue = m_value;
+ m_optionsFiller(shared_from_base<CSettingInt>(), options, bestMatchingValue, m_optionsFillerData);
+
+ if (bestMatchingValue != m_value)
+ SetValue(bestMatchingValue);
+
+ bool changed = m_dynamicOptions.size() != options.size();
+ if (!changed)
+ {
+ for (size_t index = 0; index < options.size(); index++)
+ {
+ if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
+ options[index].value != m_dynamicOptions[index].value)
+ {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed)
+ {
+ m_dynamicOptions = options;
+ OnSettingPropertyChanged(shared_from_base<CSettingInt>(), "options");
+ }
+
+ return options;
+}
+
+void CSettingInt::copy(const CSettingInt &setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_min = setting.m_min;
+ m_step = setting.m_step;
+ m_max = setting.m_max;
+ m_translatableOptions = setting.m_translatableOptions;
+ m_options = setting.m_options;
+ m_optionsFillerName = setting.m_optionsFillerName;
+ m_optionsFiller = setting.m_optionsFiller;
+ m_optionsFillerData = setting.m_optionsFillerData;
+ m_dynamicOptions = setting.m_dynamicOptions;
+}
+
+bool CSettingInt::fromString(const std::string &strValue, int &value)
+{
+ if (strValue.empty())
+ return false;
+
+ char *end = nullptr;
+ value = (int)strtol(strValue.c_str(), &end, 10);
+ if (end != nullptr && *end != '\0')
+ return false;
+
+ return true;
+}
+
+Logger CSettingNumber::s_logger;
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingNumber(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingNumber::CSettingNumber(const std::string& id, const CSettingNumber& setting)
+ : CSettingNumber(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ int label,
+ float value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingNumber(id, label, value, DefaultMin, DefaultStep, DefaultMax, settingsManager)
+{
+}
+
+CSettingNumber::CSettingNumber(const std::string& id,
+ int label,
+ float value,
+ float minimum,
+ float step,
+ float maximum,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager),
+ m_value(static_cast<double>(value)),
+ m_default(static_cast<double>(value)),
+ m_min(static_cast<double>(minimum)),
+ m_step(static_cast<double>(step)),
+ m_max(static_cast<double>(maximum))
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingNumber");
+}
+
+SettingPtr CSettingNumber::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingNumber>(id, *this);
+}
+
+void CSettingNumber::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Number)
+ return;
+
+ const auto& numberSetting = static_cast<const CSettingNumber&>(other);
+ if (m_default == 0.0 && numberSetting.m_default != 0.0)
+ m_default = numberSetting.m_default;
+ if (m_value == m_default && numberSetting.m_value != m_default)
+ m_value = numberSetting.m_value;
+ if (m_min == 0.0 && numberSetting.m_min != 0.0)
+ m_min = numberSetting.m_min;
+ if (m_step == 1.0 && numberSetting.m_step != 1.0)
+ m_step = numberSetting.m_step;
+ if (m_max == 0.0 && numberSetting.m_max != 0.0)
+ m_max = numberSetting.m_max;
+}
+
+bool CSettingNumber::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ // get the default value
+ double value;
+ if (XMLUtils::GetDouble(node, SETTING_XML_ELM_DEFAULT, value))
+ m_value = m_default = value;
+ else if (!update)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get the minimum value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MINIMUM, m_min);
+ // get the step value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_STEP, m_step);
+ // get the maximum value
+ XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
+ }
+
+ return true;
+}
+
+bool CSettingNumber::FromString(const std::string &value)
+{
+ double dValue;
+ if (!fromString(value, dValue))
+ return false;
+
+ return SetValue(dValue);
+}
+
+std::string CSettingNumber::ToString() const
+{
+ std::ostringstream oss;
+ oss << m_value;
+
+ return oss.str();
+}
+
+bool CSettingNumber::Equals(const std::string &value) const
+{
+ double dValue;
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return (fromString(value, dValue) && m_value == dValue);
+}
+
+bool CSettingNumber::CheckValidity(const std::string &value) const
+{
+ double dValue;
+ if (!fromString(value, dValue))
+ return false;
+
+ return CheckValidity(dValue);
+}
+
+bool CSettingNumber::CheckValidity(double value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (m_min != m_max &&
+ (value < m_min || value > m_max))
+ return false;
+
+ return true;
+}
+
+bool CSettingNumber::SetValue(double value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ double oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingNumber>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingNumber>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingNumber>());
+ return true;
+}
+
+void CSettingNumber::SetDefault(double value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+void CSettingNumber::copy(const CSettingNumber &setting)
+{
+ CSetting::Copy(setting);
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_min = setting.m_min;
+ m_step = setting.m_step;
+ m_max = setting.m_max;
+}
+
+bool CSettingNumber::fromString(const std::string &strValue, double &value)
+{
+ if (strValue.empty())
+ return false;
+
+ char *end = nullptr;
+ value = strtod(strValue.c_str(), &end);
+ if (end != nullptr && *end != '\0')
+ return false;
+
+ return true;
+}
+
+const CSettingString::Value CSettingString::DefaultValue;
+Logger CSettingString::s_logger;
+
+CSettingString::CSettingString(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingString(id, DefaultLabel, DefaultValue, settingsManager)
+{ }
+
+CSettingString::CSettingString(const std::string& id, const CSettingString& setting)
+ : CSettingString(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+CSettingString::CSettingString(const std::string& id,
+ int label,
+ const std::string& value,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CTraitedSetting(id, settingsManager), m_value(value), m_default(value)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingString");
+}
+
+SettingPtr CSettingString::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingString>(id, *this);
+}
+
+void CSettingString::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::String)
+ return;
+
+ const auto& stringSetting = static_cast<const CSettingString&>(other);
+ if (m_default.empty() && !stringSetting.m_default.empty())
+ m_default = stringSetting.m_default;
+ if (m_value == m_default && stringSetting.m_value != m_default)
+ m_value = stringSetting.m_value;
+ if (m_allowEmpty == false && stringSetting.m_allowEmpty == true)
+ m_allowEmpty = stringSetting.m_allowEmpty;
+ if (m_allowNewOption == false && stringSetting.m_allowNewOption == true)
+ m_allowNewOption = stringSetting.m_allowNewOption;
+ if (m_translatableOptions.empty() && !stringSetting.m_translatableOptions.empty())
+ m_translatableOptions = stringSetting.m_translatableOptions;
+ if (m_options.empty() && !stringSetting.m_options.empty())
+ m_options = stringSetting.m_options;
+ if (m_optionsFillerName.empty() && !stringSetting.m_optionsFillerName.empty())
+ m_optionsFillerName = stringSetting.m_optionsFillerName;
+ if (m_optionsFiller == nullptr && stringSetting.m_optionsFiller != nullptr)
+ m_optionsFiller = stringSetting.m_optionsFiller;
+ if (m_optionsFillerData == nullptr && stringSetting.m_optionsFillerData != nullptr)
+ m_optionsFillerData = stringSetting.m_optionsFillerData;
+ if (m_dynamicOptions.empty() && !stringSetting.m_dynamicOptions.empty())
+ m_dynamicOptions = stringSetting.m_dynamicOptions;
+ if (m_optionsSort == SettingOptionsSort::NoSorting &&
+ stringSetting.m_optionsSort != SettingOptionsSort::NoSorting)
+ m_optionsSort = stringSetting.m_optionsSort;
+}
+
+bool CSettingString::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
+ if (constraints != nullptr)
+ {
+ // get allowempty (needs to be parsed before parsing the default value)
+ XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWEMPTY, m_allowEmpty);
+
+ // Values other than those in options constraints allowed to be added
+ XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWNEWOPTION, m_allowNewOption);
+
+ // get the entries
+ auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
+ if (options != nullptr && options->FirstChild() != nullptr)
+ {
+ if (!DeserializeOptionsSort(options, m_optionsSort))
+ s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
+ m_id);
+
+ if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
+ {
+ m_optionsFillerName = options->FirstChild()->ValueStr();
+ if (!m_optionsFillerName.empty())
+ {
+ m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
+ }
+ }
+ else
+ {
+ m_translatableOptions.clear();
+ auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
+ while (optionElement != nullptr)
+ {
+ TranslatableStringSettingOption entry;
+ if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.first) == TIXML_SUCCESS && entry.first > 0)
+ {
+ entry.second = optionElement->FirstChild()->Value();
+ m_translatableOptions.push_back(entry);
+ }
+ else
+ {
+ const std::string value = optionElement->FirstChild()->Value();
+ // if a specific "label" attribute is present use it otherwise use the value as label
+ std::string label = value;
+ optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label);
+
+ m_options.emplace_back(label, value);
+ }
+
+ optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
+ }
+ }
+ }
+ }
+
+ // get the default value
+ std::string value;
+ if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, value) &&
+ (!value.empty() || m_allowEmpty))
+ m_value = m_default = value;
+ else if (!update && !m_allowEmpty)
+ {
+ s_logger->error("error reading the default value of \"{}\"", m_id);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingString::CheckValidity(const std::string &value) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_allowEmpty && value.empty())
+ return false;
+
+ if (!m_translatableOptions.empty())
+ {
+ if (!CheckSettingOptionsValidity(value, m_translatableOptions))
+ return false;
+ }
+ else if (!m_options.empty() && !m_allowNewOption)
+ {
+ if (!CheckSettingOptionsValidity(value, m_options))
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingString::SetValue(const std::string &value)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+
+ if (value == m_value)
+ return true;
+
+ if (!CheckValidity(value))
+ return false;
+
+ std::string oldValue = m_value;
+ m_value = value;
+
+ if (!OnSettingChanging(shared_from_base<CSettingString>()))
+ {
+ m_value = oldValue;
+
+ // the setting couldn't be changed because one of the
+ // callback handlers failed the OnSettingChanging()
+ // callback so we need to let all the callback handlers
+ // know that the setting hasn't changed
+ OnSettingChanging(shared_from_base<CSettingString>());
+ return false;
+ }
+
+ m_changed = m_value != m_default;
+ OnSettingChanged(shared_from_base<CSettingString>());
+ return true;
+}
+
+void CSettingString::SetDefault(const std::string &value)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ m_default = value;
+ if (!m_changed)
+ m_value = m_default;
+}
+
+SettingOptionsType CSettingString::GetOptionsType() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (!m_translatableOptions.empty())
+ return SettingOptionsType::StaticTranslatable;
+ if (!m_options.empty())
+ return SettingOptionsType::Static;
+ if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
+ return SettingOptionsType::Dynamic;
+
+ return SettingOptionsType::Unknown;
+}
+
+StringSettingOptions CSettingString::UpdateDynamicOptions()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ StringSettingOptions options;
+ if (m_optionsFiller == nullptr &&
+ (m_optionsFillerName.empty() || m_settingsManager == nullptr))
+ return options;
+
+ if (m_optionsFiller == nullptr)
+ {
+ m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
+ if (m_optionsFiller == nullptr)
+ {
+ s_logger->error("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
+ return options;
+ }
+ }
+
+ std::string bestMatchingValue = m_value;
+ m_optionsFiller(shared_from_base<CSettingString>(), options, bestMatchingValue, m_optionsFillerData);
+
+ if (bestMatchingValue != m_value)
+ SetValue(bestMatchingValue);
+
+ // check if the list of items has changed
+ bool changed = m_dynamicOptions.size() != options.size();
+ if (!changed)
+ {
+ for (size_t index = 0; index < options.size(); index++)
+ {
+ if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
+ options[index].value.compare(m_dynamicOptions[index].value) != 0)
+ {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed)
+ {
+ m_dynamicOptions = options;
+ OnSettingPropertyChanged(shared_from_base<CSettingString>(), "options");
+ }
+
+ return options;
+}
+
+void CSettingString::copy(const CSettingString &setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_value = setting.m_value;
+ m_default = setting.m_default;
+ m_allowEmpty = setting.m_allowEmpty;
+ m_allowNewOption = setting.m_allowNewOption;
+ m_translatableOptions = setting.m_translatableOptions;
+ m_options = setting.m_options;
+ m_optionsFillerName = setting.m_optionsFillerName;
+ m_optionsFiller = setting.m_optionsFiller;
+ m_optionsFillerData = setting.m_optionsFillerData;
+ m_dynamicOptions = setting.m_dynamicOptions;
+}
+
+Logger CSettingAction::s_logger;
+
+CSettingAction::CSettingAction(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingAction(id, DefaultLabel, settingsManager)
+{ }
+
+CSettingAction::CSettingAction(const std::string& id,
+ int label,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSetting(id, settingsManager)
+{
+ SetLabel(label);
+
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingAction");
+}
+
+CSettingAction::CSettingAction(const std::string& id, const CSettingAction& setting)
+ : CSettingAction(id, setting.m_settingsManager)
+{
+ copy(setting);
+}
+
+SettingPtr CSettingAction::Clone(const std::string &id) const
+{
+ return std::make_shared<CSettingAction>(id, *this);
+}
+
+void CSettingAction::MergeDetails(const CSetting& other)
+{
+ if (other.GetType() != SettingType::Action)
+ return;
+
+ const auto& actionSetting = static_cast<const CSettingAction&>(other);
+ if (!HasData() && actionSetting.HasData())
+ SetData(actionSetting.GetData());
+}
+
+bool CSettingAction::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+
+ if (!CSetting::Deserialize(node, update))
+ return false;
+
+ m_data = XMLUtils::GetString(node, SETTING_XML_ELM_DATA);
+
+ return true;
+}
+
+void CSettingAction::copy(const CSettingAction& setting)
+{
+ CSetting::Copy(setting);
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_data = setting.m_data;
+}
diff --git a/xbmc/settings/lib/Setting.h b/xbmc/settings/lib/Setting.h
new file mode 100644
index 0000000..9e3ed3f
--- /dev/null
+++ b/xbmc/settings/lib/Setting.h
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "ISetting.h"
+#include "ISettingCallback.h"
+#include "ISettingControl.h"
+#include "SettingDefinitions.h"
+#include "SettingDependency.h"
+#include "SettingLevel.h"
+#include "SettingType.h"
+#include "SettingUpdate.h"
+#include "threads/SharedSection.h"
+#include "utils/logtypes.h"
+
+#include <memory>
+#include <set>
+#include <shared_mutex>
+#include <string>
+#include <utility>
+#include <vector>
+
+enum class SettingOptionsType {
+ Unknown = 0,
+ StaticTranslatable,
+ Static,
+ Dynamic
+};
+
+class CSetting;
+using SettingPtr = std::shared_ptr<CSetting>;
+using SettingConstPtr = std::shared_ptr<const CSetting>;
+using SettingList = std::vector<SettingPtr>;
+
+/*!
+ \ingroup settings
+ \brief Setting base class containing all the properties which are common to
+ all settings independent of the setting type.
+ */
+class CSetting : public ISetting,
+ protected ISettingCallback,
+ public std::enable_shared_from_this<CSetting>
+{
+public:
+ CSetting(const std::string& id, CSettingsManager* settingsManager = nullptr);
+ CSetting(const std::string& id, const CSetting& setting);
+ ~CSetting() override = default;
+
+ virtual std::shared_ptr<CSetting> Clone(const std::string &id) const = 0;
+ void MergeBasics(const CSetting& other);
+ virtual void MergeDetails(const CSetting& other) = 0;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ virtual SettingType GetType() const = 0;
+ virtual bool FromString(const std::string &value) = 0;
+ virtual std::string ToString() const = 0;
+ virtual bool Equals(const std::string &value) const = 0;
+ virtual bool CheckValidity(const std::string &value) const = 0;
+ virtual void Reset() = 0;
+
+ bool IsEnabled() const;
+ bool GetEnabled() const { return m_enabled; }
+ void SetEnabled(bool enabled);
+ bool IsDefault() const { return !m_changed; }
+ const std::string& GetParent() const { return m_parentSetting; }
+ void SetParent(const std::string& parentSetting) { m_parentSetting = parentSetting; }
+ SettingLevel GetLevel() const { return m_level; }
+ void SetLevel(SettingLevel level) { m_level = level; }
+ std::shared_ptr<const ISettingControl> GetControl() const { return m_control; }
+ std::shared_ptr<ISettingControl> GetControl() { return m_control; }
+ void SetControl(std::shared_ptr<ISettingControl> control) { m_control = std::move(control); }
+ const SettingDependencies& GetDependencies() const { return m_dependencies; }
+ void SetDependencies(const SettingDependencies &dependencies) { m_dependencies = dependencies; }
+ const std::set<CSettingUpdate>& GetUpdates() const { return m_updates; }
+
+ void SetCallback(ISettingCallback *callback) { m_callback = callback; }
+
+ bool IsReference() const { return !m_referencedId.empty(); }
+ const std::string& GetReferencedId() const { return m_referencedId; }
+ void SetReferencedId(const std::string& referencedId) { m_referencedId = referencedId; }
+ void MakeReference(const std::string& referencedId = "");
+
+ bool GetVisible() const { return ISetting::IsVisible(); }
+ // overrides of ISetting
+ bool IsVisible() const override;
+
+ // implementation of ISettingCallback
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+ /*!
+ \brief Deserializes the given XML node to retrieve a setting object's identifier and
+ whether the setting is a reference to another setting or not.
+
+ \param node XML node containing a setting object's identifier
+ \param identification Will contain the deserialized setting object's identifier
+ \param isReference Whether the setting is a reference to the setting with the determined identifier
+ \return True if a setting object's identifier was deserialized, false otherwise
+ */
+ static bool DeserializeIdentification(const TiXmlNode* node,
+ std::string& identification,
+ bool& isReference);
+
+protected:
+ // implementation of ISettingCallback
+ bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode) override;
+ void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName) override;
+
+ void Copy(const CSetting &setting);
+
+ template<class TSetting>
+ std::shared_ptr<TSetting> shared_from_base()
+ {
+ return std::static_pointer_cast<TSetting>(shared_from_this());
+ }
+
+ ISettingCallback *m_callback = nullptr;
+ bool m_enabled = true;
+ std::string m_parentSetting;
+ SettingLevel m_level = SettingLevel::Standard;
+ std::shared_ptr<ISettingControl> m_control;
+ SettingDependencies m_dependencies;
+ std::set<CSettingUpdate> m_updates;
+ bool m_changed = false;
+ mutable CSharedSection m_critical;
+
+ std::string m_referencedId;
+
+private:
+ static Logger s_logger;
+};
+
+template<typename TValue, SettingType TSettingType>
+class CTraitedSetting : public CSetting
+{
+public:
+ typedef TValue Value;
+
+ // implementation of CSetting
+ SettingType GetType() const override { return TSettingType; }
+
+ static SettingType Type() { return TSettingType; }
+
+protected:
+ CTraitedSetting(const std::string& id, CSettingsManager* settingsManager = nullptr)
+ : CSetting(id, settingsManager)
+ { }
+ CTraitedSetting(const std::string& id, const CTraitedSetting& setting) : CSetting(id, setting) {}
+ ~CTraitedSetting() override = default;
+};
+
+/*!
+ \ingroup settings
+ \brief List setting implementation
+ \sa CSetting
+ */
+class CSettingList : public CSetting
+{
+public:
+ CSettingList(const std::string &id, std::shared_ptr<CSetting> settingDefinition, CSettingsManager *settingsManager = nullptr);
+ CSettingList(const std::string &id, std::shared_ptr<CSetting> settingDefinition, int label, CSettingsManager *settingsManager = nullptr);
+ CSettingList(const std::string &id, const CSettingList &setting);
+ ~CSettingList() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ SettingType GetType() const override { return SettingType::List; }
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override;
+
+ SettingType GetElementType() const;
+ std::shared_ptr<CSetting> GetDefinition() { return m_definition; }
+ std::shared_ptr<const CSetting> GetDefinition() const { return m_definition; }
+ void SetDefinition(std::shared_ptr<CSetting> definition) { m_definition = std::move(definition); }
+
+ const std::string& GetDelimiter() const { return m_delimiter; }
+ void SetDelimiter(const std::string &delimiter) { m_delimiter = delimiter; }
+ int GetMinimumItems() const { return m_minimumItems; }
+ void SetMinimumItems(int minimumItems) { m_minimumItems = minimumItems; }
+ int GetMaximumItems() const { return m_maximumItems; }
+ void SetMaximumItems(int maximumItems) { m_maximumItems = maximumItems; }
+
+ bool FromString(const std::vector<std::string> &value);
+
+ const SettingList& GetValue() const { return m_values; }
+ bool SetValue(const SettingList &values);
+ const SettingList& GetDefault() const { return m_defaults; }
+ void SetDefault(const SettingList &values);
+
+protected:
+ void copy(const CSettingList &setting);
+ static void copy(const SettingList &srcValues, SettingList &dstValues);
+ bool fromString(const std::string &strValue, SettingList &values) const;
+ bool fromValues(const std::vector<std::string> &strValues, SettingList &values) const;
+ std::string toString(const SettingList &values) const;
+
+ SettingList m_values;
+ SettingList m_defaults;
+ std::shared_ptr<CSetting> m_definition;
+ std::string m_delimiter = "|";
+ int m_minimumItems = 0;
+ int m_maximumItems = -1;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Boolean setting implementation.
+ \sa CSetting
+ */
+class CSettingBool : public CTraitedSetting<bool, SettingType::Boolean>
+{
+public:
+ CSettingBool(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingBool(const std::string &id, const CSettingBool &setting);
+ CSettingBool(const std::string &id, int label, bool value, CSettingsManager *settingsManager = nullptr);
+ ~CSettingBool() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override { SetValue(m_default); }
+
+ bool GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(bool value);
+ bool GetDefault() const { return m_default; }
+ void SetDefault(bool value);
+
+private:
+ static constexpr Value DefaultValue = false;
+
+ void copy(const CSettingBool &setting);
+ bool fromString(const std::string &strValue, bool &value) const;
+
+ bool m_value = DefaultValue;
+ bool m_default = DefaultValue;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Integer setting implementation
+ \sa CSetting
+ */
+class CSettingInt : public CTraitedSetting<int, SettingType::Integer>
+{
+public:
+ CSettingInt(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, const CSettingInt &setting);
+ CSettingInt(const std::string &id, int label, int value, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, int label, int value, int minimum, int step, int maximum, CSettingsManager *settingsManager = nullptr);
+ CSettingInt(const std::string &id, int label, int value, const TranslatableIntegerSettingOptions &options, CSettingsManager *settingsManager = nullptr);
+ ~CSettingInt() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ virtual bool CheckValidity(int value) const;
+ void Reset() override { SetValue(m_default); }
+
+ int GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(int value);
+ int GetDefault() const { return m_default; }
+ void SetDefault(int value);
+
+ int GetMinimum() const { return m_min; }
+ void SetMinimum(int minimum) { m_min = minimum; }
+ int GetStep() const { return m_step; }
+ void SetStep(int step) { m_step = step; }
+ int GetMaximum() const { return m_max; }
+ void SetMaximum(int maximum) { m_max = maximum; }
+
+ SettingOptionsType GetOptionsType() const;
+ const TranslatableIntegerSettingOptions& GetTranslatableOptions() const { return m_translatableOptions; }
+ void SetTranslatableOptions(const TranslatableIntegerSettingOptions &options) { m_translatableOptions = options; }
+ const IntegerSettingOptions& GetOptions() const { return m_options; }
+ void SetOptions(const IntegerSettingOptions &options) { m_options = options; }
+ const std::string& GetOptionsFillerName() const { return m_optionsFillerName; }
+ void SetOptionsFillerName(const std::string &optionsFillerName, void *data = nullptr)
+ {
+ m_optionsFillerName = optionsFillerName;
+ m_optionsFillerData = data;
+ }
+ void SetOptionsFiller(IntegerSettingOptionsFiller optionsFiller, void *data = nullptr)
+ {
+ m_optionsFiller = optionsFiller;
+ m_optionsFillerData = data;
+ }
+ IntegerSettingOptions GetDynamicOptions() const { return m_dynamicOptions; }
+ IntegerSettingOptions UpdateDynamicOptions();
+ SettingOptionsSort GetOptionsSort() const { return m_optionsSort; }
+ void SetOptionsSort(SettingOptionsSort optionsSort) { m_optionsSort = optionsSort; }
+
+private:
+ static constexpr Value DefaultValue = 0;
+ static constexpr Value DefaultMin = DefaultValue;
+ static constexpr Value DefaultStep = 1;
+ static constexpr Value DefaultMax = DefaultValue;
+
+ void copy(const CSettingInt &setting);
+ static bool fromString(const std::string &strValue, int &value);
+
+ int m_value = DefaultValue;
+ int m_default = DefaultValue;
+ int m_min = DefaultMin;
+ int m_step = DefaultStep;
+ int m_max = DefaultMax;
+ TranslatableIntegerSettingOptions m_translatableOptions;
+ IntegerSettingOptions m_options;
+ std::string m_optionsFillerName;
+ IntegerSettingOptionsFiller m_optionsFiller = nullptr;
+ void *m_optionsFillerData = nullptr;
+ IntegerSettingOptions m_dynamicOptions;
+ SettingOptionsSort m_optionsSort = SettingOptionsSort::NoSorting;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Real number setting implementation.
+ \sa CSetting
+ */
+class CSettingNumber : public CTraitedSetting<double, SettingType::Number>
+{
+public:
+ CSettingNumber(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingNumber(const std::string &id, const CSettingNumber &setting);
+ CSettingNumber(const std::string &id, int label, float value, CSettingsManager *settingsManager = nullptr);
+ CSettingNumber(const std::string &id, int label, float value, float minimum, float step, float maximum, CSettingsManager *settingsManager = nullptr);
+ ~CSettingNumber() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override;
+ std::string ToString() const override;
+ bool Equals(const std::string &value) const override;
+ bool CheckValidity(const std::string &value) const override;
+ virtual bool CheckValidity(double value) const;
+ void Reset() override { SetValue(m_default); }
+
+ double GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ bool SetValue(double value);
+ double GetDefault() const { return m_default; }
+ void SetDefault(double value);
+
+ double GetMinimum() const { return m_min; }
+ void SetMinimum(double minimum) { m_min = minimum; }
+ double GetStep() const { return m_step; }
+ void SetStep(double step) { m_step = step; }
+ double GetMaximum() const { return m_max; }
+ void SetMaximum(double maximum) { m_max = maximum; }
+
+private:
+ static constexpr Value DefaultValue = 0.0;
+ static constexpr Value DefaultMin = DefaultValue;
+ static constexpr Value DefaultStep = 1.0;
+ static constexpr Value DefaultMax = DefaultValue;
+
+ virtual void copy(const CSettingNumber &setting);
+ static bool fromString(const std::string &strValue, double &value);
+
+ double m_value = DefaultValue;
+ double m_default = DefaultValue;
+ double m_min = DefaultMin;
+ double m_step = DefaultStep;
+ double m_max = DefaultMax;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief String setting implementation.
+ \sa CSetting
+ */
+class CSettingString : public CTraitedSetting<std::string, SettingType::String>
+{
+public:
+ CSettingString(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingString(const std::string &id, const CSettingString &setting);
+ CSettingString(const std::string &id, int label, const std::string &value, CSettingsManager *settingsManager = nullptr);
+ ~CSettingString() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ bool FromString(const std::string &value) override { return SetValue(value); }
+ std::string ToString() const override { return m_value; }
+ bool Equals(const std::string &value) const override { return m_value == value; }
+ bool CheckValidity(const std::string &value) const override;
+ void Reset() override { SetValue(m_default); }
+
+ virtual const std::string& GetValue() const
+ {
+ std::shared_lock<CSharedSection> lock(m_critical);
+ return m_value;
+ }
+ virtual bool SetValue(const std::string &value);
+ virtual const std::string& GetDefault() const { return m_default; }
+ virtual void SetDefault(const std::string &value);
+
+ virtual bool AllowEmpty() const { return m_allowEmpty; }
+ void SetAllowEmpty(bool allowEmpty) { m_allowEmpty = allowEmpty; }
+ virtual bool AllowNewOption() const { return m_allowNewOption; }
+ void SetAllowNewOption(bool allowNewOption) { m_allowNewOption = allowNewOption; }
+
+ SettingOptionsType GetOptionsType() const;
+ const TranslatableStringSettingOptions& GetTranslatableOptions() const { return m_translatableOptions; }
+ void SetTranslatableOptions(const TranslatableStringSettingOptions &options) { m_translatableOptions = options; }
+ const StringSettingOptions& GetOptions() const { return m_options; }
+ void SetOptions(const StringSettingOptions &options) { m_options = options; }
+ const std::string& GetOptionsFillerName() const { return m_optionsFillerName; }
+ void SetOptionsFillerName(const std::string &optionsFillerName, void *data = nullptr)
+ {
+ m_optionsFillerName = optionsFillerName;
+ m_optionsFillerData = data;
+ }
+ void SetOptionsFiller(StringSettingOptionsFiller optionsFiller, void *data = nullptr)
+ {
+ m_optionsFiller = optionsFiller;
+ m_optionsFillerData = data;
+ }
+ StringSettingOptions GetDynamicOptions() const { return m_dynamicOptions; }
+ StringSettingOptions UpdateDynamicOptions();
+ SettingOptionsSort GetOptionsSort() const { return m_optionsSort; }
+ void SetOptionsSort(SettingOptionsSort optionsSort) { m_optionsSort = optionsSort; }
+
+protected:
+ static const Value DefaultValue;
+
+ virtual void copy(const CSettingString &setting);
+
+ std::string m_value;
+ std::string m_default;
+ bool m_allowEmpty = false;
+ bool m_allowNewOption = false;
+ TranslatableStringSettingOptions m_translatableOptions;
+ StringSettingOptions m_options;
+ std::string m_optionsFillerName;
+ StringSettingOptionsFiller m_optionsFiller = nullptr;
+ void *m_optionsFillerData = nullptr;
+ StringSettingOptions m_dynamicOptions;
+ SettingOptionsSort m_optionsSort = SettingOptionsSort::NoSorting;
+
+ static Logger s_logger;
+};
+
+/*!
+ \ingroup settings
+ \brief Action setting implementation.
+
+ A setting action will trigger a call to the OnSettingAction() callback method
+ when activated.
+
+ \sa CSetting
+ */
+class CSettingAction : public CSetting
+{
+public:
+ CSettingAction(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ CSettingAction(const std::string &id, int label, CSettingsManager *settingsManager = nullptr);
+ CSettingAction(const std::string &id, const CSettingAction &setting);
+ ~CSettingAction() override = default;
+
+ std::shared_ptr<CSetting> Clone(const std::string &id) const override;
+ void MergeDetails(const CSetting& other) override;
+
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ SettingType GetType() const override { return SettingType::Action; }
+ bool FromString(const std::string &value) override { return CheckValidity(value); }
+ std::string ToString() const override { return ""; }
+ bool Equals(const std::string &value) const override { return value.empty(); }
+ bool CheckValidity(const std::string &value) const override { return value.empty(); }
+ void Reset() override { }
+
+ bool HasData() const { return !m_data.empty(); }
+ const std::string& GetData() const { return m_data; }
+ void SetData(const std::string& data) { m_data = data; }
+
+protected:
+ virtual void copy(const CSettingAction& setting);
+
+ std::string m_data;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/SettingCategoryAccess.cpp b/xbmc/settings/lib/SettingCategoryAccess.cpp
new file mode 100644
index 0000000..4640586
--- /dev/null
+++ b/xbmc/settings/lib/SettingCategoryAccess.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013-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 "SettingCategoryAccess.h"
+
+#include "SettingConditions.h"
+#include "SettingsManager.h"
+
+bool CSettingCategoryAccessCondition::Check() const
+{
+ if (m_value.empty())
+ return true;
+
+ if (m_settingsManager == nullptr)
+ return false;
+
+ bool found = m_settingsManager->GetConditions().Check(m_value, "true");
+ if (m_negated)
+ return !found;
+
+ return found;
+}
+
+bool CSettingCategoryAccessConditionCombination::Check() const
+{
+ if (m_operations.empty() && m_values.empty())
+ return true;
+
+ return CSettingConditionCombination::Check();
+}
+
+CSettingCategoryAccess::CSettingCategoryAccess(CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingCategoryAccessConditionCombination(m_settingsManager));
+}
diff --git a/xbmc/settings/lib/SettingCategoryAccess.h b/xbmc/settings/lib/SettingCategoryAccess.h
new file mode 100644
index 0000000..5d4ceb8
--- /dev/null
+++ b/xbmc/settings/lib/SettingCategoryAccess.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+
+#include <set>
+#include <string>
+
+class CSettingCategoryAccessCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingCategoryAccessCondition(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionItem(settingsManager)
+ { }
+ ~CSettingCategoryAccessCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingCategoryAccessConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingCategoryAccessConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ ~CSettingCategoryAccessConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingCategoryAccessConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingCategoryAccessCondition(m_settingsManager); }
+};
+
+class CSettingCategoryAccess : public CSettingCondition
+{
+public:
+ explicit CSettingCategoryAccess(CSettingsManager *settingsManager = nullptr);
+ ~CSettingCategoryAccess() override = default;
+};
diff --git a/xbmc/settings/lib/SettingConditions.cpp b/xbmc/settings/lib/SettingConditions.cpp
new file mode 100644
index 0000000..7146834
--- /dev/null
+++ b/xbmc/settings/lib/SettingConditions.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2013-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 "SettingConditions.h"
+
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+
+bool CSettingConditionItem::Deserialize(const TiXmlNode *node)
+{
+ if (!CBooleanLogicValue::Deserialize(node))
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ // get the "name" attribute
+ auto strAttribute = elem->Attribute(SETTING_XML_ATTR_NAME);
+ if (strAttribute != nullptr)
+ m_name = strAttribute;
+
+ // get the "setting" attribute
+ strAttribute = elem->Attribute(SETTING_XML_ATTR_SETTING);
+ if (strAttribute != nullptr)
+ m_setting = strAttribute;
+
+ return true;
+}
+
+bool CSettingConditionItem::Check() const
+{
+ if (m_settingsManager == nullptr)
+ return false;
+
+ return m_settingsManager->GetConditions().Check(m_name, m_value, m_settingsManager->GetSetting(m_setting)) == !m_negated;
+}
+
+bool CSettingConditionCombination::Check() const
+{
+ bool ok = false;
+ for (const auto& operation : m_operations)
+ {
+ if (operation == nullptr)
+ continue;
+
+ const auto combination = std::static_pointer_cast<const CSettingConditionCombination>(operation);
+ if (combination == nullptr)
+ continue;
+
+ if (combination->Check())
+ ok = true;
+ else if (m_operation == BooleanLogicOperationAnd)
+ return false;
+ }
+
+ for (const auto& value : m_values)
+ {
+ if (value == nullptr)
+ continue;
+
+ const auto condition = std::static_pointer_cast<const CSettingConditionItem>(value);
+ if (condition == nullptr)
+ continue;
+
+ if (condition->Check())
+ ok = true;
+ else if (m_operation == BooleanLogicOperationAnd)
+ return false;
+ }
+
+ return ok;
+}
+
+CSettingCondition::CSettingCondition(CSettingsManager *settingsManager /* = nullptr */)
+ : ISettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingConditionCombination(settingsManager));
+}
+
+bool CSettingCondition::Check() const
+{
+ auto combination = std::static_pointer_cast<CSettingConditionCombination>(m_operation);
+ if (combination == nullptr)
+ return false;
+
+ return combination->Check();
+}
+
+void CSettingConditionsManager::AddCondition(std::string condition)
+{
+ if (condition.empty())
+ return;
+
+ StringUtils::ToLower(condition);
+
+ m_defines.insert(condition);
+}
+
+void CSettingConditionsManager::AddDynamicCondition(std::string identifier, SettingConditionCheck condition, void *data /*= nullptr*/)
+{
+ if (identifier.empty() || condition == nullptr)
+ return;
+
+ StringUtils::ToLower(identifier);
+
+ m_conditions.emplace(identifier, std::make_pair(condition, data));
+}
+
+void CSettingConditionsManager::RemoveDynamicCondition(std::string identifier)
+{
+ if (identifier.empty())
+ return;
+
+ StringUtils::ToLower(identifier);
+
+ auto it = m_conditions.find(identifier);
+ if (it != m_conditions.end())
+ m_conditions.erase(it);
+}
+
+bool CSettingConditionsManager::Check(
+ std::string condition,
+ const std::string& value /* = "" */,
+ const std::shared_ptr<const CSetting>& setting /* = nullptr */) const
+{
+ if (condition.empty())
+ return false;
+
+ StringUtils::ToLower(condition);
+
+ // special handling of "isdefined" conditions
+ if (condition == "isdefined")
+ {
+ std::string tmpValue = value;
+ StringUtils::ToLower(tmpValue);
+
+ return m_defines.find(tmpValue) != m_defines.end();
+ }
+
+ auto conditionIt = m_conditions.find(condition);
+ if (conditionIt == m_conditions.end())
+ return false;
+
+ return conditionIt->second.first(condition, value, setting, conditionIt->second.second);
+}
diff --git a/xbmc/settings/lib/SettingConditions.h b/xbmc/settings/lib/SettingConditions.h
new file mode 100644
index 0000000..aee2c06
--- /dev/null
+++ b/xbmc/settings/lib/SettingConditions.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "SettingDefinitions.h"
+#include "utils/BooleanLogic.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+
+class CSettingsManager;
+class CSetting;
+
+using SettingConditionCheck = bool (*)(const std::string& condition,
+ const std::string& value,
+ const std::shared_ptr<const CSetting>& setting,
+ void* data);
+
+class ISettingCondition
+{
+public:
+ explicit ISettingCondition(CSettingsManager *settingsManager)
+ : m_settingsManager(settingsManager)
+ { }
+ virtual ~ISettingCondition() = default;
+
+ virtual bool Check() const = 0;
+
+protected:
+ CSettingsManager *m_settingsManager;
+};
+
+class CSettingConditionItem : public CBooleanLogicValue, public ISettingCondition
+{
+public:
+ explicit CSettingConditionItem(CSettingsManager *settingsManager = nullptr)
+ : ISettingCondition(settingsManager)
+ { }
+ ~CSettingConditionItem() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+ const char* GetTag() const override { return SETTING_XML_ELM_CONDITION; }
+ bool Check() const override;
+
+protected:
+ std::string m_name;
+ std::string m_setting;
+};
+
+class CSettingConditionCombination : public CBooleanLogicOperation, public ISettingCondition
+{
+public:
+ explicit CSettingConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : ISettingCondition(settingsManager)
+ { }
+ ~CSettingConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingConditionItem(m_settingsManager); }
+};
+
+class CSettingCondition : public CBooleanLogic, public ISettingCondition
+{
+public:
+ explicit CSettingCondition(CSettingsManager *settingsManager = nullptr);
+ ~CSettingCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingConditionsManager
+{
+public:
+ CSettingConditionsManager() = default;
+ CSettingConditionsManager(const CSettingConditionsManager&) = delete;
+ CSettingConditionsManager const& operator=(CSettingConditionsManager const&) = delete;
+ virtual ~CSettingConditionsManager() = default;
+
+ void AddCondition(std::string condition);
+ void AddDynamicCondition(std::string identifier, SettingConditionCheck condition, void *data = nullptr);
+ void RemoveDynamicCondition(std::string identifier);
+
+ bool Check(
+ std::string condition,
+ const std::string& value = "",
+ const std::shared_ptr<const CSetting>& setting = std::shared_ptr<const CSetting>()) const;
+
+private:
+ using SettingConditionPair = std::pair<std::string, std::pair<SettingConditionCheck, void*>>;
+ using SettingConditionMap = std::map<std::string, std::pair<SettingConditionCheck, void*>>;
+
+ SettingConditionMap m_conditions;
+ std::set<std::string> m_defines;
+};
diff --git a/xbmc/settings/lib/SettingDefinitions.h b/xbmc/settings/lib/SettingDefinitions.h
new file mode 100644
index 0000000..ae18347
--- /dev/null
+++ b/xbmc/settings/lib/SettingDefinitions.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "utils/Variant.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#define SETTING_XML_ROOT "settings"
+#define SETTING_XML_ROOT_VERSION "version"
+
+#define SETTING_XML_ELM_SECTION "section"
+#define SETTING_XML_ELM_CATEGORY "category"
+#define SETTING_XML_ELM_GROUP "group"
+#define SETTING_XML_ELM_SETTING "setting"
+#define SETTING_XML_ELM_VISIBLE "visible"
+#define SETTING_XML_ELM_REQUIREMENT "requirement"
+#define SETTING_XML_ELM_CONDITION "condition"
+#define SETTING_XML_ELM_ENABLED "enable"
+#define SETTING_XML_ELM_LEVEL "level"
+#define SETTING_XML_ELM_DEFAULT "default"
+#define SETTING_XML_ELM_VALUE "value"
+#define SETTING_XML_ELM_CONTROL "control"
+#define SETTING_XML_ELM_CONSTRAINTS "constraints"
+#define SETTING_XML_ELM_OPTIONS "options"
+#define SETTING_XML_ELM_OPTION "option"
+#define SETTING_XML_ELM_MINIMUM "minimum"
+#define SETTING_XML_ELM_STEP "step"
+#define SETTING_XML_ELM_MAXIMUM "maximum"
+#define SETTING_XML_ELM_ALLOWEMPTY "allowempty"
+#define SETTING_XML_ELM_ALLOWNEWOPTION "allownewoption"
+#define SETTING_XML_ELM_DEPENDENCIES "dependencies"
+#define SETTING_XML_ELM_DEPENDENCY "dependency"
+#define SETTING_XML_ELM_UPDATES "updates"
+#define SETTING_XML_ELM_UPDATE "update"
+#define SETTING_XML_ELM_ACCESS "access"
+#define SETTING_XML_ELM_DELIMITER "delimiter"
+#define SETTING_XML_ELM_MINIMUM_ITEMS "minimumitems"
+#define SETTING_XML_ELM_MAXIMUM_ITEMS "maximumitems"
+#define SETTING_XML_ELM_DATA "data"
+
+#define SETTING_XML_ATTR_ID "id"
+#define SETTING_XML_ATTR_REFERENCE "ref"
+#define SETTING_XML_ATTR_LABEL "label"
+#define SETTING_XML_ATTR_HELP "help"
+#define SETTING_XML_ATTR_TYPE "type"
+#define SETTING_XML_ATTR_PARENT "parent"
+#define SETTING_XML_ATTR_FORMAT "format"
+#define SETTING_XML_ATTR_DELAYED "delayed"
+#define SETTING_XML_ATTR_ON "on"
+#define SETTING_XML_ATTR_OPERATOR "operator"
+#define SETTING_XML_ATTR_NAME "name"
+#define SETTING_XML_ATTR_SETTING "setting"
+#define SETTING_XML_ATTR_BEFORE "before"
+#define SETTING_XML_ATTR_AFTER "after"
+
+struct IntegerSettingOption
+{
+ IntegerSettingOption(const std::string& _label, int _value)
+ : label(_label), value(_value) {}
+
+ IntegerSettingOption(const std::string& _label,
+ const std::string& _label2,
+ int _value,
+ const std::vector<std::pair<std::string, CVariant>>& props)
+ : label(_label), label2(_label2), value(_value), properties(props)
+ {
+ }
+
+ std::string label;
+ std::string label2;
+ int value = 0;
+ std::vector<std::pair<std::string, CVariant>> properties;
+};
+
+struct StringSettingOption
+{
+ StringSettingOption(const std::string& _label, const std::string& _value)
+ : label(_label), value(_value) {}
+
+ StringSettingOption(const std::string& _label,
+ const std::string& _label2,
+ const std::string& _value,
+ const std::vector<std::pair<std::string, CVariant>>& props)
+ : label(_label), label2(_label2), value(_value), properties(props)
+ {
+ }
+
+ std::string label;
+ std::string label2;
+ std::string value;
+ std::vector<std::pair<std::string, CVariant>> properties;
+};
+
+struct TranslatableIntegerSettingOption
+{
+ TranslatableIntegerSettingOption() = default;
+ TranslatableIntegerSettingOption(int _label, int _value, const std::string& _addonId = "")
+ : label(_label), value(_value), addonId(_addonId)
+ {
+ }
+
+ int label = 0;
+ int value = 0;
+ std::string addonId; // Leaved empty for Kodi labels
+};
+
+using TranslatableIntegerSettingOptions = std::vector<TranslatableIntegerSettingOption>;
+using IntegerSettingOptions = std::vector<IntegerSettingOption>;
+using TranslatableStringSettingOption = std::pair<int, std::string>;
+using TranslatableStringSettingOptions = std::vector<TranslatableStringSettingOption>;
+using StringSettingOptions = std::vector<StringSettingOption>;
+
+class CSetting;
+using IntegerSettingOptionsFiller = void (*)(const std::shared_ptr<const CSetting>& setting,
+ IntegerSettingOptions& list,
+ int& current,
+ void* data);
+using StringSettingOptionsFiller = void (*)(const std::shared_ptr<const CSetting>& setting,
+ StringSettingOptions& list,
+ std::string& current,
+ void* data);
+
+enum class SettingOptionsSort
+{
+ NoSorting,
+ Ascending,
+ Descending
+};
diff --git a/xbmc/settings/lib/SettingDependency.cpp b/xbmc/settings/lib/SettingDependency.cpp
new file mode 100644
index 0000000..e3122da
--- /dev/null
+++ b/xbmc/settings/lib/SettingDependency.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2013-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 "SettingDependency.h"
+
+#include "ServiceBroker.h"
+#include "Setting.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <memory>
+#include <set>
+#include <stdlib.h>
+#include <string>
+
+Logger CSettingDependencyCondition::s_logger;
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(settingsManager, "", "", "")
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyOperator op,
+ bool negated /* = false */,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(
+ settingsManager, setting, setting, value, SettingDependencyTarget::Setting, op, negated)
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ const std::string& strProperty,
+ const std::string& value,
+ const std::string& setting /* = "" */,
+ bool negated /* = false */,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependencyCondition(settingsManager,
+ strProperty,
+ setting,
+ value,
+ SettingDependencyTarget::Property,
+ SettingDependencyOperator::Equals,
+ negated)
+{
+}
+
+CSettingDependencyCondition::CSettingDependencyCondition(
+ CSettingsManager* settingsManager,
+ const std::string& strProperty,
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyTarget target /* = SettingDependencyTarget::Unknown */,
+ SettingDependencyOperator op /* = SettingDependencyOperator::Equals */,
+ bool negated /* = false */)
+ : CSettingConditionItem(settingsManager), m_target(target), m_operator(op)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingDependencyCondition");
+
+ m_name = strProperty;
+ m_setting = setting;
+ m_value = value;
+ m_negated = negated;
+}
+
+bool CSettingDependencyCondition::Deserialize(const TiXmlNode *node)
+{
+ if (!CSettingConditionItem::Deserialize(node))
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ m_target = SettingDependencyTarget::Setting;
+ auto strTarget = elem->Attribute(SETTING_XML_ATTR_ON);
+ if (strTarget != nullptr && !setTarget(strTarget))
+ {
+ s_logger->warn("unknown target \"{}\"", strTarget);
+ return false;
+ }
+
+ if (m_target != SettingDependencyTarget::Setting && m_name.empty())
+ {
+ s_logger->warn("missing name for dependency");
+ return false;
+ }
+
+ if (m_target == SettingDependencyTarget::Setting)
+ {
+ if (m_setting.empty())
+ {
+ s_logger->warn("missing setting for dependency");
+ return false;
+ }
+
+ m_name = m_setting;
+ }
+
+ m_operator = SettingDependencyOperator::Equals;
+ auto strOperator = elem->Attribute(SETTING_XML_ATTR_OPERATOR);
+ if (strOperator != nullptr && !setOperator(strOperator))
+ {
+ s_logger->warn("unknown operator \"{}\"", strOperator);
+ return false;
+ }
+
+ return true;
+}
+
+bool CSettingDependencyCondition::Check() const
+{
+ if (m_name.empty() ||
+ m_target == SettingDependencyTarget::Unknown ||
+ m_operator == SettingDependencyOperator::Unknown ||
+ m_settingsManager == nullptr)
+ return false;
+
+ bool result = false;
+ switch (m_target)
+ {
+ case SettingDependencyTarget::Setting:
+ {
+ if (m_setting.empty())
+ return false;
+
+ auto setting = m_settingsManager->GetSetting(m_setting);
+ if (setting == nullptr)
+ {
+ s_logger->warn("unable to check condition on unknown setting \"{}\"", m_setting);
+ return false;
+ }
+
+ switch (m_operator)
+ {
+ case SettingDependencyOperator::Equals:
+ result = setting->Equals(m_value);
+ break;
+
+ case SettingDependencyOperator::LessThan:
+ {
+ const auto value = setting->ToString();
+ if (StringUtils::IsInteger(m_value))
+ result = strtol(value.c_str(), nullptr, 0) < strtol(m_value.c_str(), nullptr, 0);
+ else
+ result = value.compare(m_value) < 0;
+ break;
+ }
+
+ case SettingDependencyOperator::GreaterThan:
+ {
+ const auto value = setting->ToString();
+ if (StringUtils::IsInteger(m_value))
+ result = strtol(value.c_str(), nullptr, 0) > strtol(m_value.c_str(), nullptr, 0);
+ else
+ result = value.compare(m_value) > 0;
+ break;
+ }
+
+ case SettingDependencyOperator::Contains:
+ result = (setting->ToString().find(m_value) != std::string::npos);
+ break;
+
+ case SettingDependencyOperator::Unknown:
+ default:
+ break;
+ }
+
+ break;
+ }
+
+ case SettingDependencyTarget::Property:
+ {
+ SettingConstPtr setting;
+ if (!m_setting.empty())
+ {
+ setting = m_settingsManager->GetSetting(m_setting);
+ if (setting == nullptr)
+ {
+ s_logger->warn("unable to check condition on unknown setting \"{}\"", m_setting);
+ return false;
+ }
+ }
+ result = m_settingsManager->GetConditions().Check(m_name, m_value, setting);
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+ return result == !m_negated;
+}
+
+bool CSettingDependencyCondition::setTarget(const std::string &target)
+{
+ if (StringUtils::EqualsNoCase(target, "setting"))
+ m_target = SettingDependencyTarget::Setting;
+ else if (StringUtils::EqualsNoCase(target, "property"))
+ m_target = SettingDependencyTarget::Property;
+ else
+ return false;
+
+ return true;
+}
+
+bool CSettingDependencyCondition::setOperator(const std::string &op)
+{
+ size_t length = 0;
+ if (StringUtils::EndsWithNoCase(op, "is"))
+ {
+ m_operator = SettingDependencyOperator::Equals;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "lessthan"))
+ {
+ m_operator = SettingDependencyOperator::LessThan;
+ length = 8;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "lt"))
+ {
+ m_operator = SettingDependencyOperator::LessThan;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "greaterthan"))
+ {
+ m_operator = SettingDependencyOperator::GreaterThan;
+ length = 11;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "gt"))
+ {
+ m_operator = SettingDependencyOperator::GreaterThan;
+ length = 2;
+ }
+ else if (StringUtils::EndsWithNoCase(op, "contains"))
+ {
+ m_operator = SettingDependencyOperator::Contains;
+ length = 8;
+ }
+
+ if (op.size() > length + 1)
+ return false;
+ if (op.size() == length + 1)
+ {
+ if (!StringUtils::StartsWith(op, "!"))
+ return false;
+ m_negated = true;
+ }
+
+ return true;
+}
+
+bool CSettingDependencyConditionCombination::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ size_t numOperations = m_operations.size();
+ size_t numValues = m_values.size();
+
+ if (!CSettingConditionCombination::Deserialize(node))
+ return false;
+
+ if (numOperations < m_operations.size())
+ {
+ for (size_t i = numOperations; i < m_operations.size(); i++)
+ {
+ if (m_operations[i] == nullptr)
+ continue;
+
+ auto combination = static_cast<CSettingDependencyConditionCombination*>(m_operations[i].get());
+ if (combination == nullptr)
+ continue;
+
+ const std::set<std::string>& settings = combination->GetSettings();
+ m_settings.insert(settings.begin(), settings.end());
+ }
+ }
+
+ if (numValues < m_values.size())
+ {
+ for (size_t i = numValues; i < m_values.size(); i++)
+ {
+ if (m_values[i] == nullptr)
+ continue;
+
+ auto condition = static_cast<CSettingDependencyCondition*>(m_values[i].get());
+ if (condition == nullptr)
+ continue;
+
+ auto settingId = condition->GetSetting();
+ if (!settingId.empty())
+ m_settings.insert(settingId);
+ }
+ }
+
+ return true;
+}
+
+CSettingDependencyConditionCombination* CSettingDependencyConditionCombination::Add(
+ const CSettingDependencyConditionPtr& condition)
+{
+ if (condition != nullptr)
+ {
+ m_values.push_back(condition);
+
+ auto settingId = condition->GetSetting();
+ if (!settingId.empty())
+ m_settings.insert(settingId);
+ }
+
+ return this;
+}
+
+CSettingDependencyConditionCombination* CSettingDependencyConditionCombination::Add(
+ const CSettingDependencyConditionCombinationPtr& operation)
+{
+ if (operation != nullptr)
+ {
+ m_operations.push_back(operation);
+
+ const auto& settings = operation->GetSettings();
+ m_settings.insert(settings.begin(), settings.end());
+ }
+
+ return this;
+}
+
+Logger CSettingDependency::s_logger;
+
+CSettingDependency::CSettingDependency(CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingDependency(SettingDependencyType::Unknown, settingsManager)
+{
+}
+
+CSettingDependency::CSettingDependency(SettingDependencyType type,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager), m_type(type)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingDependency");
+
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+}
+
+bool CSettingDependency::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strType = elem->Attribute(SETTING_XML_ATTR_TYPE);
+ if (strType == nullptr || strlen(strType) <= 0 || !setType(strType))
+ {
+ s_logger->warn("missing or unknown dependency type definition");
+ return false;
+ }
+
+ return CSettingCondition::Deserialize(node);
+}
+
+std::set<std::string> CSettingDependency::GetSettings() const
+{
+ if (m_operation == nullptr)
+ return std::set<std::string>();
+
+ auto combination = static_cast<CSettingDependencyConditionCombination*>(m_operation.get());
+ if (combination == nullptr)
+ return std::set<std::string>();
+
+ return combination->GetSettings();
+}
+
+CSettingDependencyConditionCombinationPtr CSettingDependency::And()
+{
+ if (m_operation == nullptr)
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+
+ m_operation->SetOperation(BooleanLogicOperationAnd);
+
+ return std::dynamic_pointer_cast<CSettingDependencyConditionCombination>(m_operation);
+}
+
+CSettingDependencyConditionCombinationPtr CSettingDependency::Or()
+{
+ if (m_operation == nullptr)
+ m_operation = CBooleanLogicOperationPtr(new CSettingDependencyConditionCombination(m_settingsManager));
+
+ m_operation->SetOperation(BooleanLogicOperationOr);
+
+ return std::dynamic_pointer_cast<CSettingDependencyConditionCombination>(m_operation);
+}
+
+bool CSettingDependency::setType(const std::string &type)
+{
+ if (StringUtils::EqualsNoCase(type, "enable"))
+ m_type = SettingDependencyType::Enable;
+ else if (StringUtils::EqualsNoCase(type, "update"))
+ m_type = SettingDependencyType::Update;
+ else if (StringUtils::EqualsNoCase(type, "visible"))
+ m_type = SettingDependencyType::Visible;
+ else
+ return false;
+
+ return true;
+}
diff --git a/xbmc/settings/lib/SettingDependency.h b/xbmc/settings/lib/SettingDependency.h
new file mode 100644
index 0000000..5cb34db
--- /dev/null
+++ b/xbmc/settings/lib/SettingDependency.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+#include "utils/BooleanLogic.h"
+#include "utils/logtypes.h"
+
+#include <list>
+#include <set>
+#include <string>
+
+enum class SettingDependencyType {
+ Unknown = 0,
+ Enable,
+ Update,
+ Visible
+};
+
+enum class SettingDependencyOperator {
+ Unknown = 0,
+ Equals,
+ LessThan,
+ GreaterThan,
+ Contains
+};
+
+enum class SettingDependencyTarget {
+ Unknown = 0,
+ Setting,
+ Property
+};
+
+class CSettingDependencyCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingDependencyCondition(CSettingsManager *settingsManager = nullptr);
+ CSettingDependencyCondition(const std::string &setting, const std::string &value,
+ SettingDependencyOperator op, bool negated = false,
+ CSettingsManager *settingsManager = nullptr);
+ CSettingDependencyCondition(const std::string &strProperty, const std::string &value,
+ const std::string &setting = "", bool negated = false,
+ CSettingsManager *settingsManager = nullptr);
+ ~CSettingDependencyCondition() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+ bool Check() const override;
+
+ const std::string& GetName() const { return m_name; }
+ const std::string& GetSetting() const { return m_setting; }
+ SettingDependencyTarget GetTarget() const { return m_target; }
+ SettingDependencyOperator GetOperator() const { return m_operator; }
+
+private:
+ CSettingDependencyCondition(CSettingsManager* settingsManager,
+ const std::string& strProperty,
+ const std::string& setting,
+ const std::string& value,
+ SettingDependencyTarget target = SettingDependencyTarget::Unknown,
+ SettingDependencyOperator op = SettingDependencyOperator::Equals,
+ bool negated = false);
+
+ bool setTarget(const std::string &target);
+ bool setOperator(const std::string &op);
+
+ SettingDependencyTarget m_target = SettingDependencyTarget::Unknown;
+ SettingDependencyOperator m_operator = SettingDependencyOperator::Equals;
+
+ static Logger s_logger;
+};
+
+using CSettingDependencyConditionPtr = std::shared_ptr<CSettingDependencyCondition>;
+
+class CSettingDependencyConditionCombination;
+using CSettingDependencyConditionCombinationPtr = std::shared_ptr<CSettingDependencyConditionCombination>;
+
+class CSettingDependencyConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingDependencyConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ CSettingDependencyConditionCombination(BooleanLogicOperation op, CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ {
+ SetOperation(op);
+ }
+ ~CSettingDependencyConditionCombination() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+
+ const std::set<std::string>& GetSettings() const { return m_settings; }
+
+ CSettingDependencyConditionCombination* Add(const CSettingDependencyConditionPtr& condition);
+ CSettingDependencyConditionCombination* Add(
+ const CSettingDependencyConditionCombinationPtr& operation);
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingDependencyConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingDependencyCondition(m_settingsManager); }
+
+ std::set<std::string> m_settings;
+};
+
+class CSettingDependency : public CSettingCondition
+{
+public:
+ explicit CSettingDependency(CSettingsManager *settingsManager = nullptr);
+ CSettingDependency(SettingDependencyType type, CSettingsManager *settingsManager = nullptr);
+ ~CSettingDependency() override = default;
+
+ bool Deserialize(const TiXmlNode *node) override;
+
+ SettingDependencyType GetType() const { return m_type; }
+ std::set<std::string> GetSettings() const;
+
+ CSettingDependencyConditionCombinationPtr And();
+ CSettingDependencyConditionCombinationPtr Or();
+
+private:
+ bool setType(const std::string &type);
+
+ SettingDependencyType m_type = SettingDependencyType::Unknown;
+
+ static Logger s_logger;
+};
+
+using SettingDependencies = std::list<CSettingDependency>;
+using SettingDependencyMap = std::map<std::string, SettingDependencies>;
diff --git a/xbmc/settings/lib/SettingLevel.h b/xbmc/settings/lib/SettingLevel.h
new file mode 100644
index 0000000..db8e2b5
--- /dev/null
+++ b/xbmc/settings/lib/SettingLevel.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Levels which every setting is assigned to.
+ */
+enum class SettingLevel {
+ Basic = 0,
+ Standard,
+ Advanced,
+ Expert,
+ Internal
+};
diff --git a/xbmc/settings/lib/SettingRequirement.cpp b/xbmc/settings/lib/SettingRequirement.cpp
new file mode 100644
index 0000000..5ba81ab
--- /dev/null
+++ b/xbmc/settings/lib/SettingRequirement.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013-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 "SettingRequirement.h"
+
+#include "SettingsManager.h"
+
+bool CSettingRequirementCondition::Check() const
+{
+ if (m_settingsManager == nullptr)
+ return false;
+
+ bool found = m_settingsManager->GetConditions().Check("IsDefined", m_value);
+ if (m_negated)
+ return !found;
+
+ return found;
+}
+
+bool CSettingRequirementConditionCombination::Check() const
+{
+ if (m_operations.empty() && m_values.empty())
+ return true;
+
+ return CSettingConditionCombination::Check();
+}
+
+CSettingRequirement::CSettingRequirement(CSettingsManager *settingsManager /* = nullptr */)
+ : CSettingCondition(settingsManager)
+{
+ m_operation = CBooleanLogicOperationPtr(new CSettingRequirementConditionCombination(m_settingsManager));
+}
diff --git a/xbmc/settings/lib/SettingRequirement.h b/xbmc/settings/lib/SettingRequirement.h
new file mode 100644
index 0000000..f8357a5
--- /dev/null
+++ b/xbmc/settings/lib/SettingRequirement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "SettingConditions.h"
+
+#include <set>
+#include <string>
+
+class CSettingRequirementCondition : public CSettingConditionItem
+{
+public:
+ explicit CSettingRequirementCondition(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionItem(settingsManager)
+ { }
+ ~CSettingRequirementCondition() override = default;
+
+ bool Check() const override;
+};
+
+class CSettingRequirementConditionCombination : public CSettingConditionCombination
+{
+public:
+ explicit CSettingRequirementConditionCombination(CSettingsManager *settingsManager = nullptr)
+ : CSettingConditionCombination(settingsManager)
+ { }
+ ~CSettingRequirementConditionCombination() override = default;
+
+ bool Check() const override;
+
+private:
+ CBooleanLogicOperation* newOperation() override { return new CSettingRequirementConditionCombination(m_settingsManager); }
+ CBooleanLogicValue* newValue() override { return new CSettingRequirementCondition(m_settingsManager); }
+};
+
+class CSettingRequirement : public CSettingCondition
+{
+public:
+ explicit CSettingRequirement(CSettingsManager *settingsManager = nullptr);
+ ~CSettingRequirement() override = default;
+};
diff --git a/xbmc/settings/lib/SettingSection.cpp b/xbmc/settings/lib/SettingSection.cpp
new file mode 100644
index 0000000..4f1b564
--- /dev/null
+++ b/xbmc/settings/lib/SettingSection.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2013-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 "SettingSection.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "SettingsManager.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <algorithm>
+
+template<class T>
+void addISetting(const TiXmlNode* node, const T& item, std::vector<T>& items, bool toBegin = false)
+{
+ if (node != nullptr)
+ {
+ auto element = node->ToElement();
+ if (element != nullptr)
+ {
+ // check if there is a "before" or "after" attribute to place the setting at a specific position
+ int position = -1; // -1 => end, 0 => before, 1 => after
+ auto positionId = element->Attribute(SETTING_XML_ATTR_BEFORE);
+ if (positionId != nullptr && strlen(positionId) > 0)
+ position = 0;
+ else if ((positionId = element->Attribute(SETTING_XML_ATTR_AFTER)) != nullptr && strlen(positionId) > 0)
+ position = 1;
+
+ if (positionId != nullptr && strlen(positionId) > 0 && position >= 0)
+ {
+ for (typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ if (!StringUtils::EqualsNoCase((*it)->GetId(), positionId))
+ continue;
+
+ typename std::vector<T>::iterator positionIt = it;
+ if (position == 1)
+ ++positionIt;
+
+ items.insert(positionIt, item);
+ return;
+ }
+ }
+ }
+ }
+
+ if (!toBegin)
+ items.emplace_back(item);
+ else
+ items.insert(items.begin(), item);
+}
+
+Logger CSettingGroup::s_logger;
+
+CSettingGroup::CSettingGroup(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingGroup");
+}
+
+bool CSettingGroup::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto controlElement = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
+ if (controlElement != nullptr)
+ {
+ auto controlType = controlElement->Attribute(SETTING_XML_ATTR_TYPE);
+ if (controlType == nullptr || strlen(controlType) <= 0)
+ {
+ s_logger->error("unable to read control type");
+ return false;
+ }
+
+ m_control = m_settingsManager->CreateControl(controlType);
+ if (m_control == nullptr)
+ {
+ s_logger->error("unable to create new control \"{}\"", controlType);
+ return false;
+ }
+
+ if (!m_control->Deserialize(controlElement))
+ {
+ s_logger->warn("unable to read control \"{}\"", controlType);
+ m_control.reset();
+ }
+ }
+
+ auto settingElement = node->FirstChildElement(SETTING_XML_ELM_SETTING);
+ while (settingElement != nullptr)
+ {
+ std::string settingId;
+ bool isReference;
+ if (CSetting::DeserializeIdentification(settingElement, settingId, isReference))
+ {
+ auto settingIt = std::find_if(m_settings.begin(), m_settings.end(),
+ [&settingId](const SettingPtr& setting)
+ {
+ return setting->GetId() == settingId;
+ });
+
+ SettingPtr setting;
+ if (settingIt != m_settings.end())
+ setting = *settingIt;
+
+ update = (setting != nullptr);
+ if (!update)
+ {
+ auto settingType = settingElement->Attribute(SETTING_XML_ATTR_TYPE);
+ if (settingType == nullptr || strlen(settingType) <= 0)
+ {
+ s_logger->error("unable to read setting type of \"{}\"", settingId);
+ return false;
+ }
+
+ setting = m_settingsManager->CreateSetting(settingType, settingId, m_settingsManager);
+ if (setting == nullptr)
+ s_logger->error("unknown setting type \"{}\" of \"{}\"", settingType, settingId);
+ }
+
+ if (setting == nullptr)
+ s_logger->error("unable to create new setting \"{}\"", settingId);
+ else
+ {
+ if (!setting->Deserialize(settingElement, update))
+ s_logger->warn("unable to read setting \"{}\"", settingId);
+ else
+ {
+ // if the setting is a reference turn it into one
+ if (isReference)
+ setting->MakeReference();
+
+ if (!update)
+ addISetting(settingElement, setting, m_settings);
+ }
+ }
+ }
+
+ settingElement = settingElement->NextSiblingElement(SETTING_XML_ELM_SETTING);
+ }
+
+ return true;
+}
+
+SettingList CSettingGroup::GetSettings(SettingLevel level) const
+{
+ SettingList settings;
+ for (const auto& setting : m_settings)
+ {
+ if (setting->GetLevel() <= level && setting->MeetsRequirements())
+ settings.push_back(setting);
+ }
+
+ return settings;
+}
+
+void CSettingGroup::AddSetting(const SettingPtr& setting)
+{
+ addISetting(nullptr, setting, m_settings);
+}
+
+void CSettingGroup::AddSettings(const SettingList &settings)
+{
+ for (const auto& setting : settings)
+ addISetting(nullptr, setting, m_settings);
+}
+
+bool CSettingGroup::ReplaceSetting(const std::shared_ptr<const CSetting>& currentSetting,
+ const std::shared_ptr<CSetting>& newSetting)
+{
+ for (auto itSetting = m_settings.begin(); itSetting != m_settings.end(); ++itSetting)
+ {
+ if (*itSetting == currentSetting)
+ {
+ if (newSetting == nullptr)
+ m_settings.erase(itSetting);
+ else
+ *itSetting = newSetting;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Logger CSettingCategory::s_logger;
+
+CSettingCategory::CSettingCategory(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager),
+ m_accessCondition(settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingCategory");
+}
+
+bool CSettingCategory::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto accessNode = node->FirstChild(SETTING_XML_ELM_ACCESS);
+ if (accessNode != nullptr && !m_accessCondition.Deserialize(accessNode))
+ return false;
+
+ auto groupNode = node->FirstChild(SETTING_XML_ELM_GROUP);
+ while (groupNode != nullptr)
+ {
+ std::string groupId;
+ if (CSettingGroup::DeserializeIdentification(groupNode, groupId))
+ {
+ auto groupIt = std::find_if(m_groups.begin(), m_groups.end(),
+ [&groupId](const SettingGroupPtr& group)
+ {
+ return group->GetId() == groupId;
+ });
+
+ SettingGroupPtr group;
+ if (groupIt != m_groups.end())
+ group = *groupIt;
+
+ update = (group != nullptr);
+ if (!update)
+ group = std::make_shared<CSettingGroup>(groupId, m_settingsManager);
+
+ if (group->Deserialize(groupNode, update))
+ {
+ if (!update)
+ addISetting(groupNode, group, m_groups);
+ }
+ else
+ s_logger->warn("unable to read group \"{}\"", groupId);
+ }
+
+ groupNode = groupNode->NextSibling(SETTING_XML_ELM_GROUP);
+ }
+
+ return true;
+}
+
+SettingGroupList CSettingCategory::GetGroups(SettingLevel level) const
+{
+ SettingGroupList groups;
+ for (const auto& group : m_groups)
+ {
+ if (group->MeetsRequirements() && group->IsVisible() && group->GetSettings(level).size() > 0)
+ groups.push_back(group);
+ }
+
+ return groups;
+}
+
+bool CSettingCategory::CanAccess() const
+{
+ return m_accessCondition.Check();
+}
+
+void CSettingCategory::AddGroup(const SettingGroupPtr& group)
+{
+ addISetting(nullptr, group, m_groups, false);
+}
+
+void CSettingCategory::AddGroupToFront(const SettingGroupPtr& group)
+{
+ addISetting(nullptr, group, m_groups, true);
+}
+
+void CSettingCategory::AddGroups(const SettingGroupList &groups)
+{
+ for (const auto& group : groups)
+ addISetting(nullptr, group, m_groups);
+}
+
+Logger CSettingSection::s_logger;
+
+CSettingSection::CSettingSection(const std::string& id,
+ CSettingsManager* settingsManager /* = nullptr */)
+ : ISetting(id, settingsManager)
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingSection");
+}
+
+bool CSettingSection::Deserialize(const TiXmlNode *node, bool update /* = false */)
+{
+ // handle <visible> conditions
+ if (!ISetting::Deserialize(node, update))
+ return false;
+
+ auto categoryNode = node->FirstChild(SETTING_XML_ELM_CATEGORY);
+ while (categoryNode != nullptr)
+ {
+ std::string categoryId;
+ if (CSettingCategory::DeserializeIdentification(categoryNode, categoryId))
+ {
+ auto categoryIt = std::find_if(m_categories.begin(), m_categories.end(),
+ [&categoryId](const SettingCategoryPtr& category)
+ {
+ return category->GetId() == categoryId;
+ });
+
+ SettingCategoryPtr category;
+ if (categoryIt != m_categories.end())
+ category = *categoryIt;
+
+ update = (category != nullptr);
+ if (!update)
+ category = std::make_shared<CSettingCategory>(categoryId, m_settingsManager);
+
+ if (category->Deserialize(categoryNode, update))
+ {
+ if (!update)
+ addISetting(categoryNode, category, m_categories);
+ }
+ else
+ s_logger->warn("unable to read category \"{}\"", categoryId);
+ }
+
+ categoryNode = categoryNode->NextSibling(SETTING_XML_ELM_CATEGORY);
+ }
+
+ return true;
+}
+
+SettingCategoryList CSettingSection::GetCategories(SettingLevel level) const
+{
+ SettingCategoryList categories;
+ for (const auto& category : m_categories)
+ {
+ if (category->MeetsRequirements() && category->IsVisible() && category->GetGroups(level).size() > 0)
+ categories.push_back(category);
+ }
+
+ return categories;
+}
+
+void CSettingSection::AddCategory(const SettingCategoryPtr& category)
+{
+ addISetting(nullptr, category, m_categories);
+}
+
+void CSettingSection::AddCategories(const SettingCategoryList &categories)
+{
+ for (const auto& category : categories)
+ addISetting(nullptr, category, m_categories);
+}
diff --git a/xbmc/settings/lib/SettingSection.h b/xbmc/settings/lib/SettingSection.h
new file mode 100644
index 0000000..f8e06d6
--- /dev/null
+++ b/xbmc/settings/lib/SettingSection.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "ISetting.h"
+#include "Setting.h"
+#include "SettingCategoryAccess.h"
+#include "utils/logtypes.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+class CSettingsManager;
+
+/*!
+ \ingroup settings
+ \brief Group of settings being part of a category
+ \sa CSettingCategory
+ \sa CSetting
+ */
+class CSettingGroup : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting group with the given identifier.
+
+ \param id Identifier of the setting group
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingGroup(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingGroup() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of settings belonging to the setting group.
+
+ \return Full list of settings belonging to the setting group
+ */
+ const SettingList& GetSettings() const { return m_settings; }
+ /*!
+ \brief Gets the list of settings assigned to the given setting level (or
+ below) and that meet the requirements conditions belonging to the setting
+ group.
+
+ \param level Level the settings should be assigned to
+ \return List of settings belonging to the setting group
+ */
+ SettingList GetSettings(SettingLevel level) const;
+
+ void AddSetting(const std::shared_ptr<CSetting>& setting);
+ void AddSettings(const SettingList &settings);
+
+ bool ReplaceSetting(const std::shared_ptr<const CSetting>& currentSetting,
+ const std::shared_ptr<CSetting>& newSetting);
+
+ std::shared_ptr<const ISettingControl> GetControl() const { return m_control; }
+ std::shared_ptr<ISettingControl> GetControl() { return m_control; }
+ void SetControl(std::shared_ptr<ISettingControl> control) { m_control = std::move(control); }
+
+private:
+ SettingList m_settings;
+ std::shared_ptr<ISettingControl> m_control;
+
+ static Logger s_logger;
+};
+
+using SettingGroupPtr = std::shared_ptr<CSettingGroup>;
+using SettingGroupList = std::vector<SettingGroupPtr>;
+
+/*!
+ \ingroup settings
+ \brief Category of groups of settings being part of a section
+ \sa CSettingSection
+ \sa CSettingGroup
+ */
+class CSettingCategory : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting category with the given identifier.
+
+ \param id Identifier of the setting category
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingCategory(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingCategory() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of setting groups belonging to the setting
+ category.
+
+ \return Full list of setting groups belonging to the setting category
+ */
+ const SettingGroupList& GetGroups() const { return m_groups; }
+ /*!
+ \brief Gets the list of setting groups belonging to the setting category
+ that contain settings assigned to the given setting level (or below) and
+ that meet the requirements and visibility conditions.
+
+ \param level Level the settings should be assigned to
+ \return List of setting groups belonging to the setting category
+ */
+ SettingGroupList GetGroups(SettingLevel level) const;
+
+ /*!
+ \brief Whether the setting category can be accessed or not.
+
+ \return True if the setting category can be accessed, false otherwise
+ */
+ bool CanAccess() const;
+
+ void AddGroup(const SettingGroupPtr& group);
+ void AddGroupToFront(const SettingGroupPtr& group);
+ void AddGroups(const SettingGroupList &groups);
+
+private:
+ SettingGroupList m_groups;
+ CSettingCategoryAccess m_accessCondition;
+
+ static Logger s_logger;
+};
+
+using SettingCategoryPtr = std::shared_ptr<CSettingCategory>;
+using SettingCategoryList = std::vector<SettingCategoryPtr>;
+
+/*!
+ \ingroup settings
+ \brief Section of setting categories
+ \sa CSettings
+ \sa CSettingCategory
+ */
+class CSettingSection : public ISetting
+{
+public:
+ /*!
+ \brief Creates a new setting section with the given identifier.
+
+ \param id Identifier of the setting section
+ \param settingsManager Reference to the settings manager
+ */
+ CSettingSection(const std::string &id, CSettingsManager *settingsManager = nullptr);
+ ~CSettingSection() override = default;
+
+ // implementation of ISetting
+ bool Deserialize(const TiXmlNode *node, bool update = false) override;
+
+ /*!
+ \brief Gets the full list of setting categories belonging to the setting
+ section.
+
+ \return Full list of setting categories belonging to the setting section
+ */
+ const SettingCategoryList& GetCategories() const { return m_categories; }
+ /*!
+ \brief Gets the list of setting categories belonging to the setting section
+ that contain settings assigned to the given setting level (or below) and
+ that meet the requirements and visibility conditions.
+
+ \param level Level the settings should be assigned to
+ \return List of setting categories belonging to the setting section
+ */
+ SettingCategoryList GetCategories(SettingLevel level) const;
+
+ void AddCategory(const SettingCategoryPtr& category);
+ void AddCategories(const SettingCategoryList &categories);
+
+private:
+ SettingCategoryList m_categories;
+
+ static Logger s_logger;
+};
+
+using SettingSectionPtr = std::shared_ptr<CSettingSection>;
+using SettingSectionList = std::vector<SettingSectionPtr>;
diff --git a/xbmc/settings/lib/SettingType.h b/xbmc/settings/lib/SettingType.h
new file mode 100644
index 0000000..56838ab
--- /dev/null
+++ b/xbmc/settings/lib/SettingType.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2017-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.
+ */
+
+#pragma once
+
+/*!
+ \ingroup settings
+ \brief Basic setting types available in the settings system.
+ */
+enum class SettingType {
+ Unknown = 0,
+ Boolean,
+ Integer,
+ Number,
+ String,
+ Action,
+ List
+};
diff --git a/xbmc/settings/lib/SettingUpdate.cpp b/xbmc/settings/lib/SettingUpdate.cpp
new file mode 100644
index 0000000..0201aab
--- /dev/null
+++ b/xbmc/settings/lib/SettingUpdate.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013-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 "SettingUpdate.h"
+
+#include "ServiceBroker.h"
+#include "SettingDefinitions.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+Logger CSettingUpdate::s_logger;
+
+CSettingUpdate::CSettingUpdate()
+{
+ if (s_logger == nullptr)
+ s_logger = CServiceBroker::GetLogging().GetLogger("CSettingUpdate");
+}
+
+bool CSettingUpdate::Deserialize(const TiXmlNode *node)
+{
+ if (node == nullptr)
+ return false;
+
+ auto elem = node->ToElement();
+ if (elem == nullptr)
+ return false;
+
+ auto strType = elem->Attribute(SETTING_XML_ATTR_TYPE);
+ if (strType == nullptr || strlen(strType) <= 0 || !setType(strType))
+ {
+ s_logger->warn("missing or unknown update type definition");
+ return false;
+ }
+
+ if (m_type == SettingUpdateType::Rename)
+ {
+ if (node->FirstChild() == nullptr || node->FirstChild()->Type() != TiXmlNode::TINYXML_TEXT)
+ {
+ s_logger->warn("missing or invalid setting id for rename update definition");
+ return false;
+ }
+
+ m_value = node->FirstChild()->ValueStr();
+ }
+
+ return true;
+}
+
+bool CSettingUpdate::setType(const std::string &type)
+{
+ if (StringUtils::EqualsNoCase(type, "change"))
+ m_type = SettingUpdateType::Change;
+ else if (StringUtils::EqualsNoCase(type, "rename"))
+ m_type = SettingUpdateType::Rename;
+ else
+ return false;
+
+ return true;
+}
diff --git a/xbmc/settings/lib/SettingUpdate.h b/xbmc/settings/lib/SettingUpdate.h
new file mode 100644
index 0000000..65c3e86
--- /dev/null
+++ b/xbmc/settings/lib/SettingUpdate.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "utils/logtypes.h"
+
+#include <string>
+
+class TiXmlNode;
+
+enum class SettingUpdateType {
+ Unknown = 0,
+ Rename,
+ Change
+};
+
+class CSettingUpdate
+{
+public:
+ CSettingUpdate();
+ virtual ~CSettingUpdate() = default;
+
+ inline bool operator<(const CSettingUpdate& rhs) const
+ {
+ return m_type < rhs.m_type && m_value < rhs.m_value;
+ }
+
+ virtual bool Deserialize(const TiXmlNode *node);
+
+ SettingUpdateType GetType() const { return m_type; }
+ const std::string& GetValue() const { return m_value; }
+
+private:
+ bool setType(const std::string &type);
+
+ SettingUpdateType m_type = SettingUpdateType::Unknown;
+ std::string m_value;
+
+ static Logger s_logger;
+};
diff --git a/xbmc/settings/lib/SettingsManager.cpp b/xbmc/settings/lib/SettingsManager.cpp
new file mode 100644
index 0000000..9ff7d61
--- /dev/null
+++ b/xbmc/settings/lib/SettingsManager.cpp
@@ -0,0 +1,1424 @@
+/*
+ * Copyright (C) 2013-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 "SettingsManager.h"
+
+#include "ServiceBroker.h"
+#include "Setting.h"
+#include "SettingDefinitions.h"
+#include "SettingSection.h"
+#include "utils/StringUtils.h"
+#include "utils/XBMCTinyXML.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <map>
+#include <mutex>
+#include <shared_mutex>
+#include <unordered_set>
+#include <utility>
+
+const uint32_t CSettingsManager::Version = 2;
+const uint32_t CSettingsManager::MinimumSupportedVersion = 0;
+
+bool ParseSettingIdentifier(const std::string& settingId, std::string& categoryTag, std::string& settingTag)
+{
+ static const std::string Separator = ".";
+
+ if (settingId.empty())
+ return false;
+
+ auto parts = StringUtils::Split(settingId, Separator);
+ if (parts.size() < 1 || parts.at(0).empty())
+ return false;
+
+ if (parts.size() == 1)
+ {
+ settingTag = parts.at(0);
+ return true;
+ }
+
+ // get the category tag and remove it from the parts
+ categoryTag = parts.at(0);
+ parts.erase(parts.begin());
+
+ // put together the setting tag
+ settingTag = StringUtils::Join(parts, Separator);
+
+ return true;
+}
+
+CSettingsManager::CSettingsManager()
+ : m_logger(CServiceBroker::GetLogging().GetLogger("CSettingsManager"))
+{
+}
+
+CSettingsManager::~CSettingsManager()
+{
+ // first clear all registered settings handler and subsettings
+ // implementations because we can't be sure that they are still valid
+ m_settingsHandlers.clear();
+ m_settingCreators.clear();
+ m_settingControlCreators.clear();
+
+ Clear();
+}
+
+uint32_t CSettingsManager::ParseVersion(const TiXmlElement* root) const
+{
+ // try to get and check the version
+ uint32_t version = 0;
+ root->QueryUnsignedAttribute(SETTING_XML_ROOT_VERSION, &version);
+
+ return version;
+}
+
+bool CSettingsManager::Initialize(const TiXmlElement *root)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (m_initialized || root == nullptr)
+ return false;
+
+ if (!StringUtils::EqualsNoCase(root->ValueStr(), SETTING_XML_ROOT))
+ {
+ m_logger->error("error reading settings definition: doesn't contain <" SETTING_XML_ROOT
+ "> tag");
+ return false;
+ }
+
+ // try to get and check the version
+ uint32_t version = ParseVersion(root);
+ if (version == 0)
+ m_logger->warn("missing " SETTING_XML_ROOT_VERSION " attribute", SETTING_XML_ROOT_VERSION);
+
+ if (MinimumSupportedVersion >= version+1)
+ {
+ m_logger->error("unable to read setting definitions from version {} (minimum version: {})",
+ version, MinimumSupportedVersion);
+ return false;
+ }
+ if (version > Version)
+ {
+ m_logger->error("unable to read setting definitions from version {} (current version: {})",
+ version, Version);
+ return false;
+ }
+
+ auto sectionNode = root->FirstChild(SETTING_XML_ELM_SECTION);
+ while (sectionNode != nullptr)
+ {
+ std::string sectionId;
+ if (CSettingSection::DeserializeIdentification(sectionNode, sectionId))
+ {
+ SettingSectionPtr section = nullptr;
+ auto itSection = m_sections.find(sectionId);
+ bool update = (itSection != m_sections.end());
+ if (!update)
+ section = std::make_shared<CSettingSection>(sectionId, this);
+ else
+ section = itSection->second;
+
+ if (section->Deserialize(sectionNode, update))
+ AddSection(section);
+ else
+ {
+ m_logger->warn("unable to read section \"{}\"", sectionId);
+ }
+ }
+
+ sectionNode = sectionNode->NextSibling(SETTING_XML_ELM_SECTION);
+ }
+
+ return true;
+}
+
+bool CSettingsManager::Load(const TiXmlElement *root, bool &updated, bool triggerEvents /* = true */, std::map<std::string, SettingPtr> *loadedSettings /* = nullptr */)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (m_loaded || root == nullptr)
+ return false;
+
+ if (triggerEvents && !OnSettingsLoading())
+ return false;
+
+ // try to get and check the version
+ uint32_t version = ParseVersion(root);
+ if (version == 0)
+ m_logger->warn("missing {} attribute", SETTING_XML_ROOT_VERSION);
+
+ if (MinimumSupportedVersion >= version+1)
+ {
+ m_logger->error("unable to read setting values from version {} (minimum version: {})", version,
+ MinimumSupportedVersion);
+ return false;
+ }
+ if (version > Version)
+ {
+ m_logger->error("unable to read setting values from version {} (current version: {})", version,
+ Version);
+ return false;
+ }
+
+ if (!Deserialize(root, updated, loadedSettings))
+ return false;
+
+ if (triggerEvents)
+ OnSettingsLoaded();
+
+ return true;
+}
+
+bool CSettingsManager::Save(
+ const ISettingsValueSerializer* serializer, std::string& serializedValues) const
+{
+ if (serializer == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ std::shared_lock<CSharedSection> settingsLock(m_settingsCritical);
+ if (!m_initialized)
+ return false;
+
+ if (!OnSettingsSaving())
+ return false;
+
+ serializedValues = serializer->SerializeValues(this);
+
+ OnSettingsSaved();
+
+ return true;
+}
+
+void CSettingsManager::Unload()
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded)
+ return;
+
+ // needs to be set before calling CSetting::Reset() to avoid calls to
+ // OnSettingChanging() and OnSettingChanged()
+ m_loaded = false;
+
+ for (auto& setting : m_settings)
+ setting.second.setting->Reset();
+
+ OnSettingsUnloaded();
+}
+
+void CSettingsManager::Clear()
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ Unload();
+
+ m_settings.clear();
+ m_sections.clear();
+
+ OnSettingsCleared();
+
+ m_initialized = false;
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode *node, const std::string &settingId)
+{
+ bool updated = false;
+ return LoadSetting(node, settingId, updated);
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode *node, const std::string &settingId, bool &updated)
+{
+ updated = false;
+
+ if (node == nullptr)
+ return false;
+
+ auto setting = GetSetting(settingId);
+ if (setting == nullptr)
+ return false;
+
+ return LoadSetting(node, setting, updated);
+}
+
+void CSettingsManager::SetInitialized()
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (m_initialized)
+ return;
+
+ m_initialized = true;
+
+ // resolve any reference settings
+ for (const auto& section : m_sections)
+ ResolveReferenceSettings(section.second);
+
+ // remove any incomplete settings
+ CleanupIncompleteSettings();
+
+ // figure out all the dependencies between settings
+ for (const auto& setting : m_settings)
+ ResolveSettingDependencies(setting.second);
+}
+
+void CSettingsManager::AddSection(const SettingSectionPtr& section)
+{
+ if (section == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+
+ section->CheckRequirements();
+ m_sections[section->GetId()] = section;
+
+ // get all settings and add them to the settings map
+ std::set<SettingPtr> newSettings;
+ for (const auto& category : section->GetCategories())
+ {
+ category->CheckRequirements();
+ for (auto& group : category->GetGroups())
+ {
+ group->CheckRequirements();
+ for (const auto& setting : group->GetSettings())
+ {
+ AddSetting(setting);
+
+ newSettings.insert(setting);
+ }
+ }
+ }
+
+ if (m_initialized && !newSettings.empty())
+ {
+ // resolve any reference settings in the new section
+ ResolveReferenceSettings(section);
+
+ // cleanup any newly added incomplete settings
+ CleanupIncompleteSettings();
+
+ // resolve any dependencies for the newly added settings
+ for (const auto& setting : newSettings)
+ ResolveSettingDependencies(setting);
+ }
+}
+
+bool CSettingsManager::AddSetting(const std::shared_ptr<CSetting>& setting,
+ const std::shared_ptr<CSettingSection>& section,
+ const std::shared_ptr<CSettingCategory>& category,
+ const std::shared_ptr<CSettingGroup>& group)
+{
+ if (setting == nullptr || section == nullptr || category == nullptr || group == nullptr)
+ return false;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ std::unique_lock<CSharedSection> settingsLock(m_settingsCritical);
+
+ // check if a setting with the given ID already exists
+ if (FindSetting(setting->GetId()) != m_settings.end())
+ return false;
+
+ // if the given setting has not been added to the group yet, do it now
+ auto settings = group->GetSettings();
+ if (std::find(settings.begin(), settings.end(), setting) == settings.end())
+ group->AddSetting(setting);
+
+ // if the given group has not been added to the category yet, do it now
+ auto groups = category->GetGroups();
+ if (std::find(groups.begin(), groups.end(), group) == groups.end())
+ category->AddGroup(group);
+
+ // if the given category has not been added to the section yet, do it now
+ auto categories = section->GetCategories();
+ if (std::find(categories.begin(), categories.end(), category) == categories.end())
+ section->AddCategory(category);
+
+ // check if the given section exists and matches
+ auto sectionPtr = GetSection(section->GetId());
+ if (sectionPtr != nullptr && sectionPtr != section)
+ return false;
+
+ // if the section doesn't exist yet, add it
+ if (sectionPtr == nullptr)
+ AddSection(section);
+ else
+ {
+ // add the setting
+ AddSetting(setting);
+
+ if (m_initialized)
+ {
+ // cleanup any newly added incomplete setting
+ CleanupIncompleteSettings();
+
+ // resolve any dependencies for the newly added setting
+ ResolveSettingDependencies(setting);
+ }
+ }
+
+ return true;
+}
+
+void CSettingsManager::RegisterCallback(ISettingCallback *callback, const std::set<std::string> &settingList)
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ if (callback == nullptr)
+ return;
+
+ for (const auto& setting : settingList)
+ {
+ auto itSetting = FindSetting(setting);
+ if (itSetting == m_settings.end())
+ {
+ if (m_initialized)
+ continue;
+
+ Setting tmpSetting = {};
+ std::pair<SettingMap::iterator, bool> tmpIt = InsertSetting(setting, tmpSetting);
+ itSetting = tmpIt.first;
+ }
+
+ itSetting->second.callbacks.insert(callback);
+ }
+}
+
+void CSettingsManager::UnregisterCallback(ISettingCallback *callback)
+{
+ std::unique_lock<CSharedSection> lock(m_settingsCritical);
+ for (auto& setting : m_settings)
+ setting.second.callbacks.erase(callback);
+}
+
+void CSettingsManager::RegisterSettingType(const std::string &settingType, ISettingCreator *settingCreator)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (settingType.empty() || settingCreator == nullptr)
+ return;
+
+ auto creatorIt = m_settingCreators.find(settingType);
+ if (creatorIt == m_settingCreators.end())
+ m_settingCreators.insert(std::make_pair(settingType, settingCreator));
+}
+
+void CSettingsManager::RegisterSettingControl(const std::string &controlType, ISettingControlCreator *settingControlCreator)
+{
+ if (controlType.empty() || settingControlCreator == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto creatorIt = m_settingControlCreators.find(controlType);
+ if (creatorIt == m_settingControlCreators.end())
+ m_settingControlCreators.insert(std::make_pair(controlType, settingControlCreator));
+}
+
+void CSettingsManager::RegisterSettingsHandler(ISettingsHandler *settingsHandler, bool bFront /* = false */)
+{
+ if (settingsHandler == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (find(m_settingsHandlers.begin(), m_settingsHandlers.end(), settingsHandler) == m_settingsHandlers.end())
+ {
+ if (bFront)
+ m_settingsHandlers.insert(m_settingsHandlers.begin(), settingsHandler);
+ else
+ m_settingsHandlers.emplace_back(settingsHandler);
+ }
+}
+
+void CSettingsManager::UnregisterSettingsHandler(ISettingsHandler *settingsHandler)
+{
+ if (settingsHandler == nullptr)
+ return;
+
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto it = std::find(m_settingsHandlers.begin(), m_settingsHandlers.end(), settingsHandler);
+ if (it != m_settingsHandlers.end())
+ m_settingsHandlers.erase(it);
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, IntegerSettingOptionsFiller optionsFiller)
+{
+ if (identifier.empty() || optionsFiller == nullptr)
+ return;
+
+ RegisterSettingOptionsFiller(identifier, reinterpret_cast<void*>(optionsFiller), SettingOptionsFillerType::Integer);
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, StringSettingOptionsFiller optionsFiller)
+{
+ if (identifier.empty() || optionsFiller == nullptr)
+ return;
+
+ RegisterSettingOptionsFiller(identifier, reinterpret_cast<void*>(optionsFiller), SettingOptionsFillerType::String);
+}
+
+void CSettingsManager::UnregisterSettingOptionsFiller(const std::string &identifier)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ m_optionsFillers.erase(identifier);
+}
+
+void* CSettingsManager::GetSettingOptionsFiller(const SettingConstPtr& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (setting == nullptr)
+ return nullptr;
+
+ // get the option filler's identifier
+ std::string filler;
+ if (setting->GetType() == SettingType::Integer)
+ filler = std::static_pointer_cast<const CSettingInt>(setting)->GetOptionsFillerName();
+ else if (setting->GetType() == SettingType::String)
+ filler = std::static_pointer_cast<const CSettingString>(setting)->GetOptionsFillerName();
+
+ if (filler.empty())
+ return nullptr;
+
+ // check if such an option filler is known
+ auto fillerIt = m_optionsFillers.find(filler);
+ if (fillerIt == m_optionsFillers.end())
+ return nullptr;
+
+ if (fillerIt->second.filler == nullptr)
+ return nullptr;
+
+ // make sure the option filler's type matches the setting's type
+ switch (fillerIt->second.type)
+ {
+ case SettingOptionsFillerType::Integer:
+ {
+ if (setting->GetType() != SettingType::Integer)
+ return nullptr;
+
+ break;
+ }
+
+ case SettingOptionsFillerType::String:
+ {
+ if (setting->GetType() != SettingType::String)
+ return nullptr;
+
+ break;
+ }
+
+ default:
+ return nullptr;
+ }
+
+ return fillerIt->second.filler;
+}
+
+bool CSettingsManager::HasSettings() const
+{
+ return !m_settings.empty();
+}
+
+SettingPtr CSettingsManager::GetSetting(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (id.empty())
+ return nullptr;
+
+ auto setting = FindSetting(id);
+ if (setting != m_settings.end())
+ {
+ if (setting->second.setting->IsReference())
+ return GetSetting(setting->second.setting->GetReferencedId());
+ return setting->second.setting;
+ }
+
+ m_logger->debug("requested setting ({}) was not found.", id);
+ return nullptr;
+}
+
+SettingSectionList CSettingsManager::GetSections() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ SettingSectionList sections;
+ for (const auto& section : m_sections)
+ sections.push_back(section.second);
+
+ return sections;
+}
+
+SettingSectionPtr CSettingsManager::GetSection(std::string section) const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ if (section.empty())
+ return nullptr;
+
+ StringUtils::ToLower(section);
+
+ auto sectionIt = m_sections.find(section);
+ if (sectionIt != m_sections.end())
+ return sectionIt->second;
+
+ m_logger->debug("requested setting section ({}) was not found.", section);
+ return nullptr;
+}
+
+SettingDependencyMap CSettingsManager::GetDependencies(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ auto setting = FindSetting(id);
+ if (setting == m_settings.end())
+ return SettingDependencyMap();
+
+ return setting->second.dependencies;
+}
+
+SettingDependencyMap CSettingsManager::GetDependencies(const SettingConstPtr& setting) const
+{
+ if (setting == nullptr)
+ return SettingDependencyMap();
+
+ return GetDependencies(setting->GetId());
+}
+
+bool CSettingsManager::GetBool(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return std::static_pointer_cast<CSettingBool>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetBool(const std::string &id, bool value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return std::static_pointer_cast<CSettingBool>(setting)->SetValue(value);
+}
+
+bool CSettingsManager::ToggleBool(const std::string &id)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Boolean)
+ return false;
+
+ return SetBool(id, !std::static_pointer_cast<CSettingBool>(setting)->GetValue());
+}
+
+int CSettingsManager::GetInt(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Integer)
+ return 0;
+
+ return std::static_pointer_cast<CSettingInt>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetInt(const std::string &id, int value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Integer)
+ return false;
+
+ return std::static_pointer_cast<CSettingInt>(setting)->SetValue(value);
+}
+
+double CSettingsManager::GetNumber(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Number)
+ return 0.0;
+
+ return std::static_pointer_cast<CSettingNumber>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetNumber(const std::string &id, double value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::Number)
+ return false;
+
+ return std::static_pointer_cast<CSettingNumber>(setting)->SetValue(value);
+}
+
+std::string CSettingsManager::GetString(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::String)
+ return "";
+
+ return std::static_pointer_cast<CSettingString>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetString(const std::string &id, const std::string &value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::String)
+ return false;
+
+ return std::static_pointer_cast<CSettingString>(setting)->SetValue(value);
+}
+
+std::vector< std::shared_ptr<CSetting> > CSettingsManager::GetList(const std::string &id) const
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return std::vector< std::shared_ptr<CSetting> >();
+
+ return std::static_pointer_cast<CSettingList>(setting)->GetValue();
+}
+
+bool CSettingsManager::SetList(const std::string &id, const std::vector< std::shared_ptr<CSetting> > &value)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr || setting->GetType() != SettingType::List)
+ return false;
+
+ return std::static_pointer_cast<CSettingList>(setting)->SetValue(value);
+}
+
+bool CSettingsManager::SetDefault(const std::string &id)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ SettingPtr setting = GetSetting(id);
+ if (setting == nullptr)
+ return false;
+
+ setting->Reset();
+ return true;
+}
+
+void CSettingsManager::SetDefaults()
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ for (auto& setting : m_settings)
+ setting.second.setting->Reset();
+}
+
+void CSettingsManager::AddCondition(const std::string &condition)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (condition.empty())
+ return;
+
+ m_conditions.AddCondition(condition);
+}
+
+void CSettingsManager::AddDynamicCondition(const std::string &identifier, SettingConditionCheck condition, void *data /*= nullptr*/)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (identifier.empty() || condition == nullptr)
+ return;
+
+ m_conditions.AddDynamicCondition(identifier, condition, data);
+}
+
+void CSettingsManager::RemoveDynamicCondition(const std::string &identifier)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ if (identifier.empty())
+ return;
+
+ m_conditions.RemoveDynamicCondition(identifier);
+}
+
+bool CSettingsManager::Serialize(TiXmlNode *parent) const
+{
+ if (parent == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+
+ for (const auto& setting : m_settings)
+ {
+ if (setting.second.setting->IsReference() ||
+ setting.second.setting->GetType() == SettingType::Action)
+ continue;
+
+ TiXmlElement settingElement(SETTING_XML_ELM_SETTING);
+ settingElement.SetAttribute(SETTING_XML_ATTR_ID, setting.second.setting->GetId());
+
+ // add the default attribute
+ if (setting.second.setting->IsDefault())
+ settingElement.SetAttribute(SETTING_XML_ELM_DEFAULT, "true");
+
+ // add the value
+ TiXmlText value(setting.second.setting->ToString());
+ settingElement.InsertEndChild(value);
+
+ if (parent->InsertEndChild(settingElement) == nullptr)
+ {
+ m_logger->warn("unable to write <" SETTING_XML_ELM_SETTING " id=\"{}\"> tag",
+ setting.second.setting->GetId());
+ continue;
+ }
+ }
+
+ return true;
+}
+
+bool CSettingsManager::Deserialize(const TiXmlNode *node, bool &updated, std::map<std::string, SettingPtr> *loadedSettings /* = nullptr */)
+{
+ updated = false;
+
+ if (node == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+
+ // TODO: ideally this would be done by going through all <setting> elements
+ // in node but as long as we have to support the v1- format that's not possible
+ for (auto& setting : m_settings)
+ {
+ bool settingUpdated = false;
+ if (LoadSetting(node, setting.second.setting, settingUpdated))
+ {
+ updated |= settingUpdated;
+ if (loadedSettings != nullptr)
+ loadedSettings->insert(make_pair(setting.first, setting.second.setting));
+ }
+ }
+
+ return true;
+}
+
+bool CSettingsManager::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return false;
+
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded)
+ return true;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return false;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ {
+ if (!callback->OnSettingChanging(setting))
+ return false;
+ }
+
+ // if this is a reference setting apply the same change to the referenced setting
+ if (setting->IsReference())
+ {
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ auto referencedSettingIt = FindSetting(setting->GetReferencedId());
+ if (referencedSettingIt != m_settings.end())
+ {
+ Setting referencedSettingData = referencedSettingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ referencedSettingData.setting->FromString(setting->ToString());
+ }
+ }
+ else if (!settingData.references.empty())
+ {
+ // if the changed setting is referenced by other settings apply the same change to the referencing settings
+ std::unordered_set<SettingPtr> referenceSettings;
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ for (const auto& reference : settingData.references)
+ {
+ auto referenceSettingIt = FindSetting(reference);
+ if (referenceSettingIt != m_settings.end())
+ referenceSettings.insert(referenceSettingIt->second.setting);
+ }
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& referenceSetting : referenceSettings)
+ referenceSetting->FromString(setting->ToString());
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingChanged(setting);
+
+ // now handle any settings which depend on the changed setting
+ auto dependencies = GetDependencies(setting);
+ for (const auto& deps : dependencies)
+ {
+ for (const auto& dep : deps.second)
+ UpdateSettingByDependency(deps.first, dep);
+ }
+}
+
+void CSettingsManager::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingAction(setting);
+}
+
+bool CSettingsManager::OnSettingUpdate(const SettingPtr& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (setting == nullptr)
+ return false;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return false;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ bool ret = false;
+ for (auto& callback : settingData.callbacks)
+ ret |= callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
+
+ return ret;
+}
+
+void CSettingsManager::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName)
+{
+ std::shared_lock<CSharedSection> lock(m_settingsCritical);
+ if (!m_loaded || setting == nullptr)
+ return;
+
+ auto settingIt = FindSetting(setting->GetId());
+ if (settingIt == m_settings.end())
+ return;
+
+ Setting settingData = settingIt->second;
+ // now that we have a copy of the setting's data, we can leave the lock
+ lock.unlock();
+
+ for (auto& callback : settingData.callbacks)
+ callback->OnSettingPropertyChanged(setting, propertyName);
+
+ // check the changed property and if it may have an influence on the
+ // children of the setting
+ SettingDependencyType dependencyType = SettingDependencyType::Unknown;
+ if (StringUtils::EqualsNoCase(propertyName, "enabled"))
+ dependencyType = SettingDependencyType::Enable;
+ else if (StringUtils::EqualsNoCase(propertyName, "visible"))
+ dependencyType = SettingDependencyType::Visible;
+
+ if (dependencyType != SettingDependencyType::Unknown)
+ {
+ for (const auto& child : settingIt->second.children)
+ UpdateSettingByDependency(child, dependencyType);
+ }
+}
+
+SettingPtr CSettingsManager::CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager /* = nullptr */) const
+{
+ if (StringUtils::EqualsNoCase(settingType, "boolean"))
+ return std::make_shared<CSettingBool>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "integer"))
+ return std::make_shared<CSettingInt>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "number"))
+ return std::make_shared<CSettingNumber>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "string"))
+ return std::make_shared<CSettingString>(settingId, const_cast<CSettingsManager*>(this));
+ else if (StringUtils::EqualsNoCase(settingType, "action"))
+ return std::make_shared<CSettingAction>(settingId, const_cast<CSettingsManager*>(this));
+ else if (settingType.size() > 6 &&
+ StringUtils::StartsWith(settingType, "list[") &&
+ StringUtils::EndsWith(settingType, "]"))
+ {
+ std::string elementType = StringUtils::Mid(settingType, 5, settingType.size() - 6);
+ SettingPtr elementSetting = CreateSetting(elementType, settingId + ".definition", const_cast<CSettingsManager*>(this));
+ if (elementSetting != nullptr)
+ return std::make_shared<CSettingList>(settingId, elementSetting, const_cast<CSettingsManager*>(this));
+ }
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ auto creator = m_settingCreators.find(settingType);
+ if (creator != m_settingCreators.end())
+ return creator->second->CreateSetting(settingType, settingId, const_cast<CSettingsManager*>(this));
+
+ return nullptr;
+}
+
+std::shared_ptr<ISettingControl> CSettingsManager::CreateControl(const std::string &controlType) const
+{
+ if (controlType.empty())
+ return nullptr;
+
+ std::shared_lock<CSharedSection> lock(m_critical);
+ auto creator = m_settingControlCreators.find(controlType);
+ if (creator != m_settingControlCreators.end() && creator->second != nullptr)
+ return creator->second->CreateControl(controlType);
+
+ return nullptr;
+}
+
+bool CSettingsManager::OnSettingsLoading()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ {
+ if (!settingsHandler->OnSettingsLoading())
+ return false;
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingsUnloaded()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsUnloaded();
+}
+
+void CSettingsManager::OnSettingsLoaded()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsLoaded();
+}
+
+bool CSettingsManager::OnSettingsSaving() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ {
+ if (!settingsHandler->OnSettingsSaving())
+ return false;
+ }
+
+ return true;
+}
+
+void CSettingsManager::OnSettingsSaved() const
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsSaved();
+}
+
+void CSettingsManager::OnSettingsCleared()
+{
+ std::shared_lock<CSharedSection> lock(m_critical);
+ for (const auto& settingsHandler : m_settingsHandlers)
+ settingsHandler->OnSettingsCleared();
+}
+
+bool CSettingsManager::LoadSetting(const TiXmlNode* node, const SettingPtr& setting, bool& updated)
+{
+ updated = false;
+
+ if (node == nullptr || setting == nullptr)
+ return false;
+
+ if (setting->GetType() == SettingType::Action)
+ return false;
+
+ auto settingId = setting->GetId();
+ if (setting->IsReference())
+ settingId = setting->GetReferencedId();
+
+ const TiXmlElement* settingElement = nullptr;
+ // try to split the setting identifier into category and subsetting identifier (v1-)
+ std::string categoryTag, settingTag;
+ if (ParseSettingIdentifier(settingId, categoryTag, settingTag))
+ {
+ auto categoryNode = node;
+ if (!categoryTag.empty())
+ categoryNode = node->FirstChild(categoryTag);
+
+ if (categoryNode != nullptr)
+ settingElement = categoryNode->FirstChildElement(settingTag);
+ }
+
+ if (settingElement == nullptr)
+ {
+ // check if the setting is stored using its full setting identifier (v2+)
+ settingElement = node->FirstChildElement(SETTING_XML_ELM_SETTING);
+ while (settingElement != nullptr)
+ {
+ const auto id = settingElement->Attribute(SETTING_XML_ATTR_ID);
+ if (id != nullptr && settingId.compare(id) == 0)
+ break;
+
+ settingElement = settingElement->NextSiblingElement(SETTING_XML_ELM_SETTING);
+ }
+ }
+
+ if (settingElement == nullptr)
+ return false;
+
+ // check if the default="true" attribute is set for the value
+ auto isDefaultAttribute = settingElement->Attribute(SETTING_XML_ELM_DEFAULT);
+ bool isDefault = isDefaultAttribute != nullptr && StringUtils::EqualsNoCase(isDefaultAttribute, "true");
+
+ if (!setting->FromString(settingElement->FirstChild() != nullptr ? settingElement->FirstChild()->ValueStr() : StringUtils::Empty))
+ {
+ m_logger->warn("unable to read value of setting \"{}\"", settingId);
+ return false;
+ }
+
+ // check if we need to perform any update logic for the setting
+ auto updates = setting->GetUpdates();
+ for (const auto& update : updates)
+ updated |= UpdateSetting(node, setting, update);
+
+ // the setting's value hasn't been updated and is the default value
+ // so we can reset it to the default value (in case the default value has changed)
+ if (!updated && isDefault)
+ setting->Reset();
+
+ return true;
+}
+
+bool CSettingsManager::UpdateSetting(const TiXmlNode* node,
+ const SettingPtr& setting,
+ const CSettingUpdate& update)
+{
+ if (node == nullptr || setting == nullptr || update.GetType() == SettingUpdateType::Unknown)
+ return false;
+
+ bool updated = false;
+ const char *oldSetting = nullptr;
+ const TiXmlNode *oldSettingNode = nullptr;
+ if (update.GetType() == SettingUpdateType::Rename)
+ {
+ if (update.GetValue().empty())
+ return false;
+
+ oldSetting = update.GetValue().c_str();
+ std::string categoryTag, settingTag;
+ if (!ParseSettingIdentifier(oldSetting, categoryTag, settingTag))
+ return false;
+
+ auto categoryNode = node;
+ if (!categoryTag.empty())
+ {
+ categoryNode = node->FirstChild(categoryTag);
+ if (categoryNode == nullptr)
+ return false;
+ }
+
+ oldSettingNode = categoryNode->FirstChild(settingTag);
+ if (oldSettingNode == nullptr)
+ return false;
+
+ if (setting->FromString(oldSettingNode->FirstChild() != nullptr ? oldSettingNode->FirstChild()->ValueStr() : StringUtils::Empty))
+ updated = true;
+ else
+ m_logger->warn("unable to update \"{}\" through automatically renaming from \"{}\"",
+ setting->GetId(), oldSetting);
+ }
+
+ updated |= OnSettingUpdate(setting, oldSetting, oldSettingNode);
+ return updated;
+}
+
+void CSettingsManager::UpdateSettingByDependency(const std::string &settingId, const CSettingDependency &dependency)
+{
+ UpdateSettingByDependency(settingId, dependency.GetType());
+}
+
+void CSettingsManager::UpdateSettingByDependency(const std::string &settingId, SettingDependencyType dependencyType)
+{
+ auto settingIt = FindSetting(settingId);
+ if (settingIt == m_settings.end())
+ return;
+ SettingPtr setting = settingIt->second.setting;
+ if (setting == nullptr)
+ return;
+
+ switch (dependencyType)
+ {
+ case SettingDependencyType::Enable:
+ // just trigger the property changed callback and a call to
+ // CSetting::IsEnabled() will automatically determine the new
+ // enabled state
+ OnSettingPropertyChanged(setting, "enabled");
+ break;
+
+ case SettingDependencyType::Update:
+ {
+ SettingType type = setting->GetType();
+ if (type == SettingType::Integer)
+ {
+ auto settingInt = std::static_pointer_cast<CSettingInt>(setting);
+ if (settingInt->GetOptionsType() == SettingOptionsType::Dynamic)
+ settingInt->UpdateDynamicOptions();
+ }
+ else if (type == SettingType::String)
+ {
+ auto settingString = std::static_pointer_cast<CSettingString>(setting);
+ if (settingString->GetOptionsType() == SettingOptionsType::Dynamic)
+ settingString->UpdateDynamicOptions();
+ }
+ break;
+ }
+
+ case SettingDependencyType::Visible:
+ // just trigger the property changed callback and a call to
+ // CSetting::IsVisible() will automatically determine the new
+ // visible state
+ OnSettingPropertyChanged(setting, "visible");
+ break;
+
+ case SettingDependencyType::Unknown:
+ default:
+ break;
+ }
+}
+
+void CSettingsManager::AddSetting(const std::shared_ptr<CSetting>& setting)
+{
+ setting->CheckRequirements();
+
+ auto addedSetting = FindSetting(setting->GetId());
+ if (addedSetting == m_settings.end())
+ {
+ Setting tmpSetting = {};
+ auto tmpIt = InsertSetting(setting->GetId(), tmpSetting);
+ addedSetting = tmpIt.first;
+ }
+
+ if (addedSetting->second.setting == nullptr)
+ {
+ addedSetting->second.setting = setting;
+ setting->SetCallback(this);
+ }
+}
+
+void CSettingsManager::ResolveReferenceSettings(const std::shared_ptr<CSettingSection>& section)
+{
+ struct GroupedReferenceSettings
+ {
+ SettingPtr referencedSetting;
+ std::unordered_set<SettingPtr> referenceSettings;
+ };
+ std::map<std::string, GroupedReferenceSettings> groupedReferenceSettings;
+
+ // collect and group all reference(d) settings
+ auto categories = section->GetCategories();
+ for (const auto& category : categories)
+ {
+ auto groups = category->GetGroups();
+ for (auto& group : groups)
+ {
+ auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ {
+ if (setting->IsReference())
+ {
+ auto referencedSettingId = setting->GetReferencedId();
+ auto itGroupedReferenceSetting = groupedReferenceSettings.find(referencedSettingId);
+ if (itGroupedReferenceSetting == groupedReferenceSettings.end())
+ {
+ SettingPtr referencedSetting = nullptr;
+ auto itReferencedSetting = FindSetting(referencedSettingId);
+ if (itReferencedSetting == m_settings.end())
+ {
+ m_logger->warn("missing referenced setting \"{}\"", referencedSettingId);
+ continue;
+ }
+
+ GroupedReferenceSettings groupedReferenceSetting;
+ groupedReferenceSetting.referencedSetting = itReferencedSetting->second.setting;
+
+ itGroupedReferenceSetting = groupedReferenceSettings.insert(
+ std::make_pair(referencedSettingId, groupedReferenceSetting)).first;
+ }
+
+ itGroupedReferenceSetting->second.referenceSettings.insert(setting);
+ }
+ }
+ }
+ }
+
+ if (groupedReferenceSettings.empty())
+ return;
+
+ // merge all reference settings into the referenced setting
+ for (const auto& groupedReferenceSetting : groupedReferenceSettings)
+ {
+ auto itReferencedSetting = FindSetting(groupedReferenceSetting.first);
+ if (itReferencedSetting == m_settings.end())
+ continue;
+
+ for (const auto& referenceSetting : groupedReferenceSetting.second.referenceSettings)
+ {
+ groupedReferenceSetting.second.referencedSetting->MergeDetails(*referenceSetting);
+
+ itReferencedSetting->second.references.insert(referenceSetting->GetId());
+ }
+ }
+
+ // resolve any reference settings
+ for (const auto& category : categories)
+ {
+ auto groups = category->GetGroups();
+ for (auto& group : groups)
+ {
+ auto settings = group->GetSettings();
+ for (const auto& setting : settings)
+ {
+ if (setting->IsReference())
+ {
+ auto referencedSettingId = setting->GetReferencedId();
+ auto itGroupedReferenceSetting = groupedReferenceSettings.find(referencedSettingId);
+ if (itGroupedReferenceSetting != groupedReferenceSettings.end())
+ {
+ const auto referencedSetting = itGroupedReferenceSetting->second.referencedSetting;
+
+ // clone the referenced setting and copy the general properties of the reference setting
+ auto clonedReferencedSetting = referencedSetting->Clone(setting->GetId());
+ clonedReferencedSetting->SetReferencedId(referencedSettingId);
+ clonedReferencedSetting->MergeBasics(*setting);
+
+ group->ReplaceSetting(setting, clonedReferencedSetting);
+
+ // update the setting
+ auto itReferenceSetting = FindSetting(setting->GetId());
+ if (itReferenceSetting != m_settings.end())
+ itReferenceSetting->second.setting = clonedReferencedSetting;
+ }
+ }
+ }
+ }
+ }
+}
+
+void CSettingsManager::CleanupIncompleteSettings()
+{
+ // remove any empty and reference settings
+ for (auto setting = m_settings.begin(); setting != m_settings.end(); )
+ {
+ auto tmpIterator = setting++;
+ if (tmpIterator->second.setting == nullptr)
+ {
+ m_logger->warn("removing empty setting \"{}\"", tmpIterator->first);
+ m_settings.erase(tmpIterator);
+ }
+ }
+}
+
+void CSettingsManager::RegisterSettingOptionsFiller(const std::string &identifier, void *filler, SettingOptionsFillerType type)
+{
+ std::unique_lock<CSharedSection> lock(m_critical);
+ auto it = m_optionsFillers.find(identifier);
+ if (it != m_optionsFillers.end())
+ return;
+
+ SettingOptionsFiller optionsFiller = { filler, type };
+ m_optionsFillers.insert(make_pair(identifier, optionsFiller));
+}
+
+void CSettingsManager::ResolveSettingDependencies(const std::shared_ptr<CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ ResolveSettingDependencies(FindSetting(setting->GetId())->second);
+}
+
+void CSettingsManager::ResolveSettingDependencies(const Setting& setting)
+{
+ if (setting.setting == nullptr)
+ return;
+
+ // if the setting has a parent setting, add it to its children
+ auto parentSettingId = setting.setting->GetParent();
+ if (!parentSettingId.empty())
+ {
+ auto itParentSetting = FindSetting(parentSettingId);
+ if (itParentSetting != m_settings.end())
+ itParentSetting->second.children.insert(setting.setting->GetId());
+ }
+
+ // handle all dependencies of the setting
+ const auto& dependencies = setting.setting->GetDependencies();
+ for (const auto& deps : dependencies)
+ {
+ const auto settingIds = deps.GetSettings();
+ for (const auto& settingId : settingIds)
+ {
+ auto settingIt = FindSetting(settingId);
+ if (settingIt == m_settings.end())
+ continue;
+
+ bool newDep = true;
+ auto& settingDeps = settingIt->second.dependencies[setting.setting->GetId()];
+ for (const auto& dep : settingDeps)
+ {
+ if (dep.GetType() == deps.GetType())
+ {
+ newDep = false;
+ break;
+ }
+ }
+
+ if (newDep)
+ settingDeps.push_back(deps);
+ }
+ }
+}
+
+CSettingsManager::SettingMap::const_iterator CSettingsManager::FindSetting(std::string settingId) const
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.find(settingId);
+}
+
+CSettingsManager::SettingMap::iterator CSettingsManager::FindSetting(std::string settingId)
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.find(settingId);
+}
+
+std::pair<CSettingsManager::SettingMap::iterator, bool> CSettingsManager::InsertSetting(std::string settingId, const Setting& setting)
+{
+ StringUtils::ToLower(settingId);
+ return m_settings.insert(std::make_pair(settingId, setting));
+}
diff --git a/xbmc/settings/lib/SettingsManager.h b/xbmc/settings/lib/SettingsManager.h
new file mode 100644
index 0000000..161c1a9
--- /dev/null
+++ b/xbmc/settings/lib/SettingsManager.h
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2013-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.
+ */
+
+#pragma once
+
+#include "ISettingCallback.h"
+#include "ISettingControlCreator.h"
+#include "ISettingCreator.h"
+#include "ISettingsHandler.h"
+#include "ISettingsValueSerializer.h"
+#include "Setting.h"
+#include "SettingConditions.h"
+#include "SettingDefinitions.h"
+#include "SettingDependency.h"
+#include "threads/SharedSection.h"
+#include "utils/logtypes.h"
+
+#include <map>
+#include <set>
+#include <unordered_set>
+#include <vector>
+
+class CSettingCategory;
+class CSettingGroup;
+class CSettingSection;
+class CSettingUpdate;
+
+class TiXmlElement;
+class TiXmlNode;
+
+/*!
+ \ingroup settings
+ \brief Settings manager responsible for initializing, loading and handling
+ all settings.
+ */
+class CSettingsManager : public ISettingCreator,
+ public ISettingControlCreator,
+ private ISettingCallback,
+ private ISettingsHandler
+{
+public:
+ /*!
+ \brief Creates a new (uninitialized) settings manager.
+ */
+ CSettingsManager();
+ ~CSettingsManager() override;
+
+ static const uint32_t Version;
+ static const uint32_t MinimumSupportedVersion;
+
+ // implementation of ISettingCreator
+ std::shared_ptr<CSetting> CreateSetting(const std::string &settingType, const std::string &settingId, CSettingsManager *settingsManager = nullptr) const override;
+
+ // implementation of ISettingControlCreator
+ std::shared_ptr<ISettingControl> CreateControl(const std::string &controlType) const override;
+
+ uint32_t GetVersion() const { return Version; }
+ uint32_t GetMinimumSupportedVersion() const { return MinimumSupportedVersion; }
+
+ /*!
+ \brief Try to get the version of the setting definitions/values represented by the given XML element.
+
+ \param root XML element representing setting definitions/values
+ \return Version of the setting definitions/values or 0 if no version has been specified
+ */
+ uint32_t ParseVersion(const TiXmlElement* root) const;
+
+ /*!
+ \brief Initializes the settings manager using the setting definitions
+ represented by the given XML element.
+
+ \param root XML element representing setting definitions
+ \return True if the XML element was successfully deserialized into setting definitions, false otherwise
+ */
+ bool Initialize(const TiXmlElement *root);
+ /*!
+ \brief Loads setting values from the given XML element.
+
+ \param root XML element containing setting values
+ \param updated Whether some settings were automatically updated
+ \param triggerEvents Whether to trigger ISettingCallback methods
+ \param loadedSettings A list to fill with all the successfully loaded settings
+ \return True if the setting values were successfully loaded, false otherwise
+ */
+ bool Load(const TiXmlElement *root, bool &updated, bool triggerEvents = true, std::map<std::string, std::shared_ptr<CSetting>> *loadedSettings = nullptr);
+ /*!
+ \brief Saves the setting values using the given serializer.
+
+ \param serializer Settings value serializer to use
+ \return True if the setting values were successfully serialized, false otherwise
+ */
+ bool Save(const ISettingsValueSerializer* serializer, std::string& serializedValues) const;
+ /*!
+ \brief Unloads the previously loaded setting values.
+
+ The values of all the settings are reset to their default values.
+ */
+ void Unload();
+ /*!
+ \brief Clears the complete settings manager.
+
+ This removes all initialized settings, groups, categories and sections and
+ returns to the uninitialized state. Any registered callbacks or
+ implementations stay registered.
+ */
+ void Clear();
+
+ /*!
+ \brief Loads the setting being represented by the given XML node with the
+ given identifier.
+
+ \param node XML node representing the setting to load
+ \param settingId Setting identifier
+ \return True if the setting was successfully loaded from the given XML node, false otherwise
+ */
+ bool LoadSetting(const TiXmlNode *node, const std::string &settingId);
+
+ /*!
+ \brief Loads the setting being represented by the given XML node with the
+ given identifier.
+
+ \param node XML node representing the setting to load
+ \param settingId Setting identifier
+ \param updated Set to true if the setting's value was updated
+ \return True if the setting was successfully loaded from the given XML node, false otherwise
+ */
+ bool LoadSetting(const TiXmlNode *node, const std::string &settingId, bool &updated);
+
+ /*!
+ \brief Tells the settings system that the initialization is complete.
+
+ Setting values can only be loaded after a complete and successful
+ initialization of the settings system.
+ */
+ void SetInitialized();
+ /*!
+ \brief Returns whether the settings system has been initialized or not.
+ */
+ bool IsInitialized() const { return m_initialized; }
+ /*!
+ \brief Tells the settings system that all setting values
+ have been loaded.
+
+ This manual trigger is necessary to enable the ISettingCallback methods
+ being executed.
+ */
+ void SetLoaded() { m_loaded = true; }
+ /*!
+ \brief Returns whether the settings system has been loaded or not.
+ */
+ bool IsLoaded() const { return m_loaded; }
+
+ /*!
+ \brief Adds the given section, its categories, groups and settings.
+
+ This is possible before and after the setting definitions have been
+ initialized.
+ */
+ void AddSection(const std::shared_ptr<CSettingSection>& section);
+
+ /*!
+ \brief Adds the given setting to the given group in the given category in
+ the given section;
+
+ If the given section has not been added yet, it is added. If the given
+ category has not been added to the given section yet, it is added. If the
+ given group has not been added to the given category yet, it is added. If
+ the given setting has not been added to the given group yet, it is added.
+
+ This is possible before and after the setting definitions have been
+ initialized.
+
+ \param setting New setting to be added
+ \param section Section the new setting should be added to
+ \param category Category the new setting should be added to
+ \param group Group the new setting should be added to
+ \return True if the setting has been added, false otherwise
+ */
+ bool AddSetting(const std::shared_ptr<CSetting>& setting,
+ const std::shared_ptr<CSettingSection>& section,
+ const std::shared_ptr<CSettingCategory>& category,
+ const std::shared_ptr<CSettingGroup>& group);
+
+ /*!
+ \brief Registers the given ISettingCallback implementation to be triggered
+ for the given list of settings.
+
+ \param settingsHandler ISettingsHandler implementation
+ \param settingList List of settings to trigger the given ISettingCallback implementation
+ */
+ void RegisterCallback(ISettingCallback *callback, const std::set<std::string> &settingList);
+ /*!
+ \brief Unregisters the given ISettingCallback implementation.
+
+ \param callback ISettingCallback implementation
+ */
+ void UnregisterCallback(ISettingCallback *callback);
+
+ /*!
+ \brief Registers a custom setting type and its ISettingCreator
+ implementation.
+
+ When a setting definition for a registered custom setting type is found its
+ ISettingCreator implementation is called to create and deserialize the
+ setting definition.
+
+ \param settingType String representation of the custom setting type
+ \param settingCreator ISettingCreator implementation
+ */
+ void RegisterSettingType(const std::string &settingType, ISettingCreator *settingCreator);
+
+ /*!
+ \brief Registers a custom setting control type and its
+ ISettingControlCreator implementation
+
+ When a setting control definition for a registered custom setting control
+ type is found its ISettingControlCreator implementation is called to create
+ and deserialize the setting control definition.
+
+ \param controlType String representation of the custom setting control type
+ \param settingControlCreator ISettingControlCreator implementation
+ */
+ void RegisterSettingControl(const std::string &controlType, ISettingControlCreator *settingControlCreator);
+
+ /*!
+ \brief Registers the given ISettingsHandler implementation.
+
+ \param settingsHandler ISettingsHandler implementation
+ \param bFront If True, insert the handler in front of other registered handlers, insert at the end otherwise.
+ */
+ void RegisterSettingsHandler(ISettingsHandler *settingsHandler, bool bFront = false);
+ /*!
+ \brief Unregisters the given ISettingsHandler implementation.
+
+ \param settingsHandler ISettingsHandler implementation
+ */
+ void UnregisterSettingsHandler(ISettingsHandler *settingsHandler);
+
+ /*!
+ \brief Registers the given integer setting options filler under the given identifier.
+
+ \param identifier Setting options filler identifier
+ \param optionsFiller Integer setting options filler implementation
+ */
+ void RegisterSettingOptionsFiller(const std::string &identifier, IntegerSettingOptionsFiller optionsFiller);
+ /*!
+ \brief Registers the given string setting options filler under the given identifier.
+
+ \param identifier Setting options filler identifier
+ \param optionsFiller String setting options filler implementation
+ */
+ void RegisterSettingOptionsFiller(const std::string &identifier, StringSettingOptionsFiller optionsFiller);
+ /*!
+ \brief Unregisters the setting options filler registered under the given identifier.
+
+ \param identifier Setting options filler identifier
+ */
+ void UnregisterSettingOptionsFiller(const std::string &identifier);
+ /*!
+ \brief Gets the implementation of the setting options filler used by the
+ given setting.
+
+ \param setting Setting object
+ \return Implementation of the setting options filler (either IntegerSettingOptionsFiller or StringSettingOptionsFiller)
+ */
+ void* GetSettingOptionsFiller(const std::shared_ptr<const CSetting>& setting);
+
+ /*!
+ \brief Checks whether any settings have been initialized.
+
+ \return True if at least one setting has been initialized, false otherwise*/
+ bool HasSettings() const;
+
+ /*!
+ \brief Gets the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Setting object with the given identifier or nullptr if the identifier is unknown
+ */
+ std::shared_ptr<CSetting> GetSetting(const std::string &id) const;
+ /*!
+ \brief Gets the full list of setting sections.
+
+ \return List of setting sections
+ */
+ std::vector<std::shared_ptr<CSettingSection>> GetSections() const;
+ /*!
+ \brief Gets the setting section with the given identifier.
+
+ \param section Setting section identifier
+ \return Setting section with the given identifier or nullptr if the identifier is unknown
+ */
+ std::shared_ptr<CSettingSection> GetSection(std::string section) const;
+ /*!
+ \brief Gets a map of settings (and their dependencies) which depend on
+ the setting with the given identifier.
+
+ It is important to note that the returned dependencies are not the
+ dependencies of the setting with the given identifier but the settings
+ (and their dependencies) which depend on the setting with the given
+ identifier.
+
+ \param id Setting identifier
+ \return Map of settings (and their dependencies) which depend on the setting with the given identifier
+ */
+ SettingDependencyMap GetDependencies(const std::string &id) const;
+ /*!
+ \brief Gets a map of settings (and their dependencies) which depend on
+ the given setting.
+
+ It is important to note that the returned dependencies are not the
+ dependencies of the given setting but the settings (and their dependencies)
+ which depend on the given setting.
+
+ \param setting Setting object
+ \return Map of settings (and their dependencies) which depend on the given setting
+ */
+ SettingDependencyMap GetDependencies(const std::shared_ptr<const CSetting>& setting) const;
+
+ /*!
+ \brief Gets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Boolean value of the setting with the given identifier
+ */
+ bool GetBool(const std::string &id) const;
+ /*!
+ \brief Gets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Integer value of the setting with the given identifier
+ */
+ int GetInt(const std::string &id) const;
+ /*!
+ \brief Gets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return Real number value of the setting with the given identifier
+ */
+ double GetNumber(const std::string &id) const;
+ /*!
+ \brief Gets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return String value of the setting with the given identifier
+ */
+ std::string GetString(const std::string &id) const;
+ /*!
+ \brief Gets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \return List of values of the setting with the given identifier
+ */
+ std::vector< std::shared_ptr<CSetting> > GetList(const std::string &id) const;
+
+ /*!
+ \brief Sets the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Boolean value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetBool(const std::string &id, bool value);
+ /*!
+ \brief Toggles the boolean value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \return True if toggling the boolean value was successful, false otherwise
+ */
+ bool ToggleBool(const std::string &id);
+ /*!
+ \brief Sets the integer value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Integer value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetInt(const std::string &id, int value);
+ /*!
+ \brief Sets the real number value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Real number value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetNumber(const std::string &id, double value);
+ /*!
+ \brief Sets the string value of the setting with the given identifier.
+
+ \param id Setting identifier
+ \param value String value to set
+ \return True if setting the value was successful, false otherwise
+ */
+ bool SetString(const std::string &id, const std::string &value);
+ /*!
+ \brief Sets the values of the list setting with the given identifier.
+
+ \param id Setting identifier
+ \param value Values to set
+ \return True if setting the values was successful, false otherwise
+ */
+ bool SetList(const std::string &id, const std::vector< std::shared_ptr<CSetting> > &value);
+
+ /*!
+ \brief Sets the value of the setting to its default.
+
+ \param id Setting identifier
+ \return True if setting the value to its default was successful, false otherwise
+ */
+ bool SetDefault(const std::string &id);
+ /*!
+ \brief Sets the value of all settings to their default.
+ */
+ void SetDefaults();
+
+ /*!
+ \brief Gets the setting conditions manager used by the settings manager.
+
+ \return Setting conditions manager used by the settings manager.
+ */
+ const CSettingConditionsManager& GetConditions() const { return m_conditions; }
+ /*!
+ \brief Adds the given static condition.
+
+ A static condition is just a string. If a static condition is evaluated,
+ the result depends on whether the condition's value is defined or not.
+
+ \param condition Static condition string/value
+ */
+ void AddCondition(const std::string &condition);
+ /*!
+ \brief Adds the given dynamic condition.
+
+ A dynamic condition has an identifier and an implementation which is
+ triggered when the condition is evaluated.
+
+ \param identifier Identifier of the dynamic condition
+ \param condition Implementation of the dynamic condition
+ \param data Opaque data pointer, will be passed back to SettingConditionCheck function
+ */
+ void AddDynamicCondition(const std::string &identifier, SettingConditionCheck condition, void *data = nullptr);
+
+ /*!
+ \brief Removes the given dynamic condition.
+
+ \param identifier Identifier of the dynamic condition
+ */
+ void RemoveDynamicCondition(const std::string &identifier);
+
+private:
+ // implementation of ISettingCallback
+ bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override;
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+ bool OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
+ const char* oldSettingId,
+ const TiXmlNode* oldSettingNode) override;
+ void OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
+ const char* propertyName) override;
+
+ // implementation of ISettingsHandler
+ bool OnSettingsLoading() override;
+ void OnSettingsLoaded() override;
+ void OnSettingsUnloaded() override;
+ bool OnSettingsSaving() const override;
+ void OnSettingsSaved() const override;
+ void OnSettingsCleared() override;
+
+ bool Serialize(TiXmlNode *parent) const;
+ bool Deserialize(const TiXmlNode *node, bool &updated, std::map<std::string, std::shared_ptr<CSetting>> *loadedSettings = nullptr);
+
+ bool LoadSetting(const TiXmlNode* node, const std::shared_ptr<CSetting>& setting, bool& updated);
+ bool UpdateSetting(const TiXmlNode* node,
+ const std::shared_ptr<CSetting>& setting,
+ const CSettingUpdate& update);
+ void UpdateSettingByDependency(const std::string &settingId, const CSettingDependency &dependency);
+ void UpdateSettingByDependency(const std::string &settingId, SettingDependencyType dependencyType);
+
+ void AddSetting(const std::shared_ptr<CSetting>& setting);
+
+ void ResolveReferenceSettings(const std::shared_ptr<CSettingSection>& section);
+ void CleanupIncompleteSettings();
+
+ enum class SettingOptionsFillerType {
+ Unknown = 0,
+ Integer,
+ String
+ };
+
+ void RegisterSettingOptionsFiller(const std::string &identifier, void *filler, SettingOptionsFillerType type);
+
+ using CallbackSet = std::set<ISettingCallback *>;
+ struct Setting {
+ std::shared_ptr<CSetting> setting;
+ SettingDependencyMap dependencies;
+ std::set<std::string> children;
+ CallbackSet callbacks;
+ std::unordered_set<std::string> references;
+ };
+
+ using SettingMap = std::map<std::string, Setting>;
+
+ void ResolveSettingDependencies(const std::shared_ptr<CSetting>& setting);
+ void ResolveSettingDependencies(const Setting& setting);
+
+ SettingMap::const_iterator FindSetting(std::string settingId) const;
+ SettingMap::iterator FindSetting(std::string settingId);
+ std::pair<SettingMap::iterator, bool> InsertSetting(std::string settingId, const Setting& setting);
+
+ bool m_initialized = false;
+ bool m_loaded = false;
+
+ SettingMap m_settings;
+ using SettingSectionMap = std::map<std::string, std::shared_ptr<CSettingSection>>;
+ SettingSectionMap m_sections;
+
+ using SettingCreatorMap = std::map<std::string, ISettingCreator*>;
+ SettingCreatorMap m_settingCreators;
+
+ using SettingControlCreatorMap = std::map<std::string, ISettingControlCreator*>;
+ SettingControlCreatorMap m_settingControlCreators;
+
+ using SettingsHandlers = std::vector<ISettingsHandler*>;
+ SettingsHandlers m_settingsHandlers;
+
+ CSettingConditionsManager m_conditions;
+
+ struct SettingOptionsFiller {
+ void *filler;
+ SettingOptionsFillerType type;
+ };
+ using SettingOptionsFillerMap = std::map<std::string, SettingOptionsFiller>;
+ SettingOptionsFillerMap m_optionsFillers;
+
+ mutable CSharedSection m_critical;
+ mutable CSharedSection m_settingsCritical;
+
+ Logger m_logger;
+};
diff --git a/xbmc/settings/windows/CMakeLists.txt b/xbmc/settings/windows/CMakeLists.txt
new file mode 100644
index 0000000..c1b69ac
--- /dev/null
+++ b/xbmc/settings/windows/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(SOURCES GUIControlSettings.cpp
+ GUIWindowSettings.cpp
+ GUIWindowSettingsCategory.cpp
+ GUIWindowSettingsScreenCalibration.cpp)
+
+set(HEADERS GUIControlSettings.h
+ GUIWindowSettings.h
+ GUIWindowSettingsCategory.h
+ GUIWindowSettingsScreenCalibration.h)
+
+core_add_library(settings_windows)
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();
+}
diff --git a/xbmc/settings/windows/GUIControlSettings.h b/xbmc/settings/windows/GUIControlSettings.h
new file mode 100644
index 0000000..777b60d
--- /dev/null
+++ b/xbmc/settings/windows/GUIControlSettings.h
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/ISliderCallback.h"
+#include "utils/ILocalizer.h"
+
+#include <functional>
+#include <memory>
+#include <stdlib.h>
+#include <string>
+
+class CGUIControl;
+class CGUIImage;
+class CGUISpinControlEx;
+class CGUIEditControl;
+class CGUIButtonControl;
+class CGUIRadioButtonControl;
+class CGUISettingsSliderControl;
+class CGUILabelControl;
+class CGUIColorButtonControl;
+
+class CSetting;
+class CSettingControlSlider;
+class CSettingString;
+class CSettingPath;
+
+class CFileItemList;
+class CVariant;
+
+class CGUIControlBaseSetting : protected ILocalizer
+{
+public:
+ CGUIControlBaseSetting(int id, std::shared_ptr<CSetting> pSetting, ILocalizer* localizer);
+ ~CGUIControlBaseSetting() override = default;
+
+ int GetID() const { return m_id; }
+ std::shared_ptr<CSetting> GetSetting() { return m_pSetting; }
+
+ /*!
+ \brief Specifies that this setting should update after a delay
+ Useful for settings that have options to navigate through
+ and may take a while, or require additional input to update
+ once the final setting is chosen. Settings default to updating
+ instantly.
+ \sa IsDelayed()
+ */
+ void SetDelayed() { m_delayed = true; }
+
+ /*!
+ \brief Returns whether this setting should have delayed update
+ \return true if the setting's update should be delayed
+ \sa SetDelayed()
+ */
+ bool IsDelayed() const { return m_delayed; }
+
+ /*!
+ \brief Returns whether this setting is enabled or disabled
+ This state is independent of the real enabled state of a
+ setting control but represents the enabled state of the
+ setting itself based on specific conditions.
+ \return true if the setting is enabled otherwise false
+ \sa SetEnabled()
+ */
+ bool IsEnabled() const;
+
+ /*!
+ \brief Returns whether the setting's value is valid or not
+ */
+ bool IsValid() const { return m_valid; }
+
+ void SetValid(bool valid) { m_valid = valid; }
+
+ virtual CGUIControl* GetControl() { return NULL; }
+ virtual bool OnClick() { return false; }
+ void UpdateFromControl();
+ void UpdateFromSetting(bool updateDisplayOnly = false);
+ virtual void Clear() = 0; ///< Clears the attached control
+protected:
+ // implementation of ILocalizer
+ std::string Localize(std::uint32_t code) const override;
+
+ virtual void Update(bool fromControl, bool updateDisplayOnly);
+
+ int m_id;
+ std::shared_ptr<CSetting> m_pSetting;
+ ILocalizer* m_localizer;
+ bool m_delayed;
+ bool m_valid;
+};
+
+class CGUIControlRadioButtonSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlRadioButtonSetting(CGUIRadioButtonControl* pRadioButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlRadioButtonSetting() override;
+
+ void Select(bool bSelect);
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pRadioButton); }
+ bool OnClick() override;
+ void Clear() override { m_pRadioButton = NULL; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ CGUIRadioButtonControl* m_pRadioButton;
+};
+
+class CGUIControlColorButtonSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlColorButtonSetting(CGUIColorButtonControl* pColorControl,
+ int id,
+ const std::shared_ptr<CSetting>& pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlColorButtonSetting() override;
+
+ void Select(bool bSelect);
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pColorButton); }
+ bool OnClick() override;
+ void Clear() override { m_pColorButton = nullptr; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ CGUIColorButtonControl* m_pColorButton;
+};
+
+class CGUIControlSpinExSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlSpinExSetting(CGUISpinControlEx* pSpin,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlSpinExSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pSpin); }
+ bool OnClick() override;
+ void Clear() override { m_pSpin = NULL; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ void FillControl(bool updateDisplayOnly);
+ void FillIntegerSettingControl(bool updateValues);
+ void FillFloatSettingControl();
+ void FillStringSettingControl(bool updateValues);
+ CGUISpinControlEx* m_pSpin;
+};
+
+class CGUIControlListSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlListSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlListSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pButton); }
+ bool OnClick() override;
+ void Clear() override { m_pButton = NULL; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ bool GetItems(const std::shared_ptr<const CSetting>& setting,
+ CFileItemList& items,
+ bool updateItems) const;
+ bool GetIntegerItems(const std::shared_ptr<const CSetting>& setting,
+ CFileItemList& items,
+ bool updateItems) const;
+ bool GetStringItems(const std::shared_ptr<const CSetting>& setting,
+ CFileItemList& items,
+ bool updateItems) const;
+
+ CGUIButtonControl* m_pButton;
+};
+
+class CGUIControlListColorSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlListColorSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlListColorSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pButton); }
+ bool OnClick() override;
+ void Clear() override { m_pButton = nullptr; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ CGUIButtonControl* m_pButton;
+};
+
+class CGUIControlButtonSetting : public CGUIControlBaseSetting, protected ISliderCallback
+{
+public:
+ CGUIControlButtonSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlButtonSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pButton); }
+ bool OnClick() override;
+ void Clear() override { m_pButton = NULL; }
+
+ static bool GetPath(const std::shared_ptr<CSettingPath>& pathSetting, ILocalizer* localizer);
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+ // implementations of ISliderCallback
+ void OnSliderChange(void* data, CGUISliderControl* slider) override;
+
+private:
+ CGUIButtonControl* m_pButton;
+};
+
+class CGUIControlEditSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlEditSetting(CGUIEditControl* pButton,
+ int id,
+ const std::shared_ptr<CSetting>& pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlEditSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pEdit); }
+ bool OnClick() override;
+ void Clear() override { m_pEdit = NULL; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ static bool InputValidation(const std::string& input, void* data);
+
+ CGUIEditControl* m_pEdit;
+};
+
+class CGUIControlSliderSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlSliderSetting(CGUISettingsSliderControl* pSlider,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlSliderSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pSlider); }
+ bool OnClick() override;
+ void Clear() override { m_pSlider = NULL; }
+
+ static std::string GetText(const std::shared_ptr<CSetting>& setting,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum,
+ ILocalizer* localizer);
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ static bool FormatText(const std::string& formatString,
+ const CVariant& value,
+ const std::string& settingId,
+ std::string& formattedText);
+
+ CGUISettingsSliderControl* m_pSlider;
+};
+
+class CGUIControlRangeSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlRangeSetting(CGUISettingsSliderControl* pSlider,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlRangeSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pSlider); }
+ bool OnClick() override;
+ void Clear() override { m_pSlider = NULL; }
+
+protected:
+ // specialization of CGUIControlBaseSetting
+ void Update(bool fromControl, bool updateDisplayOnly) override;
+
+private:
+ CGUISettingsSliderControl* m_pSlider;
+};
+
+class CGUIControlSeparatorSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlSeparatorSetting(CGUIImage* pImage, int id, ILocalizer* localizer);
+ ~CGUIControlSeparatorSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pImage); }
+ bool OnClick() override { return false; }
+ void Clear() override { m_pImage = NULL; }
+
+private:
+ CGUIImage* m_pImage;
+};
+
+class CGUIControlGroupTitleSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlGroupTitleSetting(CGUILabelControl* pLabel, int id, ILocalizer* localizer);
+ ~CGUIControlGroupTitleSetting() override;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pLabel); }
+ bool OnClick() override { return false; }
+ void Clear() override { m_pLabel = NULL; }
+
+private:
+ CGUILabelControl* m_pLabel;
+};
+
+class CGUIControlLabelSetting : public CGUIControlBaseSetting
+{
+public:
+ CGUIControlLabelSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer);
+ ~CGUIControlLabelSetting() override = default;
+
+ CGUIControl* GetControl() override { return reinterpret_cast<CGUIControl*>(m_pButton); }
+ void Clear() override { m_pButton = NULL; }
+
+private:
+ CGUIButtonControl* m_pButton;
+};
diff --git a/xbmc/settings/windows/GUIWindowSettings.cpp b/xbmc/settings/windows/GUIWindowSettings.cpp
new file mode 100644
index 0000000..d8edede
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettings.cpp
@@ -0,0 +1,19 @@
+/*
+ * 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 "GUIWindowSettings.h"
+
+#include "guilib/WindowIDs.h"
+
+CGUIWindowSettings::CGUIWindowSettings(void)
+ : CGUIWindow(WINDOW_SETTINGS_MENU, "Settings.xml")
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIWindowSettings::~CGUIWindowSettings(void) = default;
diff --git a/xbmc/settings/windows/GUIWindowSettings.h b/xbmc/settings/windows/GUIWindowSettings.h
new file mode 100644
index 0000000..c658186
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettings.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIWindow.h"
+
+class CGUIWindowSettings :
+ public CGUIWindow
+{
+public:
+ CGUIWindowSettings(void);
+ ~CGUIWindowSettings(void) override;
+};
diff --git a/xbmc/settings/windows/GUIWindowSettingsCategory.cpp b/xbmc/settings/windows/GUIWindowSettingsCategory.cpp
new file mode 100644
index 0000000..18be9cd
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettingsCategory.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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 "GUIWindowSettingsCategory.h"
+
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "input/Key.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "utils/log.h"
+#include "view/ViewStateSettings.h"
+
+#include <string>
+
+#define SETTINGS_SYSTEM WINDOW_SETTINGS_SYSTEM - WINDOW_SETTINGS_START
+#define SETTINGS_SERVICE WINDOW_SETTINGS_SERVICE - WINDOW_SETTINGS_START
+#define SETTINGS_PVR WINDOW_SETTINGS_MYPVR - WINDOW_SETTINGS_START
+#define SETTINGS_PLAYER WINDOW_SETTINGS_PLAYER - WINDOW_SETTINGS_START
+#define SETTINGS_MEDIA WINDOW_SETTINGS_MEDIA - WINDOW_SETTINGS_START
+#define SETTINGS_INTERFACE WINDOW_SETTINGS_INTERFACE - WINDOW_SETTINGS_START
+#define SETTINGS_GAMES WINDOW_SETTINGS_MYGAMES - WINDOW_SETTINGS_START
+
+#define CONTROL_BTN_LEVELS 20
+
+typedef struct {
+ int id;
+ std::string name;
+} SettingGroup;
+
+static const SettingGroup s_settingGroupMap[] = { { SETTINGS_SYSTEM, "system" },
+ { SETTINGS_SERVICE, "services" },
+ { SETTINGS_PVR, "pvr" },
+ { SETTINGS_PLAYER, "player" },
+ { SETTINGS_MEDIA, "media" },
+ { SETTINGS_INTERFACE, "interface" },
+ { SETTINGS_GAMES, "games" } };
+
+#define SettingGroupSize sizeof(s_settingGroupMap) / sizeof(SettingGroup)
+
+CGUIWindowSettingsCategory::CGUIWindowSettingsCategory()
+ : CGUIDialogSettingsManagerBase(WINDOW_SETTINGS_SYSTEM, "SettingsCategory.xml"),
+ m_settings(CServiceBroker::GetSettingsComponent()->GetSettings())
+{
+ // set the correct ID range...
+ m_idRange.clear();
+ m_idRange.push_back(WINDOW_SETTINGS_SYSTEM);
+ m_idRange.push_back(WINDOW_SETTINGS_SERVICE);
+ m_idRange.push_back(WINDOW_SETTINGS_MYPVR);
+ m_idRange.push_back(WINDOW_SETTINGS_PLAYER);
+ m_idRange.push_back(WINDOW_SETTINGS_MEDIA);
+ m_idRange.push_back(WINDOW_SETTINGS_INTERFACE);
+ m_idRange.push_back(WINDOW_SETTINGS_MYGAMES);
+}
+
+CGUIWindowSettingsCategory::~CGUIWindowSettingsCategory() = default;
+
+bool CGUIWindowSettingsCategory::OnMessage(CGUIMessage &message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_INIT:
+ {
+ m_iSection = message.GetParam2() - CGUIDialogSettingsManagerBase::GetID();
+ CGUIDialogSettingsManagerBase::OnMessage(message);
+ m_returningFromSkinLoad = false;
+
+ if (!message.GetStringParam(0).empty())
+ FocusElement(message.GetStringParam(0));
+
+ return true;
+ }
+
+ case GUI_MSG_FOCUSED:
+ {
+ if (!m_returningFromSkinLoad)
+ CGUIDialogSettingsManagerBase::OnMessage(message);
+ return true;
+ }
+
+ case GUI_MSG_LOAD_SKIN:
+ {
+ if (IsActive())
+ m_returningFromSkinLoad = true;
+ break;
+ }
+
+ case GUI_MSG_NOTIFY_ALL:
+ {
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
+ {
+ if (IsActive() && CDisplaySettings::GetInstance().GetCurrentResolution() != CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution())
+ {
+ CDisplaySettings::GetInstance().SetCurrentResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution(), true);
+ CreateSettings();
+ }
+ }
+ break;
+ }
+
+ case GUI_MSG_PLAYBACK_STARTED:
+ case GUI_MSG_PLAYBACK_ENDED:
+ case GUI_MSG_PLAYBACK_STOPPED:
+ {
+ if (IsActive())
+ {
+ UpdateSettings();
+ }
+ break;
+ }
+ }
+
+ return CGUIDialogSettingsManagerBase::OnMessage(message);
+}
+
+bool CGUIWindowSettingsCategory::OnAction(const CAction &action)
+{
+ switch (action.GetID())
+ {
+ case ACTION_SETTINGS_LEVEL_CHANGE:
+ {
+ //Test if we can access the new level
+ if (!g_passwordManager.CheckSettingLevelLock(CViewStateSettings::GetInstance().GetNextSettingLevel(), true))
+ return false;
+
+ CViewStateSettings::GetInstance().CycleSettingLevel();
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+
+ // try to keep the current position
+ std::string oldCategory;
+ if (m_iCategory >= 0 && m_iCategory < (int)m_categories.size())
+ oldCategory = m_categories[m_iCategory]->GetId();
+
+ SET_CONTROL_LABEL(CONTROL_BTN_LEVELS, 10036 + (int)CViewStateSettings::GetInstance().GetSettingLevel());
+ // only re-create the categories, the settings will be created later
+ SetupControls(false);
+
+ m_iCategory = 0;
+ // try to find the category that was previously selected
+ if (!oldCategory.empty())
+ {
+ for (int i = 0; i < (int)m_categories.size(); i++)
+ {
+ if (m_categories[i]->GetId() == oldCategory)
+ {
+ m_iCategory = i;
+ break;
+ }
+ }
+ }
+
+ CreateSettings();
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ return CGUIDialogSettingsManagerBase::OnAction(action);
+}
+
+bool CGUIWindowSettingsCategory::OnBack(int actionID)
+{
+ Save();
+ return CGUIDialogSettingsManagerBase::OnBack(actionID);
+}
+
+void CGUIWindowSettingsCategory::OnWindowLoaded()
+{
+ SET_CONTROL_LABEL(CONTROL_BTN_LEVELS, 10036 + (int)CViewStateSettings::GetInstance().GetSettingLevel());
+ CGUIDialogSettingsManagerBase::OnWindowLoaded();
+}
+
+int CGUIWindowSettingsCategory::GetSettingLevel() const
+{
+ return (int)CViewStateSettings::GetInstance().GetSettingLevel();
+}
+
+SettingSectionPtr CGUIWindowSettingsCategory::GetSection()
+{
+ for (const SettingGroup& settingGroup : s_settingGroupMap)
+ {
+ if (settingGroup.id == m_iSection)
+ return m_settings->GetSection(settingGroup.name);
+ }
+
+ return NULL;
+}
+
+bool CGUIWindowSettingsCategory::Save()
+{
+ m_settings->Save();
+
+ return true;
+}
+
+CSettingsManager* CGUIWindowSettingsCategory::GetSettingsManager() const
+{
+ return m_settings->GetSettingsManager();
+}
+
+void CGUIWindowSettingsCategory::FocusElement(const std::string& elementId)
+{
+ for (size_t i = 0; i < m_categories.size(); ++i)
+ {
+ if (m_categories[i]->GetId() == elementId)
+ {
+ SET_CONTROL_FOCUS(CONTROL_SETTINGS_START_BUTTONS + i, 0);
+ return;
+ }
+ for (const auto& group: m_categories[i]->GetGroups())
+ {
+ for (const auto& setting : group->GetSettings())
+ {
+ if (setting->GetId() == elementId)
+ {
+ SET_CONTROL_FOCUS(CONTROL_SETTINGS_START_BUTTONS + i, 0);
+
+ auto control = GetSettingControl(elementId);
+ if (control)
+ SET_CONTROL_FOCUS(control->GetID(), 0);
+ else
+ CLog::Log(LOGERROR,
+ "CGUIWindowSettingsCategory: failed to get control for setting '{}'.",
+ elementId);
+ return;
+ }
+ }
+ }
+ }
+ CLog::Log(LOGERROR,
+ "CGUIWindowSettingsCategory: failed to set focus. unknown category/setting id '{}'.",
+ elementId);
+}
diff --git a/xbmc/settings/windows/GUIWindowSettingsCategory.h b/xbmc/settings/windows/GUIWindowSettingsCategory.h
new file mode 100644
index 0000000..88c3ac8
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettingsCategory.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "settings/dialogs/GUIDialogSettingsManagerBase.h"
+
+class CSettings;
+
+class CGUIWindowSettingsCategory : public CGUIDialogSettingsManagerBase
+{
+public:
+ CGUIWindowSettingsCategory();
+ ~CGUIWindowSettingsCategory() override;
+
+ // specialization of CGUIControl
+ bool OnMessage(CGUIMessage &message) override;
+ bool OnAction(const CAction &action) override;
+ bool OnBack(int actionID) override;
+ int GetID() const override { return CGUIDialogSettingsManagerBase::GetID() + m_iSection; }
+
+ // specialization of CGUIWindow
+ bool IsDialog() const override { return false; }
+
+protected:
+ // specialization of CGUIWindow
+ void OnWindowLoaded() override;
+
+ // implementation of CGUIDialogSettingsBase
+ int GetSettingLevel() const override;
+ std::shared_ptr<CSettingSection> GetSection() override;
+ bool Save() override;
+
+ // implementation of CGUIDialogSettingsManagerBase
+ CSettingsManager* GetSettingsManager() const override;
+
+ /*!
+ * Set focus to a category or setting in this window. The setting/category must be active in the
+ * current level.
+ */
+ void FocusElement(const std::string& elementId);
+
+ std::shared_ptr<CSettings> m_settings;
+ int m_iSection = 0;
+ bool m_returningFromSkinLoad = false; // true if we are returning from loading the skin
+};
diff --git a/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.cpp b/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.cpp
new file mode 100644
index 0000000..7e67de6
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.cpp
@@ -0,0 +1,630 @@
+/*
+ * 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 "GUIWindowSettingsScreenCalibration.h"
+
+#include "ServiceBroker.h"
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPlayer.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMoverControl.h"
+#include "guilib/GUIResizeControl.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/actions/Action.h"
+#include "input/actions/ActionIDs.h"
+#include "settings/DisplaySettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/SubtitlesSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+#include "windowing/WinSystem.h"
+
+#include <string>
+#include <utility>
+
+using namespace KODI;
+
+namespace
+{
+constexpr int CONTROL_LABEL_RES = 2;
+constexpr int CONTROL_LABEL_DESCRIPTION = 3;
+constexpr int CONTROL_LABEL_VALUE = 4;
+constexpr int CONTROL_TOP_LEFT = 8;
+constexpr int CONTROL_BOTTOM_RIGHT = 9;
+constexpr int CONTROL_SUBTITLES = 10;
+constexpr int CONTROL_PIXEL_RATIO = 11;
+constexpr int CONTROL_RESET = 12;
+constexpr int CONTROL_VIDEO = 20;
+
+constexpr int DEFAULT_GUI_HEIGHT = 1080;
+constexpr int DEFAULT_GUI_WIDTH = 1920;
+
+// Fixed transparent space of the subtitle bar (on top + below) for touch screen
+// must match with the space of the skin bar image
+constexpr int CONTROL_SUBTITLES_SPACE = 80;
+} // unnamed namespace
+
+CGUIWindowSettingsScreenCalibration::CGUIWindowSettingsScreenCalibration(void)
+ : CGUIWindow(WINDOW_SCREEN_CALIBRATION, "SettingsScreenCalibration.xml")
+{
+ m_iCurRes = 0;
+ m_iControl = 0;
+ m_fPixelRatioBoxHeight = 0.0f;
+ m_needsScaling = false; // we handle all the scaling
+}
+
+CGUIWindowSettingsScreenCalibration::~CGUIWindowSettingsScreenCalibration(void) = default;
+
+
+void CGUIWindowSettingsScreenCalibration::ResetCalibration()
+{
+ // We ask to reset the calibration
+ // Reset will be applied to: windowed mode or per fullscreen resolution
+ CGUIDialogYesNo* pDialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO);
+ pDialog->SetHeading(CVariant{20325});
+ std::string strText = StringUtils::Format(
+ g_localizeStrings.Get(20326),
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(m_Res[m_iCurRes]).strMode);
+ pDialog->SetText(CVariant{std::move(strText)});
+ pDialog->SetChoice(0, CVariant{222});
+ pDialog->SetChoice(1, CVariant{186});
+ pDialog->Open();
+ if (pDialog->IsConfirmed())
+ {
+ CServiceBroker::GetWinSystem()->GetGfxContext().ResetScreenParameters(m_Res[m_iCurRes]);
+ ResetControls();
+ // Send GUI_MSG_WINDOW_RESIZE to rescale font size/aspect for label controls
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(
+ GUI_MSG_NOTIFY_ALL, WINDOW_SCREEN_CALIBRATION, 0, GUI_MSG_WINDOW_RESIZE);
+ }
+}
+
+bool CGUIWindowSettingsScreenCalibration::OnAction(const CAction& action)
+{
+ switch (action.GetID())
+ {
+ case ACTION_CALIBRATE_SWAP_ARROWS:
+ {
+ NextControl();
+ return true;
+ }
+ break;
+
+ case ACTION_CALIBRATE_RESET:
+ {
+ ResetCalibration();
+ return true;
+ }
+ break;
+
+ case ACTION_CHANGE_RESOLUTION:
+ // choose the next resolution in our list
+ {
+ m_iCurRes = (m_iCurRes + 1) % m_Res.size();
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(m_Res[m_iCurRes], false);
+ ResetControls();
+ // Send GUI_MSG_WINDOW_RESIZE to rescale font size/aspect for label controls
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(
+ GUI_MSG_NOTIFY_ALL, WINDOW_SCREEN_CALIBRATION, 0, GUI_MSG_WINDOW_RESIZE);
+ return true;
+ }
+ break;
+
+ // ignore all gesture meta actions
+ case ACTION_GESTURE_BEGIN:
+ case ACTION_GESTURE_END:
+ case ACTION_GESTURE_ABORT:
+ case ACTION_GESTURE_NOTIFY:
+ case ACTION_GESTURE_PAN:
+ case ACTION_GESTURE_ROTATE:
+ case ACTION_GESTURE_ZOOM:
+ return true;
+
+ case ACTION_MOUSE_LEFT_CLICK:
+ case ACTION_TOUCH_TAP:
+ if (GetFocusedControlID() == CONTROL_RESET)
+ {
+ ResetCalibration();
+ return true;
+ }
+ break;
+ }
+
+ // if we see a mouse move event without dx and dy (amount2 and amount3) these
+ // are the focus actions which are generated on touch events and those should
+ // be eaten/ignored here. Else we will switch to the screencalibration controls
+ // which are at that x/y value on each touch/tap/swipe which makes the whole window
+ // unusable for touch screens
+ if (action.GetID() == ACTION_MOUSE_MOVE && action.GetAmount(2) == 0 && action.GetAmount(3) == 0)
+ return true;
+
+ return CGUIWindow::OnAction(action); // base class to handle basic movement etc.
+}
+
+void CGUIWindowSettingsScreenCalibration::AllocResources(bool forceLoad)
+{
+ CGUIWindow::AllocResources(forceLoad);
+}
+
+void CGUIWindowSettingsScreenCalibration::FreeResources(bool forceUnload)
+{
+ CGUIWindow::FreeResources(forceUnload);
+}
+
+
+bool CGUIWindowSettingsScreenCalibration::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CDisplaySettings::GetInstance().UpdateCalibrations();
+ CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetCalibrating(false);
+ // reset our screen resolution to what it was initially
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetVideoResolution(
+ CDisplaySettings::GetInstance().GetCurrentResolution(), false);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(
+ GUI_MSG_NOTIFY_ALL, WINDOW_SCREEN_CALIBRATION, 0, GUI_MSG_WINDOW_RESIZE);
+ }
+ break;
+
+ case GUI_MSG_WINDOW_INIT:
+ {
+ CGUIWindow::OnMessage(message);
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetCalibrating(true);
+
+ // Get the default XML size values of controls,
+ // we will use these values to scale controls when the resolution change
+ for (int id = CONTROL_TOP_LEFT; id <= CONTROL_RESET; id++)
+ {
+ CGUIControl* control = GetControl(id);
+ if (control)
+ {
+ m_controlsSize.emplace(id, std::make_pair(control->GetHeight(), control->GetWidth()));
+ }
+ }
+
+ // Get the allowable resolutions that we can calibrate...
+ m_Res.clear();
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPlayer = components.GetComponent<CApplicationPlayer>();
+ bool isPlayingVideo{appPlayer->IsPlayingVideo()};
+ if (isPlayingVideo)
+ { // don't allow resolution switching if we are playing a video
+
+ appPlayer->TriggerUpdateResolution();
+
+ m_iCurRes = 0;
+ m_Res.push_back(CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution());
+ SET_CONTROL_VISIBLE(CONTROL_VIDEO);
+ }
+ else
+ {
+ SET_CONTROL_HIDDEN(CONTROL_VIDEO);
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(m_Res);
+ // find our starting resolution
+ m_iCurRes = FindCurrentResolution();
+ }
+
+ // Setup the first control
+ m_iControl = CONTROL_TOP_LEFT;
+
+ m_isSubtitleBarEnabled =
+ !(CServiceBroker::GetSettingsComponent()->GetSubtitlesSettings()->GetAlignment() !=
+ SUBTITLES::Align::MANUAL &&
+ isPlayingVideo);
+
+ ResetControls();
+ return true;
+ }
+ break;
+ case GUI_MSG_CLICKED:
+ {
+ // On click event select the next control
+ NextControl();
+ }
+ break;
+ case GUI_MSG_NOTIFY_ALL:
+ {
+ if (message.GetParam1() == GUI_MSG_WINDOW_RESIZE &&
+ message.GetSenderId() != WINDOW_SCREEN_CALIBRATION && IsActive())
+ {
+ m_Res.clear();
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions(m_Res);
+ m_iCurRes = FindCurrentResolution();
+ ResetControls();
+ }
+ }
+ break;
+ // send before touch for requesting gesture features - we don't want this
+ // it would result in unfocus in the onmessage below ...
+ case GUI_MSG_GESTURE_NOTIFY:
+ // send after touch for unfocussing - we don't want this in this window!
+ case GUI_MSG_UNFOCUS_ALL:
+ return true;
+ break;
+ }
+ return CGUIWindow::OnMessage(message);
+}
+
+unsigned int CGUIWindowSettingsScreenCalibration::FindCurrentResolution()
+{
+ RESOLUTION curRes = CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution();
+ for (size_t i = 0; i < m_Res.size(); i++)
+ {
+ // If it's a CUSTOM (monitor) resolution, then CServiceBroker::GetWinSystem()->GetGfxContext().GetAllowedResolutions()
+ // returns just one entry with CUSTOM in it. Update that entry to point to the current
+ // CUSTOM resolution.
+ if (curRes >= RES_CUSTOM)
+ {
+ if (m_Res[i] == RES_CUSTOM)
+ {
+ m_Res[i] = curRes;
+ return i;
+ }
+ }
+ else if (m_Res[i] == CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution())
+ return i;
+ }
+ CLog::Log(LOGERROR, "CALIBRATION: Reported current resolution: {}",
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetVideoResolution());
+ CLog::Log(LOGERROR,
+ "CALIBRATION: Could not determine current resolution, falling back to default");
+ return 0;
+}
+
+void CGUIWindowSettingsScreenCalibration::NextControl()
+{ // set the old control invisible and not focused, and choose the next control
+ CGUIControl* pControl = GetControl(m_iControl);
+ if (pControl)
+ {
+ pControl->SetVisible(false);
+ pControl->SetFocus(false);
+ }
+ // If the current control is the reset button
+ // ask to reset the calibration settings
+ if (m_iControl == CONTROL_RESET)
+ {
+ ResetCalibration();
+ }
+ // switch to the next control
+ m_iControl++;
+ if (m_iControl > CONTROL_RESET)
+ m_iControl = CONTROL_TOP_LEFT;
+ // enable the new control
+ EnableControl(m_iControl);
+}
+
+void CGUIWindowSettingsScreenCalibration::EnableControl(int iControl)
+{
+ SET_CONTROL_VISIBLE(CONTROL_TOP_LEFT);
+ SET_CONTROL_VISIBLE(CONTROL_BOTTOM_RIGHT);
+ SET_CONTROL_VISIBLE(CONTROL_SUBTITLES);
+ SET_CONTROL_VISIBLE(CONTROL_PIXEL_RATIO);
+ SET_CONTROL_VISIBLE(CONTROL_RESET);
+ SET_CONTROL_FOCUS(iControl, 0);
+}
+
+void CGUIWindowSettingsScreenCalibration::ResetControls()
+{
+ // disable the video control, so that our other controls take mouse clicks etc.
+ CONTROL_DISABLE(CONTROL_VIDEO);
+ // disable the UI calibration for our controls
+ // and set their limits
+ // also, set them to invisible if they don't have focus
+ CGUIMoverControl* pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_TOP_LEFT));
+ RESOLUTION_INFO info =
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(m_Res[m_iCurRes]);
+
+ m_subtitleVerticalMargin = static_cast<int>(
+ static_cast<float>(info.iHeight) / 100 *
+ CServiceBroker::GetSettingsComponent()->GetSubtitlesSettings()->GetVerticalMarginPerc());
+
+ if (pControl)
+ {
+ pControl->SetLimits(-info.iWidth / 4, -info.iHeight / 4, info.iWidth / 4, info.iHeight / 4);
+ auto& size = m_controlsSize[CONTROL_TOP_LEFT];
+ pControl->SetHeight(size.first / DEFAULT_GUI_HEIGHT * info.iHeight);
+ pControl->SetWidth(size.second / DEFAULT_GUI_WIDTH * info.iWidth);
+ pControl->SetPosition(static_cast<float>(info.Overscan.left),
+ static_cast<float>(info.Overscan.top));
+ pControl->SetLocation(info.Overscan.left, info.Overscan.top, false);
+ }
+ pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_BOTTOM_RIGHT));
+ if (pControl)
+ {
+ pControl->SetLimits(info.iWidth * 3 / 4, info.iHeight * 3 / 4, info.iWidth * 5 / 4,
+ info.iHeight * 5 / 4);
+ auto& size = m_controlsSize[CONTROL_BOTTOM_RIGHT];
+ pControl->SetHeight(size.first / DEFAULT_GUI_HEIGHT * info.iHeight);
+ pControl->SetWidth(size.second / DEFAULT_GUI_WIDTH * info.iWidth);
+ pControl->SetPosition(static_cast<float>(info.Overscan.right) - pControl->GetWidth(),
+ static_cast<float>(info.Overscan.bottom) - pControl->GetHeight());
+ pControl->SetLocation(info.Overscan.right, info.Overscan.bottom, false);
+ }
+ // Subtitles and OSD controls can only move up and down
+ pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_SUBTITLES));
+ if (pControl)
+ {
+ auto& size = m_controlsSize[CONTROL_SUBTITLES];
+ float scaledHeight = size.first / DEFAULT_GUI_HEIGHT * info.iHeight;
+ float scaledSpace =
+ static_cast<float>(CONTROL_SUBTITLES_SPACE) / DEFAULT_GUI_HEIGHT * info.iHeight;
+ m_subtitlesHalfSpace = static_cast<int>(scaledSpace / 2);
+ int barHeight = static_cast<int>(scaledHeight - scaledSpace);
+ pControl->SetLimits(0, m_subtitlesHalfSpace + barHeight + info.Overscan.top, 0,
+ info.Overscan.bottom + m_subtitlesHalfSpace);
+ pControl->SetHeight(scaledHeight);
+ pControl->SetWidth(size.second / DEFAULT_GUI_WIDTH * info.iWidth);
+ // If the vertical margin has been changed from the previous calibration,
+ // the text bar could appear offscreen, then force move to visible area
+ if (info.iSubtitles - m_subtitleVerticalMargin > info.iHeight)
+ info.iSubtitles = info.Overscan.bottom;
+ // We want the text to be at the base of the bar,
+ // then we shift the position to include the vertical margin
+ pControl->SetPosition((info.iWidth - pControl->GetWidth()) * 0.5f,
+ info.iSubtitles - pControl->GetHeight() + m_subtitlesHalfSpace -
+ m_subtitleVerticalMargin);
+ pControl->SetLocation(0, info.iSubtitles + m_subtitlesHalfSpace - m_subtitleVerticalMargin,
+ false);
+ pControl->SetEnabled(m_isSubtitleBarEnabled);
+ }
+ // The pixel ratio control
+ CGUIResizeControl* pResize = dynamic_cast<CGUIResizeControl*>(GetControl(CONTROL_PIXEL_RATIO));
+ if (pResize)
+ {
+ pResize->SetLimits(info.iWidth * 0.25f, info.iHeight * 0.5f, info.iWidth * 0.75f,
+ info.iHeight * 0.5f);
+ pResize->SetHeight(info.iHeight * 0.5f);
+ pResize->SetWidth(pResize->GetHeight() / info.fPixelRatio);
+ pResize->SetPosition((info.iWidth - pResize->GetWidth()) / 2,
+ (info.iHeight - pResize->GetHeight()) / 2);
+ }
+ // The calibration reset
+ pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_RESET));
+ if (pControl)
+ {
+ auto& size = m_controlsSize[CONTROL_RESET];
+ pControl->SetHeight(size.first / DEFAULT_GUI_HEIGHT * info.iHeight);
+ pControl->SetWidth(size.second / DEFAULT_GUI_WIDTH * info.iWidth);
+ float posX = 0;
+ float posY = info.iHeight - pControl->GetHeight();
+ pControl->SetLimits(posX, posY, posX, posY);
+ pControl->SetPosition(posX, posY);
+ pControl->SetLocation(posX, posY, false);
+ }
+ // Enable the default control
+ EnableControl(m_iControl);
+}
+
+bool CGUIWindowSettingsScreenCalibration::UpdateFromControl(int iControl)
+{
+ RESOLUTION_INFO info =
+ CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(m_Res[m_iCurRes]);
+ RESOLUTION_INFO infoPrev = info;
+ std::string labelDescription;
+ std::string labelValue;
+
+ if (iControl == CONTROL_PIXEL_RATIO)
+ {
+ CGUIControl* pControl = GetControl(CONTROL_PIXEL_RATIO);
+ if (pControl)
+ {
+ float fWidth = pControl->GetWidth();
+ float fHeight = pControl->GetHeight();
+ info.fPixelRatio = fHeight / fWidth;
+ // recenter our control...
+ pControl->SetPosition((static_cast<float>(info.iWidth) - pControl->GetWidth()) / 2,
+ (static_cast<float>(info.iHeight) - pControl->GetHeight()) / 2);
+ labelDescription = StringUtils::Format("[B]{}[/B][CR]{}", g_localizeStrings.Get(272),
+ g_localizeStrings.Get(273));
+ labelValue = StringUtils::Format("{:5.3f}", info.fPixelRatio);
+ labelValue = StringUtils::Format(g_localizeStrings.Get(20327), labelValue);
+ }
+ }
+ else
+ {
+ CGUIMoverControl* pControl = dynamic_cast<CGUIMoverControl*>(GetControl(iControl));
+ if (pControl)
+ {
+ switch (iControl)
+ {
+ case CONTROL_TOP_LEFT:
+ {
+ info.Overscan.left = pControl->GetXLocation();
+ info.Overscan.top = pControl->GetYLocation();
+ labelDescription = StringUtils::Format("[B]{}[/B][CR]{}", g_localizeStrings.Get(274),
+ g_localizeStrings.Get(276));
+ labelValue =
+ StringUtils::Format("{}, {}", pControl->GetXLocation(), pControl->GetYLocation());
+ labelValue = StringUtils::Format(g_localizeStrings.Get(20327), labelValue);
+ // Update reset control position
+ CGUIMoverControl* pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_RESET));
+ if (pControl)
+ {
+ float posX = info.Overscan.left;
+ float posY = info.Overscan.bottom - pControl->GetHeight();
+ pControl->SetLimits(posX, posY, posX, posY);
+ pControl->SetPosition(posX, posY);
+ pControl->SetLocation(posX, posY, false);
+ }
+ }
+ break;
+
+ case CONTROL_BOTTOM_RIGHT:
+ {
+ info.Overscan.right = pControl->GetXLocation();
+ info.Overscan.bottom = pControl->GetYLocation();
+ int iXOff1 = info.iWidth - pControl->GetXLocation();
+ int iYOff1 = info.iHeight - pControl->GetYLocation();
+ labelDescription = StringUtils::Format("[B]{}[/B][CR]{}", g_localizeStrings.Get(275),
+ g_localizeStrings.Get(276));
+ labelValue = StringUtils::Format("{}, {}", iXOff1, iYOff1);
+ labelValue = StringUtils::Format(g_localizeStrings.Get(20327), labelValue);
+ // Update reset control position
+ pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_RESET));
+ if (pControl)
+ {
+ float posX = info.Overscan.left;
+ float posY = info.Overscan.bottom - pControl->GetHeight();
+ pControl->SetLimits(posX, posY, posX, posY);
+ pControl->SetPosition(posX, posY);
+ pControl->SetLocation(posX, posY, false);
+ }
+ }
+ break;
+
+ case CONTROL_SUBTITLES:
+ {
+ if (m_isSubtitleBarEnabled)
+ {
+ info.iSubtitles =
+ pControl->GetYLocation() - m_subtitlesHalfSpace + m_subtitleVerticalMargin;
+
+ labelDescription = StringUtils::Format("[B]{}[/B][CR]{}", g_localizeStrings.Get(277),
+ g_localizeStrings.Get(278));
+ labelValue = StringUtils::Format(g_localizeStrings.Get(39184), info.iSubtitles,
+ info.iSubtitles - m_subtitleVerticalMargin);
+ }
+ else
+ {
+ labelDescription = StringUtils::Format("[B]{}[/B][CR]{}", g_localizeStrings.Get(277),
+ g_localizeStrings.Get(39189));
+ }
+ }
+ break;
+
+ case CONTROL_RESET:
+ {
+ labelDescription = g_localizeStrings.Get(20325);
+ }
+ break;
+ }
+ }
+ }
+
+ SET_CONTROL_LABEL(CONTROL_LABEL_DESCRIPTION, labelDescription);
+ SET_CONTROL_LABEL(CONTROL_LABEL_VALUE, labelValue);
+
+ // Set resolution info text
+ std::string resInfo;
+ if (CServiceBroker::GetWinSystem()->IsFullScreen())
+ {
+ resInfo =
+ StringUtils::Format("{} {}x{}@{:.2f} - {}", g_localizeStrings.Get(13287), info.iScreenWidth,
+ info.iScreenHeight, info.fRefreshRate, g_localizeStrings.Get(244));
+ }
+ else
+ {
+ resInfo = StringUtils::Format("{} {}x{} - {}", g_localizeStrings.Get(13287), info.iScreenWidth,
+ info.iScreenHeight, g_localizeStrings.Get(242));
+ }
+ SET_CONTROL_LABEL(CONTROL_LABEL_RES, resInfo);
+
+ // Detect overscan changes
+ bool isOverscanChanged = info.Overscan != infoPrev.Overscan;
+
+ // Adjust subtitle bar position due to overscan changes
+ if (isOverscanChanged)
+ {
+ CGUIMoverControl* pControl = dynamic_cast<CGUIMoverControl*>(GetControl(CONTROL_SUBTITLES));
+ if (pControl)
+ {
+ // Keep the subtitle bar within the overscan boundary
+ if (info.Overscan.bottom + m_subtitleVerticalMargin < info.iSubtitles)
+ {
+ info.iSubtitles = info.Overscan.bottom + m_subtitleVerticalMargin;
+
+ // We want the text to be at the base of the bar,
+ // then we shift the position to include the vertical margin
+ pControl->SetPosition((info.iWidth - pControl->GetWidth()) * 0.5f,
+ info.iSubtitles - pControl->GetHeight() + m_subtitlesHalfSpace -
+ m_subtitleVerticalMargin);
+ pControl->SetLocation(0, info.iSubtitles + m_subtitlesHalfSpace - m_subtitleVerticalMargin,
+ false);
+ }
+
+ // Recalculate limits based on overscan values
+ const auto& size = m_controlsSize[CONTROL_SUBTITLES];
+ const float scaledHeight = size.first / DEFAULT_GUI_HEIGHT * info.iHeight;
+ const float scaledSpace =
+ static_cast<float>(CONTROL_SUBTITLES_SPACE) / DEFAULT_GUI_HEIGHT * info.iHeight;
+
+ m_subtitlesHalfSpace = static_cast<int>(scaledSpace / 2);
+ const int barHeight = static_cast<int>(scaledHeight - scaledSpace);
+
+ pControl->SetLimits(0, m_subtitlesHalfSpace + barHeight + info.Overscan.top, 0,
+ info.Overscan.bottom + m_subtitlesHalfSpace);
+ }
+ }
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetResInfo(m_Res[m_iCurRes], info);
+
+ return isOverscanChanged;
+}
+
+void CGUIWindowSettingsScreenCalibration::FrameMove()
+{
+ m_iControl = GetFocusedControlID();
+ if (m_iControl >= 0)
+ {
+ if (UpdateFromControl(m_iControl))
+ {
+ // Send GUI_MSG_WINDOW_RESIZE to rescale font size/aspect for label controls
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(
+ GUI_MSG_NOTIFY_ALL, WINDOW_SCREEN_CALIBRATION, 0, GUI_MSG_WINDOW_RESIZE);
+ }
+ }
+ else
+ {
+ SET_CONTROL_LABEL(CONTROL_LABEL_DESCRIPTION, "");
+ SET_CONTROL_LABEL(CONTROL_LABEL_VALUE, "");
+ SET_CONTROL_LABEL(CONTROL_LABEL_RES, "");
+ }
+ CGUIWindow::FrameMove();
+}
+
+void CGUIWindowSettingsScreenCalibration::DoProcess(unsigned int currentTime,
+ CDirtyRegionList& dirtyregions)
+{
+ MarkDirtyRegion();
+
+ for (int i = CONTROL_TOP_LEFT; i <= CONTROL_RESET; i++)
+ SET_CONTROL_HIDDEN(i);
+ m_needsScaling = true;
+ CGUIWindow::DoProcess(currentTime, dirtyregions);
+ m_needsScaling = false;
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(m_Res[m_iCurRes], false);
+ CServiceBroker::GetWinSystem()->GetGfxContext().AddGUITransform();
+
+ // process the movers etc.
+ for (int i = CONTROL_TOP_LEFT; i <= CONTROL_RESET; i++)
+ {
+ SET_CONTROL_VISIBLE(i);
+ CGUIControl* control = GetControl(i);
+ if (control)
+ control->DoProcess(currentTime, dirtyregions);
+ }
+ CServiceBroker::GetWinSystem()->GetGfxContext().RemoveTransform();
+}
+
+void CGUIWindowSettingsScreenCalibration::DoRender()
+{
+ // we set that we need scaling here to render so that anything else on screen scales correctly
+ m_needsScaling = true;
+ CGUIWindow::DoRender();
+ m_needsScaling = false;
+}
diff --git a/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.h b/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.h
new file mode 100644
index 0000000..dd3e6b1
--- /dev/null
+++ b/xbmc/settings/windows/GUIWindowSettingsScreenCalibration.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "guilib/GUIWindow.h"
+
+#include <map>
+#include <utility>
+#include <vector>
+
+class CGUIWindowSettingsScreenCalibration : public CGUIWindow
+{
+public:
+ CGUIWindowSettingsScreenCalibration(void);
+ ~CGUIWindowSettingsScreenCalibration(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction& action) override;
+ void DoProcess(unsigned int currentTime, CDirtyRegionList& dirtyregions) override;
+ void FrameMove() override;
+ void DoRender() override;
+ void AllocResources(bool forceLoad = false) override;
+ void FreeResources(bool forceUnLoad = false) override;
+
+protected:
+ unsigned int FindCurrentResolution();
+ void NextControl();
+ void ResetControls();
+ void EnableControl(int iControl);
+ bool UpdateFromControl(int iControl);
+ void ResetCalibration();
+ unsigned int m_iCurRes;
+ std::vector<RESOLUTION> m_Res;
+ int m_iControl;
+ float m_fPixelRatioBoxHeight;
+
+private:
+ std::map<int, std::pair<float, float>> m_controlsSize;
+ int m_subtitlesHalfSpace{0};
+ int m_subtitleVerticalMargin{0};
+ bool m_isSubtitleBarEnabled{false};
+};