summaryrefslogtreecommitdiffstats
path: root/xbmc/profiles
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/profiles
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/profiles')
-rw-r--r--xbmc/profiles/CMakeLists.txt7
-rw-r--r--xbmc/profiles/Profile.cpp126
-rw-r--r--xbmc/profiles/Profile.h98
-rw-r--r--xbmc/profiles/ProfileManager.cpp747
-rw-r--r--xbmc/profiles/ProfileManager.h222
-rw-r--r--xbmc/profiles/dialogs/CMakeLists.txt7
-rw-r--r--xbmc/profiles/dialogs/GUIDialogLockSettings.cpp320
-rw-r--r--xbmc/profiles/dialogs/GUIDialogLockSettings.h53
-rw-r--r--xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp397
-rw-r--r--xbmc/profiles/dialogs/GUIDialogProfileSettings.h62
-rw-r--r--xbmc/profiles/windows/CMakeLists.txt5
-rw-r--r--xbmc/profiles/windows/GUIWindowSettingsProfile.cpp269
-rw-r--r--xbmc/profiles/windows/GUIWindowSettingsProfile.h35
13 files changed, 2348 insertions, 0 deletions
diff --git a/xbmc/profiles/CMakeLists.txt b/xbmc/profiles/CMakeLists.txt
new file mode 100644
index 0000000..1756c0c
--- /dev/null
+++ b/xbmc/profiles/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES Profile.cpp
+ ProfileManager.cpp)
+
+set(HEADERS Profile.h
+ ProfileManager.h)
+
+core_add_library(profiles)
diff --git a/xbmc/profiles/Profile.cpp b/xbmc/profiles/Profile.cpp
new file mode 100644
index 0000000..5665d25
--- /dev/null
+++ b/xbmc/profiles/Profile.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 "Profile.h"
+
+#include "XBDateTime.h"
+#include "utils/XMLUtils.h"
+
+CProfile::CLock::CLock(LockType type, const std::string &password):
+ code(password)
+{
+ programs = false;
+ pictures = false;
+ files = false;
+ video = false;
+ music = false;
+ settings = LOCK_LEVEL::NONE;
+ addonManager = false;
+ games = false;
+ mode = type;
+}
+
+void CProfile::CLock::Validate()
+{
+ if (mode != LOCK_MODE_EVERYONE && (code == "-" || code.empty()))
+ mode = LOCK_MODE_EVERYONE;
+
+ if (code.empty() || mode == LOCK_MODE_EVERYONE)
+ code = "-";
+}
+
+CProfile::CProfile(const std::string &directory, const std::string &name, const int id):
+ m_directory(directory),
+ m_name(name)
+{
+ m_id = id;
+ m_bDatabases = true;
+ m_bCanWrite = true;
+ m_bSources = true;
+ m_bCanWriteSources = true;
+ m_bAddons = true;
+}
+
+CProfile::~CProfile(void) = default;
+
+void CProfile::setDate()
+{
+ const CDateTime now = CDateTime::GetCurrentDateTime();
+ std::string strDate = now.GetAsLocalizedDate(false);
+ std::string strTime = now.GetAsLocalizedTime(TIME_FORMAT_GUESS);
+ if (strDate.empty() || strTime.empty())
+ setDate("-");
+ else
+ setDate(strDate+" - "+strTime);
+}
+
+void CProfile::Load(const TiXmlNode *node, int nextIdProfile)
+{
+ if (!XMLUtils::GetInt(node, "id", m_id))
+ m_id = nextIdProfile;
+
+ XMLUtils::GetString(node, "name", m_name);
+ XMLUtils::GetPath(node, "directory", m_directory);
+ XMLUtils::GetPath(node, "thumbnail", m_thumb);
+ XMLUtils::GetBoolean(node, "hasdatabases", m_bDatabases);
+ XMLUtils::GetBoolean(node, "canwritedatabases", m_bCanWrite);
+ XMLUtils::GetBoolean(node, "hassources", m_bSources);
+ XMLUtils::GetBoolean(node, "canwritesources", m_bCanWriteSources);
+ XMLUtils::GetBoolean(node, "lockaddonmanager", m_locks.addonManager);
+ int settings = m_locks.settings;
+ XMLUtils::GetInt(node, "locksettings", settings);
+ m_locks.settings = (LOCK_LEVEL::SETTINGS_LOCK)settings;
+ XMLUtils::GetBoolean(node, "lockfiles", m_locks.files);
+ XMLUtils::GetBoolean(node, "lockmusic", m_locks.music);
+ XMLUtils::GetBoolean(node, "lockvideo", m_locks.video);
+ XMLUtils::GetBoolean(node, "lockpictures", m_locks.pictures);
+ XMLUtils::GetBoolean(node, "lockprograms", m_locks.programs);
+ XMLUtils::GetBoolean(node, "lockgames", m_locks.games);
+
+ int lockMode = m_locks.mode;
+ XMLUtils::GetInt(node, "lockmode", lockMode);
+ m_locks.mode = (LockType)lockMode;
+ if (m_locks.mode > LOCK_MODE_QWERTY || m_locks.mode < LOCK_MODE_EVERYONE)
+ m_locks.mode = LOCK_MODE_EVERYONE;
+
+ XMLUtils::GetString(node, "lockcode", m_locks.code);
+ XMLUtils::GetString(node, "lastdate", m_date);
+}
+
+void CProfile::Save(TiXmlNode *root) const
+{
+ TiXmlElement profileNode("profile");
+ TiXmlNode *node = root->InsertEndChild(profileNode);
+
+ XMLUtils::SetInt(node, "id", m_id);
+ XMLUtils::SetString(node, "name", m_name);
+ XMLUtils::SetPath(node, "directory", m_directory);
+ XMLUtils::SetPath(node, "thumbnail", m_thumb);
+ XMLUtils::SetBoolean(node, "hasdatabases", m_bDatabases);
+ XMLUtils::SetBoolean(node, "canwritedatabases", m_bCanWrite);
+ XMLUtils::SetBoolean(node, "hassources", m_bSources);
+ XMLUtils::SetBoolean(node, "canwritesources", m_bCanWriteSources);
+ XMLUtils::SetBoolean(node, "lockaddonmanager", m_locks.addonManager);
+ XMLUtils::SetInt(node, "locksettings", m_locks.settings);
+ XMLUtils::SetBoolean(node, "lockfiles", m_locks.files);
+ XMLUtils::SetBoolean(node, "lockmusic", m_locks.music);
+ XMLUtils::SetBoolean(node, "lockvideo", m_locks.video);
+ XMLUtils::SetBoolean(node, "lockpictures", m_locks.pictures);
+ XMLUtils::SetBoolean(node, "lockprograms", m_locks.programs);
+ XMLUtils::SetBoolean(node, "lockgames", m_locks.games);
+
+ XMLUtils::SetInt(node, "lockmode", m_locks.mode);
+ XMLUtils::SetString(node,"lockcode", m_locks.code);
+ XMLUtils::SetString(node, "lastdate", m_date);
+}
+
+void CProfile::SetLocks(const CProfile::CLock &locks)
+{
+ m_locks = locks;
+ m_locks.Validate();
+}
diff --git a/xbmc/profiles/Profile.h b/xbmc/profiles/Profile.h
new file mode 100644
index 0000000..42d4fba
--- /dev/null
+++ b/xbmc/profiles/Profile.h
@@ -0,0 +1,98 @@
+/*
+ * 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 "LockType.h"
+#include "SettingsLock.h"
+
+#include <string>
+#include <vector>
+
+class TiXmlNode;
+
+class CProfile
+{
+public:
+ /*! \brief Class for handling lock status
+ */
+ class CLock
+ {
+ public:
+ CLock(LockType type = LOCK_MODE_EVERYONE, const std::string &password = "");
+ void Validate();
+
+ LockType mode;
+ std::string code;
+ bool addonManager;
+ LOCK_LEVEL::SETTINGS_LOCK settings;
+ bool music;
+ bool video;
+ bool files;
+ bool pictures;
+ bool programs;
+ bool games;
+ };
+
+ CProfile(const std::string &directory = "", const std::string &name = "", const int id = -1);
+ ~CProfile(void);
+
+ void Load(const TiXmlNode *node, int nextIdProfile);
+ void Save(TiXmlNode *root) const;
+
+ const std::string& getDate() const { return m_date;}
+ int getId() const { return m_id; }
+ const std::string& getName() const { return m_name;}
+ const std::string& getDirectory() const { return m_directory;}
+ const std::string& getThumb() const { return m_thumb;}
+ const std::string& getLockCode() const { return m_locks.code;}
+ LockType getLockMode() const { return m_locks.mode; }
+
+ bool hasDatabases() const { return m_bDatabases; }
+ bool canWriteDatabases() const { return m_bCanWrite; }
+ bool hasSources() const { return m_bSources; }
+ bool canWriteSources() const { return m_bCanWriteSources; }
+ bool hasAddons() const { return m_bAddons; }
+ /**
+ \brief Returns which settings levels are locked for the current profile
+ \sa LOCK_LEVEL::SETTINGS_LOCK
+ */
+ LOCK_LEVEL::SETTINGS_LOCK settingsLockLevel() const { return m_locks.settings; }
+ bool addonmanagerLocked() const { return m_locks.addonManager; }
+ bool musicLocked() const { return m_locks.music; }
+ bool videoLocked() const { return m_locks.video; }
+ bool picturesLocked() const { return m_locks.pictures; }
+ bool filesLocked() const { return m_locks.files; }
+ bool programsLocked() const { return m_locks.programs; }
+ bool gamesLocked() const { return m_locks.games; }
+ const CLock &GetLocks() const { return m_locks; }
+
+ void setName(const std::string& name) {m_name = name;}
+ void setDirectory(const std::string& directory) {m_directory = directory;}
+ void setDate(const std::string& strDate) { m_date = strDate;}
+ void setDate();
+ void setThumb(const std::string& thumb) {m_thumb = thumb;}
+ void setDatabases(bool bHas) { m_bDatabases = bHas; }
+ void setWriteDatabases(bool bCan) { m_bCanWrite = bCan; }
+ void setSources(bool bHas) { m_bSources = bHas; }
+ void setWriteSources(bool bCan) { m_bCanWriteSources = bCan; }
+ void SetLocks(const CLock &locks);
+
+private:
+ std::string m_directory;
+ int m_id;
+ std::string m_name;
+ std::string m_date;
+ std::string m_thumb;
+ bool m_bDatabases;
+ bool m_bCanWrite;
+ bool m_bSources;
+ bool m_bCanWriteSources;
+ bool m_bAddons;
+ CLock m_locks;
+};
diff --git a/xbmc/profiles/ProfileManager.cpp b/xbmc/profiles/ProfileManager.cpp
new file mode 100644
index 0000000..e4e5bec
--- /dev/null
+++ b/xbmc/profiles/ProfileManager.cpp
@@ -0,0 +1,747 @@
+/*
+ * 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 "ProfileManager.h"
+
+#include "DatabaseManager.h"
+#include "FileItem.h"
+#include "GUIInfoManager.h"
+#include "GUIPassword.h"
+#include "PasswordManager.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/Skin.h"
+#include "application/Application.h" //! @todo Remove me
+#include "application/ApplicationComponents.h"
+#include "application/ApplicationPowerHandling.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "events/EventLog.h"
+#include "events/EventLogManager.h"
+#include "filesystem/Directory.h"
+#include "filesystem/DirectoryCache.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/InputManager.h"
+#include "music/MusicLibraryQueue.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/SettingsManager.h"
+#include "threads/SingleLock.h"
+
+#include <algorithm>
+#include <mutex>
+#include <string>
+#include <vector>
+#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE)
+#include "storage/DetectDVDType.h"
+#endif
+#include "ContextMenuManager.h" //! @todo Remove me
+#include "PlayListPlayer.h" //! @todo Remove me
+#include "addons/AddonManager.h" //! @todo Remove me
+#include "addons/Service.h" //! @todo Remove me
+#include "application/Application.h" //! @todo Remove me
+#include "favourites/FavouritesService.h" //! @todo Remove me
+#include "guilib/StereoscopicsManager.h" //! @todo Remove me
+#include "interfaces/json-rpc/JSONRPC.h" //! @todo Remove me
+#include "network/Network.h" //! @todo Remove me
+#include "network/NetworkServices.h" //! @todo Remove me
+#include "pvr/PVRManager.h" //! @todo Remove me
+#include "utils/FileUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/XMLUtils.h"
+#include "utils/log.h"
+#include "video/VideoLibraryQueue.h" //! @todo Remove me
+#include "weather/WeatherManager.h" //! @todo Remove me
+
+//! @todo
+//! eventually the profile should dictate where special://masterprofile/ is
+//! but for now it makes sense to leave all the profile settings in a user
+//! writeable location like special://masterprofile/
+#define PROFILES_FILE "special://masterprofile/profiles.xml"
+
+#define XML_PROFILES "profiles"
+#define XML_AUTO_LOGIN "autologin"
+#define XML_LAST_LOADED "lastloaded"
+#define XML_LOGIN_SCREEN "useloginscreen"
+#define XML_NEXTID "nextIdProfile"
+#define XML_PROFILE "profile"
+
+using namespace XFILE;
+
+static CProfile EmptyProfile;
+
+CProfileManager::CProfileManager() : m_eventLogs(new CEventLogManager)
+{
+}
+
+CProfileManager::~CProfileManager() = default;
+
+void CProfileManager::Initialize(const std::shared_ptr<CSettings>& settings)
+{
+ m_settings = settings;
+
+ if (m_settings->IsLoaded())
+ OnSettingsLoaded();
+
+ m_settings->GetSettingsManager()->RegisterSettingsHandler(this);
+
+ std::set<std::string> settingSet = {
+ CSettings::SETTING_EVENTLOG_SHOW
+ };
+
+ m_settings->GetSettingsManager()->RegisterCallback(this, settingSet);
+}
+
+void CProfileManager::Uninitialize()
+{
+ m_settings->GetSettingsManager()->UnregisterCallback(this);
+ m_settings->GetSettingsManager()->UnregisterSettingsHandler(this);
+}
+
+void CProfileManager::OnSettingsLoaded()
+{
+ // check them all
+ std::string strDir = m_settings->GetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH);
+ if (strDir == "set default" || strDir.empty())
+ {
+ strDir = "special://profile/playlists/";
+ m_settings->SetString(CSettings::SETTING_SYSTEM_PLAYLISTSPATH, strDir);
+ }
+
+ CDirectory::Create(strDir);
+ CDirectory::Create(URIUtils::AddFileToFolder(strDir,"music"));
+ CDirectory::Create(URIUtils::AddFileToFolder(strDir,"video"));
+ CDirectory::Create(URIUtils::AddFileToFolder(strDir,"mixed"));
+}
+
+void CProfileManager::OnSettingsSaved() const
+{
+ // save mastercode
+ Save();
+}
+
+void CProfileManager::OnSettingsCleared()
+{
+ Clear();
+}
+
+bool CProfileManager::Load()
+{
+ bool ret = true;
+ const std::string file = PROFILES_FILE;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ // clear out our profiles
+ m_profiles.clear();
+
+ if (CFile::Exists(file))
+ {
+ CXBMCTinyXML profilesDoc;
+ if (profilesDoc.LoadFile(file))
+ {
+ const TiXmlElement *rootElement = profilesDoc.RootElement();
+ if (rootElement && StringUtils::EqualsNoCase(rootElement->Value(), XML_PROFILES))
+ {
+ XMLUtils::GetUInt(rootElement, XML_LAST_LOADED, m_lastUsedProfile);
+ XMLUtils::GetBoolean(rootElement, XML_LOGIN_SCREEN, m_usingLoginScreen);
+ XMLUtils::GetInt(rootElement, XML_AUTO_LOGIN, m_autoLoginProfile);
+ XMLUtils::GetInt(rootElement, XML_NEXTID, m_nextProfileId);
+
+ std::string defaultDir("special://home/userdata");
+ if (!CDirectory::Exists(defaultDir))
+ defaultDir = "special://xbmc/userdata";
+
+ const TiXmlElement* pProfile = rootElement->FirstChildElement(XML_PROFILE);
+ while (pProfile)
+ {
+ CProfile profile(defaultDir);
+ profile.Load(pProfile, GetNextProfileId());
+ AddProfile(profile);
+
+ pProfile = pProfile->NextSiblingElement(XML_PROFILE);
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CProfileManager: error loading {}, no <profiles> node", file);
+ ret = false;
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "CProfileManager: error loading {}, Line {}\n{}", file,
+ profilesDoc.ErrorRow(), profilesDoc.ErrorDesc());
+ ret = false;
+ }
+ }
+ if (!ret)
+ {
+ CLog::Log(LOGERROR,
+ "Failed to load profile - might be corrupted - falling back to master profile");
+ m_profiles.clear();
+ CFile::Delete(file);
+
+ ret = true;
+ }
+
+ if (m_profiles.empty())
+ { // add the master user
+ CProfile profile("special://masterprofile/", "Master user", 0);
+ AddProfile(profile);
+ }
+
+ // check the validity of the previous profile index
+ if (m_lastUsedProfile >= m_profiles.size())
+ m_lastUsedProfile = 0;
+
+ SetCurrentProfileId(m_lastUsedProfile);
+
+ // check the validity of the auto login profile index
+ if (m_autoLoginProfile < -1 || m_autoLoginProfile >= (int)m_profiles.size())
+ m_autoLoginProfile = -1;
+ else if (m_autoLoginProfile >= 0)
+ SetCurrentProfileId(m_autoLoginProfile);
+
+ // the login screen runs as the master profile, so if we're using this, we need to ensure
+ // we switch to the master profile
+ if (m_usingLoginScreen)
+ SetCurrentProfileId(0);
+
+ return ret;
+}
+
+bool CProfileManager::Save() const
+{
+ const std::string file = PROFILES_FILE;
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+ CXBMCTinyXML xmlDoc;
+ TiXmlElement xmlRootElement(XML_PROFILES);
+ TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
+ if (pRoot == nullptr)
+ return false;
+
+ XMLUtils::SetInt(pRoot, XML_LAST_LOADED, m_currentProfile);
+ XMLUtils::SetBoolean(pRoot, XML_LOGIN_SCREEN, m_usingLoginScreen);
+ XMLUtils::SetInt(pRoot, XML_AUTO_LOGIN, m_autoLoginProfile);
+ XMLUtils::SetInt(pRoot, XML_NEXTID, m_nextProfileId);
+
+ for (const auto& profile : m_profiles)
+ profile.Save(pRoot);
+
+ // save the file
+ return xmlDoc.SaveFile(file);
+}
+
+void CProfileManager::Clear()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_usingLoginScreen = false;
+ m_profileLoadedForLogin = false;
+ m_previousProfileLoadedForLogin = false;
+ m_lastUsedProfile = 0;
+ m_nextProfileId = 0;
+ SetCurrentProfileId(0);
+ m_profiles.clear();
+}
+
+void CProfileManager::PrepareLoadProfile(unsigned int profileIndex)
+{
+ CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager();
+ ADDON::CServiceAddonManager &serviceAddons = CServiceBroker::GetServiceAddons();
+ PVR::CPVRManager &pvrManager = CServiceBroker::GetPVRManager();
+ CNetworkBase &networkManager = CServiceBroker::GetNetwork();
+
+ contextMenuManager.Deinit();
+
+ serviceAddons.Stop();
+
+ // stop PVR related services
+ pvrManager.Stop();
+
+ if (profileIndex != 0 || !IsMasterProfile())
+ networkManager.NetworkMessage(CNetworkBase::SERVICES_DOWN, 1);
+}
+
+bool CProfileManager::LoadProfile(unsigned int index)
+{
+ PrepareLoadProfile(index);
+
+ if (index == 0 && IsMasterProfile())
+ {
+ CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_HOME);
+ if (pWindow)
+ pWindow->ResetControlStates();
+
+ UpdateCurrentProfileDate();
+ FinalizeLoadProfile();
+
+ return true;
+ }
+
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // check if the index is valid or not
+ if (index >= m_profiles.size())
+ return false;
+
+ // check if the profile is already active
+ if (m_currentProfile == index)
+ return true;
+
+ // save any settings of the currently used skin but only if the (master)
+ // profile hasn't just been loaded as a temporary profile for login
+ if (g_SkinInfo != nullptr && !m_previousProfileLoadedForLogin)
+ g_SkinInfo->SaveSettings();
+
+ // @todo: why is m_settings not used here?
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ // unload any old settings
+ settings->Unload();
+
+ SetCurrentProfileId(index);
+ m_previousProfileLoadedForLogin = false;
+
+ // load the new settings
+ if (!settings->Load())
+ {
+ CLog::Log(LOGFATAL, "CProfileManager: unable to load settings for profile \"{}\"",
+ m_profiles.at(index).getName());
+ return false;
+ }
+ settings->SetLoaded();
+
+ CreateProfileFolders();
+
+ CServiceBroker::GetDatabaseManager().Initialize();
+ CServiceBroker::GetInputManager().LoadKeymaps();
+
+ CServiceBroker::GetInputManager().SetMouseEnabled(settings->GetBool(CSettings::SETTING_INPUT_ENABLEMOUSE));
+
+ CGUIComponent* gui = CServiceBroker::GetGUI();
+ if (gui)
+ {
+ CGUIInfoManager& infoMgr = gui->GetInfoManager();
+ infoMgr.ResetCache();
+ infoMgr.GetInfoProviders().GetGUIControlsInfoProvider().ResetContainerMovingCache();
+ infoMgr.GetInfoProviders().GetLibraryInfoProvider().ResetLibraryBools();
+ }
+
+ if (m_currentProfile != 0)
+ {
+ CXBMCTinyXML doc;
+ if (doc.LoadFile(URIUtils::AddFileToFolder(GetUserDataFolder(), "guisettings.xml")))
+ {
+ settings->LoadSetting(doc.RootElement(), CSettings::SETTING_MASTERLOCK_MAXRETRIES);
+ settings->LoadSetting(doc.RootElement(), CSettings::SETTING_MASTERLOCK_STARTUPLOCK);
+ }
+ }
+
+ CPasswordManager::GetInstance().Clear();
+
+ // to set labels - shares are reloaded
+#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE)
+ MEDIA_DETECT::CDetectDVDMedia::UpdateState();
+#endif
+
+ // init windows
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESET);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ CUtil::DeleteDirectoryCache();
+ g_directoryCache.Clear();
+
+ lock.unlock();
+
+ UpdateCurrentProfileDate();
+ FinalizeLoadProfile();
+
+ m_profileLoadedForLogin = false;
+
+ return true;
+}
+
+void CProfileManager::FinalizeLoadProfile()
+{
+ CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager();
+ ADDON::CServiceAddonManager &serviceAddons = CServiceBroker::GetServiceAddons();
+ PVR::CPVRManager &pvrManager = CServiceBroker::GetPVRManager();
+ CNetworkBase &networkManager = CServiceBroker::GetNetwork();
+ ADDON::CAddonMgr &addonManager = CServiceBroker::GetAddonMgr();
+ CWeatherManager &weatherManager = CServiceBroker::GetWeatherManager();
+ CFavouritesService &favouritesManager = CServiceBroker::GetFavouritesService();
+ PLAYLIST::CPlayListPlayer &playlistManager = CServiceBroker::GetPlaylistPlayer();
+ CStereoscopicsManager &stereoscopicsManager = CServiceBroker::GetGUI()->GetStereoscopicsManager();
+
+ if (m_lastUsedProfile != m_currentProfile)
+ {
+ playlistManager.ClearPlaylist(PLAYLIST::TYPE_VIDEO);
+ playlistManager.ClearPlaylist(PLAYLIST::TYPE_MUSIC);
+ playlistManager.SetCurrentPlaylist(PLAYLIST::TYPE_NONE);
+ }
+
+ networkManager.NetworkMessage(CNetworkBase::SERVICES_UP, 1);
+
+ // reload the add-ons, or we will first load all add-ons from the master account without checking disabled status
+ addonManager.ReInit();
+
+ // let CApplication know that we are logging into a new profile
+ g_application.SetLoggingIn(true);
+
+ if (!g_application.LoadLanguage(true))
+ {
+ CLog::Log(LOGFATAL, "Unable to load language for profile \"{}\"",
+ GetCurrentProfile().getName());
+ return;
+ }
+
+ weatherManager.Refresh();
+
+ JSONRPC::CJSONRPC::Initialize();
+
+ // Restart context menu manager
+ contextMenuManager.Init();
+
+ // Restart PVR services if we are not just loading the master profile for the login screen
+ if (m_previousProfileLoadedForLogin || m_currentProfile != 0 || m_lastUsedProfile == 0)
+ pvrManager.Init();
+
+ favouritesManager.ReInit(GetProfileUserDataFolder());
+
+ // Start these operations only when a profile is loaded, not on the login screen
+ if (!m_profileLoadedForLogin || (m_profileLoadedForLogin && m_lastUsedProfile == 0))
+ {
+ serviceAddons.Start();
+ g_application.UpdateLibraries();
+ }
+
+ stereoscopicsManager.Initialize();
+
+ // Load initial window
+ int firstWindow = g_SkinInfo->GetFirstWindow();
+
+ CServiceBroker::GetGUI()->GetWindowManager().ChangeActiveWindow(firstWindow);
+
+ //the user interfaces has been fully initialized, let everyone know
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, WINDOW_SETTINGS_PROFILES, 0, GUI_MSG_UI_READY);
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg);
+}
+
+void CProfileManager::LogOff()
+{
+ CNetworkBase &networkManager = CServiceBroker::GetNetwork();
+
+ g_application.StopPlaying();
+
+ if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
+ CMusicLibraryQueue::GetInstance().StopLibraryScanning();
+
+ if (CVideoLibraryQueue::GetInstance().IsRunning())
+ CVideoLibraryQueue::GetInstance().CancelAllJobs();
+
+ // Stop PVR services
+ CServiceBroker::GetPVRManager().Stop();
+
+ networkManager.NetworkMessage(CNetworkBase::SERVICES_DOWN, 1);
+
+ LoadMasterProfileForLogin();
+
+ g_passwordManager.bMasterUser = false;
+
+ auto& components = CServiceBroker::GetAppComponents();
+ const auto appPower = components.GetComponent<CApplicationPowerHandling>();
+ appPower->WakeUpScreenSaverAndDPMS();
+ CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_LOGIN_SCREEN, {}, false);
+
+ if (!CServiceBroker::GetNetwork().GetServices().StartEventServer()) // event server could be needed in some situations
+ CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(33102), g_localizeStrings.Get(33100));
+}
+
+bool CProfileManager::DeleteProfile(unsigned int index)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ const CProfile *profile = GetProfile(index);
+ if (profile == NULL)
+ return false;
+
+ CGUIDialogYesNo* dlgYesNo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO);
+ if (dlgYesNo == NULL)
+ return false;
+
+ const std::string& str = g_localizeStrings.Get(13201);
+ dlgYesNo->SetHeading(CVariant{13200});
+ dlgYesNo->SetLine(0, CVariant{StringUtils::Format(str, profile->getName())});
+ dlgYesNo->SetLine(1, CVariant{""});
+ dlgYesNo->SetLine(2, CVariant{""});
+ dlgYesNo->Open();
+
+ if (!dlgYesNo->IsConfirmed())
+ return false;
+
+ // fall back to master profile if necessary
+ if ((int)index == m_autoLoginProfile)
+ m_autoLoginProfile = 0;
+
+ // delete profile
+ std::string strDirectory = profile->getDirectory();
+ m_profiles.erase(m_profiles.begin() + index);
+
+ // fall back to master profile if necessary
+ if (index == m_currentProfile)
+ {
+ LoadProfile(0);
+ m_settings->Save();
+ }
+
+ CFileItemPtr item = CFileItemPtr(new CFileItem(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory)));
+ item->SetPath(URIUtils::AddFileToFolder(GetUserDataFolder(), strDirectory + "/"));
+ item->m_bIsFolder = true;
+ item->Select(true);
+
+ CGUIComponent *gui = CServiceBroker::GetGUI();
+ if (gui && gui->ConfirmDelete(item->GetPath()))
+ CFileUtils::DeleteItem(item);
+
+ return Save();
+}
+
+void CProfileManager::CreateProfileFolders()
+{
+ CDirectory::Create(GetDatabaseFolder());
+ CDirectory::Create(GetCDDBFolder());
+ CDirectory::Create(GetLibraryFolder());
+
+ // create Thumbnails/*
+ CDirectory::Create(GetThumbnailsFolder());
+ CDirectory::Create(GetVideoThumbFolder());
+ CDirectory::Create(GetBookmarksThumbFolder());
+ CDirectory::Create(GetSavestatesFolder());
+ for (size_t hex = 0; hex < 16; hex++)
+ CDirectory::Create(
+ URIUtils::AddFileToFolder(GetThumbnailsFolder(), StringUtils::Format("{:x}", hex)));
+
+ CDirectory::Create("special://profile/addon_data");
+ CDirectory::Create("special://profile/keymaps");
+}
+
+const CProfile& CProfileManager::GetMasterProfile() const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (!m_profiles.empty())
+ return m_profiles[0];
+
+ CLog::Log(LOGERROR, "{}: master profile doesn't exist", __FUNCTION__);
+ return EmptyProfile;
+}
+
+const CProfile& CProfileManager::GetCurrentProfile() const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (m_currentProfile < m_profiles.size())
+ return m_profiles[m_currentProfile];
+
+ CLog::Log(LOGERROR, "CProfileManager: current profile index ({0}) is outside of the valid range ({1})", m_currentProfile, m_profiles.size());
+ return EmptyProfile;
+}
+
+const CProfile* CProfileManager::GetProfile(unsigned int index) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index < m_profiles.size())
+ return &m_profiles[index];
+
+ return NULL;
+}
+
+CProfile* CProfileManager::GetProfile(unsigned int index)
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (index < m_profiles.size())
+ return &m_profiles[index];
+
+ return NULL;
+}
+
+int CProfileManager::GetProfileIndex(const std::string &name) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ for (int i = 0; i < static_cast<int>(m_profiles.size()); i++)
+ {
+ if (StringUtils::EqualsNoCase(m_profiles[i].getName(), name))
+ return i;
+ }
+
+ return -1;
+}
+
+void CProfileManager::AddProfile(const CProfile &profile)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // data integrity check - covers off migration from old profiles.xml,
+ // incrementing of the m_nextIdProfile,and bad data coming in
+ m_nextProfileId = std::max(m_nextProfileId, profile.getId() + 1);
+
+ m_profiles.push_back(profile);
+ }
+ Save();
+}
+
+void CProfileManager::UpdateCurrentProfileDate()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ if (m_currentProfile < m_profiles.size())
+ {
+ m_profiles[m_currentProfile].setDate();
+ CSingleExit exit(m_critical);
+ Save();
+ }
+}
+
+void CProfileManager::LoadMasterProfileForLogin()
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ // save the previous user
+ m_lastUsedProfile = m_currentProfile;
+ if (m_currentProfile != 0)
+ {
+ // determines that the (master) profile has only been loaded for login
+ m_profileLoadedForLogin = true;
+
+ LoadProfile(0);
+
+ // remember that the (master) profile has only been loaded for login
+ m_previousProfileLoadedForLogin = true;
+ }
+}
+
+bool CProfileManager::GetProfileName(const unsigned int profileId, std::string& name) const
+{
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ const CProfile *profile = GetProfile(profileId);
+ if (!profile)
+ return false;
+
+ name = profile->getName();
+ return true;
+}
+
+std::string CProfileManager::GetUserDataFolder() const
+{
+ return GetMasterProfile().getDirectory();
+}
+
+std::string CProfileManager::GetProfileUserDataFolder() const
+{
+ if (m_currentProfile == 0)
+ return GetUserDataFolder();
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), GetCurrentProfile().getDirectory());
+}
+
+std::string CProfileManager::GetDatabaseFolder() const
+{
+ if (GetCurrentProfile().hasDatabases())
+ return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Database");
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), "Database");
+}
+
+std::string CProfileManager::GetCDDBFolder() const
+{
+ return URIUtils::AddFileToFolder(GetDatabaseFolder(), "CDDB");
+}
+
+std::string CProfileManager::GetThumbnailsFolder() const
+{
+ if (GetCurrentProfile().hasDatabases())
+ return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails");
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), "Thumbnails");
+}
+
+std::string CProfileManager::GetVideoThumbFolder() const
+{
+ return URIUtils::AddFileToFolder(GetThumbnailsFolder(), "Video");
+}
+
+std::string CProfileManager::GetBookmarksThumbFolder() const
+{
+ return URIUtils::AddFileToFolder(GetVideoThumbFolder(), "Bookmarks");
+}
+
+std::string CProfileManager::GetLibraryFolder() const
+{
+ if (GetCurrentProfile().hasDatabases())
+ return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "library");
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), "library");
+}
+
+std::string CProfileManager::GetSavestatesFolder() const
+{
+ if (GetCurrentProfile().hasDatabases())
+ return URIUtils::AddFileToFolder(GetProfileUserDataFolder(), "Savestates");
+
+ return URIUtils::AddFileToFolder(GetUserDataFolder(), "Savestates");
+}
+
+std::string CProfileManager::GetSettingsFile() const
+{
+ if (m_currentProfile == 0)
+ return "special://masterprofile/guisettings.xml";
+
+ return "special://profile/guisettings.xml";
+}
+
+std::string CProfileManager::GetUserDataItem(const std::string& strFile) const
+{
+ std::string path;
+ path = "special://profile/" + strFile;
+
+ // check if item exists in the profile (either for folder or
+ // for a file (depending on slashAtEnd of strFile) otherwise
+ // return path to masterprofile
+ if ((URIUtils::HasSlashAtEnd(path) && !CDirectory::Exists(path)) || !CFile::Exists(path))
+ path = "special://masterprofile/" + strFile;
+
+ return path;
+}
+
+CEventLog& CProfileManager::GetEventLog()
+{
+ return m_eventLogs->GetEventLog(GetCurrentProfileId());
+}
+
+void CProfileManager::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ const std::string& settingId = setting->GetId();
+ if (settingId == CSettings::SETTING_EVENTLOG_SHOW)
+ GetEventLog().ShowFullEventLog();
+}
+
+void CProfileManager::SetCurrentProfileId(unsigned int profileId)
+{
+ {
+ std::unique_lock<CCriticalSection> lock(m_critical);
+ m_currentProfile = profileId;
+ CSpecialProtocol::SetProfilePath(GetProfileUserDataFolder());
+ }
+ Save();
+}
diff --git a/xbmc/profiles/ProfileManager.h b/xbmc/profiles/ProfileManager.h
new file mode 100644
index 0000000..e394518
--- /dev/null
+++ b/xbmc/profiles/ProfileManager.h
@@ -0,0 +1,222 @@
+/*
+ * 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 "profiles/Profile.h"
+#include "settings/lib/ISettingCallback.h"
+#include "settings/lib/ISettingsHandler.h"
+#include "threads/CriticalSection.h"
+
+#include <memory>
+#include <stdint.h>
+#include <vector>
+
+class CEventLog;
+class CEventLogManager;
+class CSettings;
+class TiXmlNode;
+
+class CProfileManager : protected ISettingsHandler,
+ protected ISettingCallback
+{
+public:
+ CProfileManager();
+ CProfileManager(const CProfileManager&) = delete;
+ CProfileManager& operator=(CProfileManager const&) = delete;
+ ~CProfileManager() override;
+
+ void Initialize(const std::shared_ptr<CSettings>& settings);
+ void Uninitialize();
+
+ void OnSettingsLoaded() override;
+ void OnSettingsSaved() const override;
+ void OnSettingsCleared() override;
+
+ bool Load();
+ /*! \brief Load the user profile information from disk
+ Loads the profiles.xml file and creates the list of profiles.
+ If no profiles exist, a master user is created. Should be called
+ after special://masterprofile/ has been defined.
+ \param file XML file to load.
+ */
+
+ bool Save() const;
+ /*! \brief Save the user profile information to disk
+ Saves the list of profiles to the profiles.xml file.
+ \param file XML file to save.
+ \return true on success, false on failure to save
+ */
+
+ void Clear();
+
+ bool LoadProfile(unsigned int index);
+ void LogOff();
+
+ bool DeleteProfile(unsigned int index);
+
+ void CreateProfileFolders();
+
+ /*! \brief Retrieve the master profile
+ \return const reference to the master profile
+ */
+ const CProfile& GetMasterProfile() const;
+
+ /*! \brief Retrieve the current profile
+ \return const reference to the current profile
+ */
+ const CProfile& GetCurrentProfile() const;
+
+ /*! \brief Retrieve the profile from an index
+ \param unsigned index of the profile to retrieve
+ \return const pointer to the profile, NULL if the index is invalid
+ */
+ const CProfile* GetProfile(unsigned int index) const;
+
+ /*! \brief Retrieve the profile from an index
+ \param unsigned index of the profile to retrieve
+ \return pointer to the profile, NULL if the index is invalid
+ */
+ CProfile* GetProfile(unsigned int index);
+
+ /*! \brief Retrieve index of a particular profile by name
+ \param name name of the profile index to retrieve
+ \return index of this profile, -1 if invalid.
+ */
+ int GetProfileIndex(const std::string &name) const;
+
+ /*! \brief Retrieve the number of profiles
+ \return number of profiles
+ */
+ size_t GetNumberOfProfiles() const { return m_profiles.size(); }
+
+ /*! \brief Add a new profile
+ \param profile CProfile to add
+ */
+ void AddProfile(const CProfile &profile);
+
+ /*! \brief Are we using the login screen?
+ \return true if we're using the login screen, false otherwise
+ */
+ bool UsingLoginScreen() const { return m_usingLoginScreen; }
+
+ /*! \brief Toggle login screen use on and off
+ Toggles the login screen state
+ */
+ void ToggleLoginScreen()
+ {
+ m_usingLoginScreen = !m_usingLoginScreen;
+ Save();
+ }
+
+ /*! \brief Are we the master user?
+ \return true if the current profile is the master user, false otherwise
+ */
+ bool IsMasterProfile() const { return m_currentProfile == 0; }
+
+ /*! \brief Update the date of the current profile
+ */
+ void UpdateCurrentProfileDate();
+
+ /*! \brief Load the master user for the purposes of logging in
+ Loads the master user. Identical to LoadProfile(0) but doesn't
+ update the last logged in details
+ */
+ void LoadMasterProfileForLogin();
+
+ /*! \brief Retrieve the last used profile index
+ \return the last used profile that logged in. Does not count the
+ master user during login.
+ */
+ uint32_t GetLastUsedProfileIndex() const { return m_lastUsedProfile; }
+
+ /*! \brief Retrieve the current profile index
+ \return the index of the currently logged in profile.
+ */
+ uint32_t GetCurrentProfileIndex() const { return m_currentProfile; }
+
+ /*! \brief Retrieve the next id to use for a new profile
+ \return the unique <id> to be used when creating a new profile
+ */
+ int GetNextProfileId() const { return m_nextProfileId; }
+
+ int GetCurrentProfileId() const { return GetCurrentProfile().getId(); }
+
+ /*! \brief Retrieve the autologin profile id
+ Retrieves the autologin profile id. When set to -1, then the last
+ used profile will be loaded
+ \return the id to the autologin profile
+ */
+ int GetAutoLoginProfileId() const { return m_autoLoginProfile; }
+
+ /*! \brief Retrieve the autologin profile id
+ Retrieves the autologin profile id. When set to -1, then the last
+ used profile will be loaded
+ \return the id to the autologin profile
+ */
+ void SetAutoLoginProfileId(const int profileId)
+ {
+ m_autoLoginProfile = profileId;
+ Save();
+ }
+
+ /*! \brief Retrieve the name of a particular profile by index
+ \param profileId profile index for which to retrieve the name
+ \param name will hold the name of the profile when a valid profile index has been provided
+ \return false if profileId is an invalid index, true if the name parameter is set
+ */
+ bool GetProfileName(const unsigned int profileId, std::string& name) const;
+
+ std::string GetUserDataFolder() const;
+ std::string GetProfileUserDataFolder() const;
+ std::string GetDatabaseFolder() const;
+ std::string GetCDDBFolder() const;
+ std::string GetThumbnailsFolder() const;
+ std::string GetVideoThumbFolder() const;
+ std::string GetBookmarksThumbFolder() const;
+ std::string GetLibraryFolder() const;
+ std::string GetSavestatesFolder() const;
+ std::string GetSettingsFile() const;
+
+ // uses HasSlashAtEnd to determine if a directory or file was meant
+ std::string GetUserDataItem(const std::string& strFile) const;
+
+ // Event log access
+ CEventLog &GetEventLog();
+
+protected:
+ // implementation of ISettingCallback
+ void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override;
+
+private:
+ /*! \brief Set the current profile id and update the special://profile path
+ \param profileId profile index
+ */
+ void SetCurrentProfileId(unsigned int profileId);
+
+ void PrepareLoadProfile(unsigned int profileIndex);
+ void FinalizeLoadProfile();
+
+ // Construction parameters
+ std::shared_ptr<CSettings> m_settings;
+
+ std::vector<CProfile> m_profiles;
+ bool m_usingLoginScreen = false;
+ bool m_profileLoadedForLogin = false;
+ bool m_previousProfileLoadedForLogin = false;
+ int m_autoLoginProfile = -1;
+ unsigned int m_lastUsedProfile = 0;
+ unsigned int m_currentProfile =
+ 0; // do not modify directly, use SetCurrentProfileId() function instead
+ int m_nextProfileId =
+ 0; // for tracking the next available id to give to a new profile to ensure id's are not re-used
+ mutable CCriticalSection m_critical;
+
+ // Event properties
+ std::unique_ptr<CEventLogManager> m_eventLogs;
+};
diff --git a/xbmc/profiles/dialogs/CMakeLists.txt b/xbmc/profiles/dialogs/CMakeLists.txt
new file mode 100644
index 0000000..e7fc216
--- /dev/null
+++ b/xbmc/profiles/dialogs/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(SOURCES GUIDialogLockSettings.cpp
+ GUIDialogProfileSettings.cpp)
+
+set(HEADERS GUIDialogLockSettings.h
+ GUIDialogProfileSettings.h)
+
+core_add_library(profiles_dialogs)
diff --git a/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
new file mode 100644
index 0000000..52eabc4
--- /dev/null
+++ b/xbmc/profiles/dialogs/GUIDialogLockSettings.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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 "GUIDialogLockSettings.h"
+
+#include "ServiceBroker.h"
+#include "URL.h"
+#include "dialogs/GUIDialogGamepad.h"
+#include "dialogs/GUIDialogNumeric.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "favourites/FavouritesService.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingSection.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#include <utility>
+
+#define SETTING_USERNAME "user.name"
+#define SETTING_PASSWORD "user.password"
+#define SETTING_PASSWORD_REMEMBER "user.rememberpassword"
+
+#define SETTING_LOCKCODE "lock.code"
+#define SETTING_LOCK_MUSIC "lock.music"
+#define SETTING_LOCK_VIDEOS "lock.videos"
+#define SETTING_LOCK_PICTURES "lock.pictures"
+#define SETTING_LOCK_PROGRAMS "lock.programs"
+#define SETTING_LOCK_FILEMANAGER "lock.filemanager"
+#define SETTING_LOCK_SETTINGS "lock.settings"
+#define SETTING_LOCK_ADDONMANAGER "lock.addonmanager"
+
+CGUIDialogLockSettings::CGUIDialogLockSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_LOCK_SETTINGS, "DialogSettings.xml"),
+ m_saveUserDetails(NULL)
+{ }
+
+CGUIDialogLockSettings::~CGUIDialogLockSettings() = default;
+
+bool CGUIDialogLockSettings::ShowAndGetLock(LockType &lockMode, std::string &password, int header /* = 20091 */)
+{
+ CProfile::CLock locks(lockMode, password);
+ if (!ShowAndGetLock(locks, header, false, false))
+ return false;
+
+ locks.Validate();
+ lockMode = locks.mode;
+ password = locks.code;
+
+ return true;
+}
+
+bool CGUIDialogLockSettings::ShowAndGetLock(CProfile::CLock &locks, int buttonLabel /* = 20091 */, bool conditional /* = false */, bool details /* = true */)
+{
+ CGUIDialogLockSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogLockSettings>(WINDOW_DIALOG_LOCK_SETTINGS);
+ if (dialog == NULL)
+ return false;
+
+ dialog->m_locks = locks;
+ dialog->m_buttonLabel = buttonLabel;
+ dialog->m_getUser = false;
+ dialog->m_conditionalDetails = conditional;
+ dialog->m_details = details;
+ dialog->Open();
+
+ if (!dialog->m_changed)
+ return false;
+
+ locks = dialog->m_locks;
+
+ // changed lock settings for certain sections (e.g. video, audio, or pictures)
+ // => refresh favourites due to possible visibility changes
+ CServiceBroker::GetFavouritesService().RefreshFavourites();
+
+ return true;
+}
+
+bool CGUIDialogLockSettings::ShowAndGetUserAndPassword(std::string &user, std::string &password, const std::string &url, bool *saveUserDetails)
+{
+ CGUIDialogLockSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogLockSettings>(WINDOW_DIALOG_LOCK_SETTINGS);
+ if (dialog == NULL)
+ return false;
+
+ dialog->m_getUser = true;
+ dialog->m_locks.code = password;
+ dialog->m_user = user;
+ dialog->m_url = url;
+ dialog->m_saveUserDetails = saveUserDetails;
+ dialog->Open();
+
+ if (!dialog->m_changed)
+ return false;
+
+ user = dialog->m_user;
+ password = dialog->m_locks.code;
+ return true;
+}
+
+void CGUIDialogLockSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_USERNAME)
+ m_user = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ else if (settingId == SETTING_PASSWORD)
+ m_locks.code = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ else if (settingId == SETTING_PASSWORD_REMEMBER)
+ *m_saveUserDetails = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_MUSIC)
+ m_locks.music = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_VIDEOS)
+ m_locks.video = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_PICTURES)
+ m_locks.pictures = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_PROGRAMS)
+ m_locks.programs = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_FILEMANAGER)
+ m_locks.files = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ else if (settingId == SETTING_LOCK_SETTINGS)
+ m_locks.settings = static_cast<LOCK_LEVEL::SETTINGS_LOCK>(std::static_pointer_cast<const CSettingInt>(setting)->GetValue());
+ else if (settingId == SETTING_LOCK_ADDONMANAGER)
+ m_locks.addonManager = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+
+ m_changed = true;
+}
+
+void CGUIDialogLockSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_LOCKCODE)
+ {
+ CGUIDialogSelect* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (!dialog)
+ return;
+
+ dialog->Reset();
+ dialog->SetHeading(CVariant{12360});
+ dialog->Add(g_localizeStrings.Get(1223));
+ dialog->Add(g_localizeStrings.Get(12337));
+ dialog->Add(g_localizeStrings.Get(12338));
+ dialog->Add(g_localizeStrings.Get(12339));
+ dialog->SetSelected(GetLockModeLabel());
+ dialog->Open();
+
+ std::string newPassword;
+ LockType iLockMode = LOCK_MODE_UNKNOWN;
+ bool bResult = false;
+ switch (dialog->GetSelectedItem())
+ {
+ case 0:
+ iLockMode = LOCK_MODE_EVERYONE; //Disabled! Need check routine!!!
+ bResult = true;
+ break;
+
+ case 1:
+ iLockMode = LOCK_MODE_NUMERIC;
+ bResult = CGUIDialogNumeric::ShowAndVerifyNewPassword(newPassword);
+ break;
+
+ case 2:
+ iLockMode = LOCK_MODE_GAMEPAD;
+ bResult = CGUIDialogGamepad::ShowAndVerifyNewPassword(newPassword);
+ break;
+
+ case 3:
+ iLockMode = LOCK_MODE_QWERTY;
+ bResult = CGUIKeyboardFactory::ShowAndVerifyNewPassword(newPassword);
+ break;
+
+ default:
+ break;
+ }
+
+ if (bResult)
+ {
+ if (iLockMode == LOCK_MODE_EVERYONE)
+ newPassword = "-";
+ m_locks.code = newPassword;
+ if (m_locks.code == "-")
+ iLockMode = LOCK_MODE_EVERYONE;
+ m_locks.mode = iLockMode;
+
+ SetSettingLockCodeLabel();
+ SetDetailSettingsEnabled(m_locks.mode != LOCK_MODE_EVERYONE);
+ m_changed = true;
+ }
+ }
+}
+
+void CGUIDialogLockSettings::OnCancel()
+{
+ m_changed = false;
+
+ CGUIDialogSettingsManualBase::OnCancel();
+}
+
+void CGUIDialogLockSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ // set the title
+ if (m_getUser)
+ SetHeading(StringUtils::Format(g_localizeStrings.Get(20152), CURL::Decode(m_url)));
+ else
+ {
+ SetHeading(20066);
+ SetSettingLockCodeLabel();
+ SetDetailSettingsEnabled(m_locks.mode != LOCK_MODE_EVERYONE);
+ }
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+}
+
+void CGUIDialogLockSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("locksettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings");
+ return;
+ }
+
+ const std::shared_ptr<CSettingGroup> group = AddGroup(category);
+ if (group == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings");
+ return;
+ }
+
+ if (m_getUser)
+ {
+ AddEdit(group, SETTING_USERNAME, 20142, SettingLevel::Basic, m_user);
+ AddEdit(group, SETTING_PASSWORD, 12326, SettingLevel::Basic, m_locks.code, false, true);
+ if (m_saveUserDetails != NULL)
+ AddToggle(group, SETTING_PASSWORD_REMEMBER, 13423, SettingLevel::Basic, *m_saveUserDetails);
+
+ return;
+ }
+
+ AddButton(group, SETTING_LOCKCODE, m_buttonLabel, SettingLevel::Basic);
+
+ if (m_details)
+ {
+ const std::shared_ptr<CSettingGroup> groupDetails = AddGroup(category);
+ if (groupDetails == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogLockSettings: unable to setup settings");
+ return;
+ }
+
+ AddToggle(groupDetails, SETTING_LOCK_MUSIC, 20038, SettingLevel::Basic, m_locks.music);
+ AddToggle(groupDetails, SETTING_LOCK_VIDEOS, 20039, SettingLevel::Basic, m_locks.video);
+ AddToggle(groupDetails, SETTING_LOCK_PICTURES, 20040, SettingLevel::Basic, m_locks.pictures);
+ AddToggle(groupDetails, SETTING_LOCK_PROGRAMS, 20041, SettingLevel::Basic, m_locks.programs);
+ AddToggle(groupDetails, SETTING_LOCK_FILEMANAGER, 20042, SettingLevel::Basic, m_locks.files);
+
+ TranslatableIntegerSettingOptions settingsLevelOptions;
+ settingsLevelOptions.push_back(TranslatableIntegerSettingOption(106, LOCK_LEVEL::NONE));
+ settingsLevelOptions.push_back(TranslatableIntegerSettingOption(593, LOCK_LEVEL::ALL));
+ settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10037, LOCK_LEVEL::STANDARD));
+ settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10038, LOCK_LEVEL::ADVANCED));
+ settingsLevelOptions.push_back(TranslatableIntegerSettingOption(10039, LOCK_LEVEL::EXPERT));
+ AddList(groupDetails, SETTING_LOCK_SETTINGS, 20043, SettingLevel::Basic, static_cast<int>(m_locks.settings), settingsLevelOptions, 20043);
+
+ AddToggle(groupDetails, SETTING_LOCK_ADDONMANAGER, 24090, SettingLevel::Basic, m_locks.addonManager);
+ }
+
+ m_changed = false;
+}
+
+std::string CGUIDialogLockSettings::GetLockModeLabel()
+{
+ return g_localizeStrings.Get(m_locks.mode == LOCK_MODE_EVERYONE ? 1223 : 12336 + m_locks.mode);
+}
+
+void CGUIDialogLockSettings::SetDetailSettingsEnabled(bool enabled)
+{
+ if (!m_details)
+ return;
+
+ enabled |= !m_conditionalDetails;
+ GetSettingControl(SETTING_LOCK_MUSIC)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_VIDEOS)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_PICTURES)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_PROGRAMS)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_FILEMANAGER)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_SETTINGS)->GetSetting()->SetEnabled(enabled);
+ GetSettingControl(SETTING_LOCK_ADDONMANAGER)->GetSetting()->SetEnabled(enabled);
+}
+
+void CGUIDialogLockSettings::SetSettingLockCodeLabel()
+{
+ // adjust label2 of the lock code setting button
+ if (m_locks.mode > LOCK_MODE_QWERTY)
+ m_locks.mode = LOCK_MODE_EVERYONE;
+ BaseSettingControlPtr settingControl = GetSettingControl(SETTING_LOCKCODE);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_LABEL2(settingControl->GetID(), GetLockModeLabel());
+}
diff --git a/xbmc/profiles/dialogs/GUIDialogLockSettings.h b/xbmc/profiles/dialogs/GUIDialogLockSettings.h
new file mode 100644
index 0000000..bdcaed7
--- /dev/null
+++ b/xbmc/profiles/dialogs/GUIDialogLockSettings.h
@@ -0,0 +1,53 @@
+/*
+ * 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 "profiles/Profile.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+class CGUIDialogLockSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogLockSettings();
+ ~CGUIDialogLockSettings() override;
+
+ static bool ShowAndGetLock(LockType &lockMode, std::string &password, int header = 20091);
+ static bool ShowAndGetLock(CProfile::CLock &locks, int buttonLabel = 20091, bool conditional = false, bool details = true);
+ static bool ShowAndGetUserAndPassword(std::string &user, std::string &password, const std::string &url, bool *saveUserDetails);
+
+protected:
+ // 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 { return true; }
+ void OnCancel() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+private:
+ std::string GetLockModeLabel();
+ void SetDetailSettingsEnabled(bool enabled);
+ void SetSettingLockCodeLabel();
+
+ bool m_changed = false;
+
+ CProfile::CLock m_locks;
+ std::string m_user;
+ std::string m_url;
+ bool m_details = true;
+ bool m_conditionalDetails = false;
+ bool m_getUser = false;
+ bool* m_saveUserDetails;
+ int m_buttonLabel = 20091;
+};
diff --git a/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
new file mode 100644
index 0000000..d7ce0da
--- /dev/null
+++ b/xbmc/profiles/dialogs/GUIDialogProfileSettings.cpp
@@ -0,0 +1,397 @@
+/*
+ * 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 "GUIDialogProfileSettings.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogYesNo.h"
+#include "filesystem/Directory.h"
+#include "filesystem/File.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "profiles/ProfileManager.h"
+#include "profiles/dialogs/GUIDialogLockSettings.h"
+#include "settings/SettingsComponent.h"
+#include "settings/lib/Setting.h"
+#include "settings/windows/GUIControlSettings.h"
+#include "storage/MediaManager.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <cassert>
+#include <utility>
+
+#define SETTING_PROFILE_NAME "profile.name"
+#define SETTING_PROFILE_IMAGE "profile.image"
+#define SETTING_PROFILE_DIRECTORY "profile.directory"
+#define SETTING_PROFILE_LOCKS "profile.locks"
+#define SETTING_PROFILE_MEDIA "profile.media"
+#define SETTING_PROFILE_MEDIA_SOURCES "profile.mediasources"
+
+CGUIDialogProfileSettings::CGUIDialogProfileSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PROFILE_SETTINGS, "DialogSettings.xml")
+{ }
+
+CGUIDialogProfileSettings::~CGUIDialogProfileSettings() = default;
+
+bool CGUIDialogProfileSettings::ShowForProfile(unsigned int iProfile, bool firstLogin)
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (firstLogin && iProfile > profileManager->GetNumberOfProfiles())
+ return false;
+
+ CGUIDialogProfileSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProfileSettings>(WINDOW_DIALOG_PROFILE_SETTINGS);
+ if (dialog == NULL)
+ return false;
+
+ dialog->m_needsSaving = false;
+ dialog->m_isDefault = iProfile == 0;
+ dialog->m_showDetails = !firstLogin;
+
+ const CProfile *profile = profileManager->GetProfile(iProfile);
+ if (profile == NULL)
+ {
+ dialog->m_name.clear();
+ dialog->m_dbMode = 2;
+ dialog->m_sourcesMode = 2;
+ dialog->m_locks = CProfile::CLock();
+
+ bool bLock = profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser;
+ dialog->m_locks.addonManager = bLock;
+ dialog->m_locks.settings = (bLock) ? LOCK_LEVEL::ALL : LOCK_LEVEL::NONE;
+ dialog->m_locks.files = bLock;
+
+ dialog->m_directory.clear();
+ dialog->m_thumb.clear();
+
+ // prompt for a name
+ std::string profileName;
+ if (!CGUIKeyboardFactory::ShowAndGetInput(profileName, CVariant{g_localizeStrings.Get(20093)}, false) || profileName.empty())
+ return false;
+ dialog->m_name = profileName;
+
+ // create a default path
+ std::string defaultDir = URIUtils::AddFileToFolder("profiles", CUtil::MakeLegalFileName(dialog->m_name));
+ URIUtils::AddSlashAtEnd(defaultDir);
+ XFILE::CDirectory::Create(URIUtils::AddFileToFolder("special://masterprofile/", defaultDir));
+
+ // prompt for the user to change it if they want
+ std::string userDir = defaultDir;
+ if (GetProfilePath(userDir, false)) // can't be the master user
+ {
+ if (!URIUtils::PathHasParent(userDir, defaultDir)) // user chose a different folder
+ XFILE::CDirectory::Remove(URIUtils::AddFileToFolder("special://masterprofile/", defaultDir));
+ }
+ dialog->m_directory = userDir;
+ dialog->m_needsSaving = true;
+ }
+ else
+ {
+ dialog->m_name = profile->getName();
+ dialog->m_thumb = profile->getThumb();
+ dialog->m_directory = profile->getDirectory();
+ dialog->m_dbMode = profile->canWriteDatabases() ? 0 : 1;
+ if (profile->hasDatabases())
+ dialog->m_dbMode += 2;
+ dialog->m_sourcesMode = profile->canWriteSources() ? 0 : 1;
+ if (profile->hasSources())
+ dialog->m_sourcesMode += 2;
+
+ dialog->m_locks = profile->GetLocks();
+ }
+
+ dialog->Open();
+ if (dialog->m_needsSaving)
+ {
+ if (iProfile >= profileManager->GetNumberOfProfiles())
+ {
+ if (dialog->m_name.empty() || dialog->m_directory.empty())
+ return false;
+
+ /*std::string strLabel;
+ strLabel.Format(g_localizeStrings.Get(20047),dialog->m_strName);
+ if (!CGUIDialogYesNo::ShowAndGetInput(20058, strLabel, dialog->m_strDirectory, ""))
+ {
+ CDirectory::Remove(URIUtils::AddFileToFolder(profileManager.GetUserDataFolder(), dialog->m_strDirectory));
+ return false;
+ }*/
+
+ // check for old profile settings
+ CProfile profile(dialog->m_directory, dialog->m_name, profileManager->GetNextProfileId());
+ profileManager->AddProfile(profile);
+ bool exists = XFILE::CFile::Exists(URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "guisettings.xml"));
+
+ if (exists && !CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20104}))
+ exists = false;
+
+ if (!exists)
+ {
+ // copy masterprofile guisettings to new profile guisettings
+ // If the user selects 'start fresh', do nothing as a fresh
+ // guisettings.xml will be created on first profile use.
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20048}, CVariant{""}, CVariant{""}, CVariant{20044}, CVariant{20064}))
+ {
+ XFILE::CFile::Copy(URIUtils::AddFileToFolder("special://masterprofile/", "guisettings.xml"),
+ URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "guisettings.xml"));
+ }
+ }
+
+ exists = XFILE::CFile::Exists(URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "sources.xml"));
+ if (exists && !CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20106}))
+ exists = false;
+
+ if (!exists)
+ {
+ if ((dialog->m_sourcesMode & 2) == 2)
+ // prompt user to copy masterprofile's sources.xml file
+ // If 'start fresh' (no) is selected, do nothing.
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20058}, CVariant{20071}, CVariant{""}, CVariant{""}, CVariant{20044}, CVariant{20064}))
+ {
+ XFILE::CFile::Copy(URIUtils::AddFileToFolder("special://masterprofile/", "sources.xml"),
+ URIUtils::AddFileToFolder("special://masterprofile/", dialog->m_directory, "sources.xml"));
+ }
+ }
+ }
+
+ /*if (!dialog->m_bIsNewUser)
+ if (!CGUIDialogYesNo::ShowAndGetInput(20067, 20103))
+ return false;*/
+
+ CProfile *profile = profileManager->GetProfile(iProfile);
+ assert(profile);
+ profile->setName(dialog->m_name);
+ profile->setDirectory(dialog->m_directory);
+ profile->setThumb(dialog->m_thumb);
+ profile->setWriteDatabases(!((dialog->m_dbMode & 1) == 1));
+ profile->setWriteSources(!((dialog->m_sourcesMode & 1) == 1));
+ profile->setDatabases((dialog->m_dbMode & 2) == 2);
+ profile->setSources((dialog->m_sourcesMode & 2) == 2);
+ profile->SetLocks(dialog->m_locks);
+ profileManager->Save();
+
+ return true;
+ }
+
+ return dialog->m_needsSaving;
+}
+
+void CGUIDialogProfileSettings::OnWindowLoaded()
+{
+ CGUIDialogSettingsManualBase::OnWindowLoaded();
+}
+
+void CGUIDialogProfileSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_PROFILE_NAME)
+ {
+ m_name = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ }
+ else if (settingId == SETTING_PROFILE_MEDIA)
+ m_dbMode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ else if (settingId == SETTING_PROFILE_MEDIA_SOURCES)
+ m_sourcesMode = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+
+ m_needsSaving = true;
+}
+
+void CGUIDialogProfileSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == NULL)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+ if (settingId == SETTING_PROFILE_IMAGE)
+ {
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+
+ CFileItemList items;
+ if (!m_thumb.empty())
+ {
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", m_thumb);
+ item->SetLabel(g_localizeStrings.Get(20016));
+ items.Add(item);
+ }
+
+ CFileItemPtr item(new CFileItem("thumb://None", false));
+ item->SetArt("thumb", "DefaultUser.png");
+ item->SetLabel(g_localizeStrings.Get(20018));
+ items.Add(item);
+
+ std::string thumb;
+ if (CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(1030), thumb) &&
+ !StringUtils::EqualsNoCase(thumb, "thumb://Current"))
+ {
+ m_needsSaving = true;
+ m_thumb = StringUtils::EqualsNoCase(thumb, "thumb://None") ? "" : thumb;
+
+ UpdateProfileImage();
+ }
+ }
+ else if (settingId == SETTING_PROFILE_DIRECTORY)
+ {
+ if (!GetProfilePath(m_directory, m_isDefault))
+ return;
+
+ m_needsSaving = true;
+ updateProfileDirectory();
+ }
+ else if (settingId == SETTING_PROFILE_LOCKS)
+ {
+ if (m_showDetails)
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE && !m_isDefault)
+ {
+ if (CGUIDialogYesNo::ShowAndGetInput(CVariant{20066}, CVariant{20118}))
+ g_passwordManager.SetMasterLockMode(false);
+ if (profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE)
+ return;
+ }
+ if (CGUIDialogLockSettings::ShowAndGetLock(m_locks, m_isDefault ? 12360 : 20068,
+ profileManager->GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || m_isDefault))
+ m_needsSaving = true;
+ }
+ else
+ {
+ if (CGUIDialogLockSettings::ShowAndGetLock(m_locks, m_isDefault ? 12360 : 20068, false, false))
+ m_needsSaving = true;
+ }
+ }
+}
+
+void CGUIDialogProfileSettings::OnCancel()
+{
+ m_needsSaving = false;
+
+ CGUIDialogSettingsManualBase::OnCancel();
+}
+
+void CGUIDialogProfileSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ // set the heading
+ SetHeading(!m_showDetails ? 20255 : 20067);
+
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+
+ // set the profile image and directory
+ UpdateProfileImage();
+ updateProfileDirectory();
+}
+
+void CGUIDialogProfileSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ const std::shared_ptr<CSettingCategory> category = AddCategory("profilesettings", -1);
+ if (category == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings");
+ return;
+ }
+
+ const std::shared_ptr<CSettingGroup> group = AddGroup(category);
+ if (group == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings");
+ return;
+ }
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ AddEdit(group, SETTING_PROFILE_NAME, 20093, SettingLevel::Basic, m_name);
+ AddButton(group, SETTING_PROFILE_IMAGE, 20065, SettingLevel::Basic);
+
+ if (!m_isDefault && m_showDetails)
+ AddButton(group, SETTING_PROFILE_DIRECTORY, 20070, SettingLevel::Basic);
+
+ if (m_showDetails ||
+ (m_locks.mode == LOCK_MODE_EVERYONE && profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE))
+ AddButton(group, SETTING_PROFILE_LOCKS, 20066, SettingLevel::Basic);
+
+ if (!m_isDefault && m_showDetails)
+ {
+ const std::shared_ptr<CSettingGroup> groupMedia = AddGroup(category);
+ if (groupMedia == NULL)
+ {
+ CLog::Log(LOGERROR, "CGUIDialogProfileSettings: unable to setup settings");
+ return;
+ }
+
+ TranslatableIntegerSettingOptions entries;
+ entries.push_back(TranslatableIntegerSettingOption(20062, 0));
+ entries.push_back(TranslatableIntegerSettingOption(20063, 1));
+ entries.push_back(TranslatableIntegerSettingOption(20061, 2));
+ if (profileManager->GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE)
+ entries.push_back(TranslatableIntegerSettingOption(20107, 3));
+
+ AddSpinner(groupMedia, SETTING_PROFILE_MEDIA, 20060, SettingLevel::Basic, m_dbMode, entries);
+ AddSpinner(groupMedia, SETTING_PROFILE_MEDIA_SOURCES, 20094, SettingLevel::Basic, m_sourcesMode, entries);
+ }
+}
+
+bool CGUIDialogProfileSettings::GetProfilePath(std::string &directory, bool isDefault)
+{
+ VECSOURCES shares;
+ CMediaSource share;
+ share.strName = g_localizeStrings.Get(13200);
+ share.strPath = "special://masterprofile/profiles/";
+ shares.push_back(share);
+
+ std::string strDirectory;
+ if (directory.empty())
+ strDirectory = share.strPath;
+ else
+ strDirectory = URIUtils::AddFileToFolder("special://masterprofile/", directory);
+
+ if (!CGUIDialogFileBrowser::ShowAndGetDirectory(shares, g_localizeStrings.Get(657), strDirectory, true))
+ return false;
+
+ directory = strDirectory;
+ if (!isDefault)
+ directory.erase(0, 24);
+
+ return true;
+}
+
+void CGUIDialogProfileSettings::UpdateProfileImage()
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(SETTING_PROFILE_IMAGE);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_LABEL2(settingControl->GetID(), URIUtils::GetFileName(m_thumb));
+}
+
+void CGUIDialogProfileSettings::updateProfileDirectory()
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(SETTING_PROFILE_DIRECTORY);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_LABEL2(settingControl->GetID(), m_directory);
+}
diff --git a/xbmc/profiles/dialogs/GUIDialogProfileSettings.h b/xbmc/profiles/dialogs/GUIDialogProfileSettings.h
new file mode 100644
index 0000000..5b6f14e
--- /dev/null
+++ b/xbmc/profiles/dialogs/GUIDialogProfileSettings.h
@@ -0,0 +1,62 @@
+/*
+ * 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 "profiles/Profile.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <string>
+
+class CGUIDialogProfileSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogProfileSettings();
+ ~CGUIDialogProfileSettings() override;
+
+ static bool ShowForProfile(unsigned int iProfile, bool firstLogin = false);
+
+protected:
+ // specializations of CGUIWindow
+ void OnWindowLoaded() 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 { return true; }
+ void OnCancel() override;
+ void SetupView() override;
+
+ // specialization of CGUIDialogSettingsManualBase
+ void InitializeSettings() override;
+
+ /*! \brief Prompt for a change in profile path
+ \param directory Current directory for the profile, new profile directory will be returned here
+ \param isDefault whether this is the default profile or not
+ \return true if the profile path has been changed, false otherwise.
+ */
+ static bool GetProfilePath(std::string &directory, bool isDefault);
+
+ void UpdateProfileImage();
+ void updateProfileDirectory();
+
+ bool m_needsSaving = false;
+ std::string m_name;
+ std::string m_thumb;
+ std::string m_directory;
+ int m_sourcesMode;
+ int m_dbMode;
+ bool m_isDefault;
+ bool m_isNewUser;
+ bool m_showDetails;
+
+ CProfile::CLock m_locks;
+};
diff --git a/xbmc/profiles/windows/CMakeLists.txt b/xbmc/profiles/windows/CMakeLists.txt
new file mode 100644
index 0000000..232d0aa
--- /dev/null
+++ b/xbmc/profiles/windows/CMakeLists.txt
@@ -0,0 +1,5 @@
+set(SOURCES GUIWindowSettingsProfile.cpp)
+
+set(HEADERS GUIWindowSettingsProfile.h)
+
+core_add_library(profiles_windows)
diff --git a/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp b/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp
new file mode 100644
index 0000000..05f1c09
--- /dev/null
+++ b/xbmc/profiles/windows/GUIWindowSettingsProfile.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "GUIWindowSettingsProfile.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "messaging/ApplicationMessenger.h"
+#include "profiles/Profile.h"
+#include "profiles/ProfileManager.h"
+#include "profiles/dialogs/GUIDialogProfileSettings.h"
+#include "settings/SettingsComponent.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "windows/GUIWindowFileManager.h"
+
+using namespace XFILE;
+
+#define CONTROL_PROFILES 2
+#define CONTROL_LOGINSCREEN 4
+#define CONTROL_AUTOLOGIN 5
+
+CGUIWindowSettingsProfile::CGUIWindowSettingsProfile(void)
+ : CGUIWindow(WINDOW_SETTINGS_PROFILES, "SettingsProfile.xml")
+{
+ m_listItems = new CFileItemList;
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIWindowSettingsProfile::~CGUIWindowSettingsProfile(void)
+{
+ delete m_listItems;
+}
+
+int CGUIWindowSettingsProfile::GetSelectedItem()
+{
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PROFILES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return msg.GetParam1();
+}
+
+void CGUIWindowSettingsProfile::OnPopupMenu(int iItem)
+{
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ if (iItem == (int)profileManager->GetNumberOfProfiles())
+ return;
+
+ // popup the context menu
+ CContextButtons choices;
+ choices.Add(1, 20092); // Load profile
+ if (iItem > 0)
+ choices.Add(2, 117); // Delete
+
+ int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
+ if (choice == 1)
+ {
+ CServiceBroker::GetAppMessenger()->PostMsg(TMSG_LOADPROFILE, iItem);
+ return;
+ }
+
+ if (choice == 2)
+ {
+ if (profileManager->DeleteProfile(iItem))
+ iItem--;
+ }
+
+ LoadList();
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(),CONTROL_PROFILES,iItem);
+ OnMessage(msg);
+}
+
+bool CGUIWindowSettingsProfile::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ CGUIWindow::OnMessage(message);
+ ClearListItems();
+ return true;
+ }
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_PROFILES)
+ {
+ int iAction = message.GetParam1();
+ if (
+ iAction == ACTION_SELECT_ITEM ||
+ iAction == ACTION_MOUSE_LEFT_CLICK ||
+ iAction == ACTION_CONTEXT_MENU ||
+ iAction == ACTION_MOUSE_RIGHT_CLICK
+ )
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CONTROL_PROFILES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ int iItem = msg.GetParam1();
+ if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
+ {
+ //contextmenu
+ if (iItem <= static_cast<int>(profileManager->GetNumberOfProfiles()) - 1)
+ {
+ OnPopupMenu(iItem);
+ }
+ return true;
+ }
+ else if (iItem < static_cast<int>(profileManager->GetNumberOfProfiles()))
+ {
+ if (CGUIDialogProfileSettings::ShowForProfile(iItem))
+ {
+ LoadList();
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), 2,iItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ return true;
+ }
+
+ return false;
+ }
+ else if (iItem > static_cast<int>(profileManager->GetNumberOfProfiles()) - 1)
+ {
+ CDirectory::Create(URIUtils::AddFileToFolder(profileManager->GetUserDataFolder(),"profiles"));
+ if (CGUIDialogProfileSettings::ShowForProfile(profileManager->GetNumberOfProfiles()))
+ {
+ LoadList();
+ CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), 2,iItem);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ return true;
+ }
+
+ return false;
+ }
+ }
+ }
+ else if (iControl == CONTROL_LOGINSCREEN)
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ profileManager->ToggleLoginScreen();
+ profileManager->Save();
+ return true;
+ }
+ else if (iControl == CONTROL_AUTOLOGIN)
+ {
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ int currentId = profileManager->GetAutoLoginProfileId();
+ int profileId;
+ if (GetAutoLoginProfileChoice(profileId) && (currentId != profileId))
+ {
+ profileManager->SetAutoLoginProfileId(profileId);
+ profileManager->Save();
+ }
+ return true;
+ }
+ }
+ break;
+ }
+
+ return CGUIWindow::OnMessage(message);
+}
+
+void CGUIWindowSettingsProfile::LoadList()
+{
+ ClearListItems();
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ for (unsigned int i = 0; i < profileManager->GetNumberOfProfiles(); i++)
+ {
+ const CProfile *profile = profileManager->GetProfile(i);
+ CFileItemPtr item(new CFileItem(profile->getName()));
+ item->SetLabel2(profile->getDate());
+ item->SetArt("thumb", profile->getThumb());
+ item->SetOverlayImage(profile->getLockMode() == LOCK_MODE_EVERYONE ? CGUIListItem::ICON_OVERLAY_NONE : CGUIListItem::ICON_OVERLAY_LOCKED);
+ m_listItems->Add(item);
+ }
+ {
+ CFileItemPtr item(new CFileItem(g_localizeStrings.Get(20058)));
+ m_listItems->Add(item);
+ }
+ CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_PROFILES, 0, 0, m_listItems);
+ OnMessage(msg);
+
+ if (profileManager->UsingLoginScreen())
+ {
+ CONTROL_SELECT(CONTROL_LOGINSCREEN);
+ }
+ else
+ {
+ CONTROL_DESELECT(CONTROL_LOGINSCREEN);
+ }
+}
+
+void CGUIWindowSettingsProfile::ClearListItems()
+{
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_PROFILES);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ m_listItems->Clear();
+}
+
+void CGUIWindowSettingsProfile::OnInitWindow()
+{
+ LoadList();
+ CGUIWindow::OnInitWindow();
+}
+
+bool CGUIWindowSettingsProfile::GetAutoLoginProfileChoice(int &iProfile)
+{
+ CGUIDialogSelect *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT);
+ if (!dialog) return false;
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ // add items
+ // "Last used profile" option comes first, so up indices by 1
+ int autoLoginProfileId = profileManager->GetAutoLoginProfileId() + 1;
+ CFileItemList items;
+ CFileItemPtr item(new CFileItem());
+ item->SetLabel(g_localizeStrings.Get(37014)); // Last used profile
+ item->SetArt("icon", "DefaultUser.png");
+ items.Add(item);
+
+ for (unsigned int i = 0; i < profileManager->GetNumberOfProfiles(); i++)
+ {
+ const CProfile *profile = profileManager->GetProfile(i);
+ const std::string& locked = g_localizeStrings.Get(profile->getLockMode() > 0 ? 20166 : 20165);
+ CFileItemPtr item(new CFileItem(profile->getName()));
+ item->SetLabel2(locked); // lock setting
+ std::string thumb = profile->getThumb();
+ if (thumb.empty())
+ thumb = "DefaultUser.png";
+ item->SetArt("icon", thumb);
+ items.Add(item);
+ }
+
+ dialog->SetHeading(CVariant{20093}); // Profile name
+ dialog->Reset();
+ dialog->SetUseDetails(true);
+ dialog->SetItems(items);
+ dialog->SetSelected(autoLoginProfileId);
+ dialog->Open();
+
+ if (dialog->IsButtonPressed() || dialog->GetSelectedItem() < 0)
+ return false; // user cancelled
+ iProfile = dialog->GetSelectedItem() - 1;
+
+ return true;
+}
diff --git a/xbmc/profiles/windows/GUIWindowSettingsProfile.h b/xbmc/profiles/windows/GUIWindowSettingsProfile.h
new file mode 100644
index 0000000..0057305
--- /dev/null
+++ b/xbmc/profiles/windows/GUIWindowSettingsProfile.h
@@ -0,0 +1,35 @@
+/*
+ * 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 CFileItemList;
+
+class CGUIWindowSettingsProfile :
+ public CGUIWindow
+{
+public:
+ CGUIWindowSettingsProfile(void);
+ ~CGUIWindowSettingsProfile(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+
+protected:
+ void OnInitWindow() override;
+ CFileItemList *m_listItems;
+
+ void OnPopupMenu(int iItem);
+ void DoRename(int iItem);
+ void DoOverwrite(int iItem);
+ int GetSelectedItem();
+ void LoadList();
+ void SetLastLoaded();
+ void ClearListItems();
+ bool GetAutoLoginProfileChoice(int &iProfile);
+};