summaryrefslogtreecommitdiffstats
path: root/xbmc/music/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/music/dialogs')
-rw-r--r--xbmc/music/dialogs/CMakeLists.txt13
-rw-r--r--xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp479
-rw-r--r--xbmc/music/dialogs/GUIDialogInfoProviderSettings.h92
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicInfo.cpp1043
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicInfo.h80
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicOSD.cpp91
-rw-r--r--xbmc/music/dialogs/GUIDialogMusicOSD.h22
-rw-r--r--xbmc/music/dialogs/GUIDialogSongInfo.cpp523
-rw-r--r--xbmc/music/dialogs/GUIDialogSongInfo.h54
-rw-r--r--xbmc/music/dialogs/GUIDialogVisualisationPresetList.cpp98
-rw-r--r--xbmc/music/dialogs/GUIDialogVisualisationPresetList.h32
11 files changed, 2527 insertions, 0 deletions
diff --git a/xbmc/music/dialogs/CMakeLists.txt b/xbmc/music/dialogs/CMakeLists.txt
new file mode 100644
index 0000000..40b6e90
--- /dev/null
+++ b/xbmc/music/dialogs/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(SOURCES GUIDialogInfoProviderSettings.cpp
+ GUIDialogMusicInfo.cpp
+ GUIDialogMusicOSD.cpp
+ GUIDialogSongInfo.cpp
+ GUIDialogVisualisationPresetList.cpp)
+
+set(HEADERS GUIDialogInfoProviderSettings.h
+ GUIDialogMusicInfo.h
+ GUIDialogMusicOSD.h
+ GUIDialogSongInfo.h
+ GUIDialogVisualisationPresetList.h)
+
+core_add_library(music_dialogs)
diff --git a/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp
new file mode 100644
index 0000000..a9e2232
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.cpp
@@ -0,0 +1,479 @@
+/*
+ * 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 "GUIDialogInfoProviderSettings.h"
+
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/AddonSystemSettings.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIDialogAddonSettings.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogKaiToast.h"
+#include "filesystem/AddonsDirectory.h"
+#include "filesystem/Directory.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "interfaces/builtins/Builtins.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;
+
+const std::string SETTING_ALBUMSCRAPER_SETTINGS = "albumscrapersettings";
+const std::string SETTING_ARTISTSCRAPER_SETTINGS = "artistscrapersettings";
+const std::string SETTING_APPLYTOITEMS = "applysettingstoitems";
+
+CGUIDialogInfoProviderSettings::CGUIDialogInfoProviderSettings()
+ : CGUIDialogSettingsManualBase(WINDOW_DIALOG_INFOPROVIDER_SETTINGS, "DialogSettings.xml")
+{ }
+
+bool CGUIDialogInfoProviderSettings::Show()
+{
+ CGUIDialogInfoProviderSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogInfoProviderSettings>(WINDOW_DIALOG_INFOPROVIDER_SETTINGS);
+ if (!dialog)
+ return false;
+
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+
+ dialog->m_showSingleScraper = false;
+
+ // Get current default info provider settings from service broker
+ dialog->m_fetchInfo = settings->GetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO);
+
+ ADDON::AddonPtr defaultScraper;
+ // Get default album scraper (when enabled - can default scraper be disabled??)
+ if (ADDON::CAddonSystemSettings::GetInstance().GetActive(ADDON::AddonType::SCRAPER_ALBUMS,
+ defaultScraper))
+ {
+ ADDON::ScraperPtr scraper = std::dynamic_pointer_cast<ADDON::CScraper>(defaultScraper);
+ dialog->SetAlbumScraper(scraper);
+ }
+
+ // Get default artist scraper
+ if (ADDON::CAddonSystemSettings::GetInstance().GetActive(ADDON::AddonType::SCRAPER_ARTISTS,
+ defaultScraper))
+ {
+ ADDON::ScraperPtr scraper = std::dynamic_pointer_cast<ADDON::CScraper>(defaultScraper);
+ dialog->SetArtistScraper(scraper);
+ }
+
+ dialog->m_strArtistInfoPath = settings->GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+
+ dialog->Open();
+
+ dialog->ResetDefaults();
+ return dialog->IsConfirmed();
+}
+
+int CGUIDialogInfoProviderSettings::Show(ADDON::ScraperPtr& scraper)
+{
+ CGUIDialogInfoProviderSettings *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogInfoProviderSettings>(WINDOW_DIALOG_INFOPROVIDER_SETTINGS);
+ if (!dialog || !scraper)
+ return -1;
+ if (scraper->Content() != CONTENT_ARTISTS && scraper->Content() != CONTENT_ALBUMS)
+ return -1;
+
+ dialog->m_showSingleScraper = true;
+ dialog->m_singleScraperType = scraper->Content();
+
+ if (dialog->m_singleScraperType == CONTENT_ALBUMS)
+ dialog->SetAlbumScraper(scraper);
+ else
+ dialog->SetArtistScraper(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->Open();
+
+ bool confirmed = dialog->IsConfirmed();
+ unsigned int applyToItems = dialog->m_applyToItems;
+ if (confirmed)
+ {
+ if (dialog->m_singleScraperType == CONTENT_ALBUMS)
+ scraper = dialog->GetAlbumScraper();
+ else
+ {
+ scraper = dialog->GetArtistScraper();
+ // Save artist information folder (here not in the caller) when applying setting as default for all artists
+ if (applyToItems == INFOPROVIDERAPPLYOPTIONS::INFOPROVIDER_DEFAULT)
+ CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, dialog->m_strArtistInfoPath);
+ }
+ if (scraper)
+ scraper->SetPathSettings(dialog->m_singleScraperType, "");
+ }
+
+ dialog->ResetDefaults();
+
+ if (confirmed)
+ return applyToItems;
+ else
+ return -1;
+}
+
+void CGUIDialogInfoProviderSettings::OnInitWindow()
+{
+ CGUIDialogSettingsManualBase::OnInitWindow();
+}
+
+void CGUIDialogInfoProviderSettings::OnSettingChanged(
+ const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingChanged(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO)
+ {
+ m_fetchInfo = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
+ SetupView();
+ SetFocus(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO);
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER)
+ m_strArtistInfoPath = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ else if (settingId == SETTING_APPLYTOITEMS)
+ {
+ m_applyToItems = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
+ SetupView();
+ SetFocus(SETTING_APPLYTOITEMS);
+ }
+}
+
+void CGUIDialogInfoProviderSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
+{
+ if (setting == nullptr)
+ return;
+
+ CGUIDialogSettingsManualBase::OnSettingAction(setting);
+
+ const std::string &settingId = setting->GetId();
+
+ if (settingId == CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER)
+ {
+ std::string currentScraperId;
+ if (m_albumscraper)
+ currentScraperId = m_albumscraper->ID();
+ std::string selectedAddonId = currentScraperId;
+
+ if (CGUIWindowAddonBrowser::SelectAddonID(AddonType::SCRAPER_ALBUMS, selectedAddonId, false) ==
+ 1 &&
+ selectedAddonId != currentScraperId)
+ {
+ AddonPtr scraperAddon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(selectedAddonId, scraperAddon,
+ OnlyEnabled::CHOICE_YES))
+ {
+ m_albumscraper = std::dynamic_pointer_cast<CScraper>(scraperAddon);
+ SetupView();
+ SetFocus(settingId);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} - Could not get settings for addon: {}", __FUNCTION__,
+ selectedAddonId);
+ }
+ }
+ }
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER)
+ {
+ std::string currentScraperId;
+ if (m_artistscraper)
+ currentScraperId = m_artistscraper->ID();
+ std::string selectedAddonId = currentScraperId;
+
+ if (CGUIWindowAddonBrowser::SelectAddonID(AddonType::SCRAPER_ARTISTS, selectedAddonId, false) ==
+ 1 &&
+ selectedAddonId != currentScraperId)
+ {
+ AddonPtr scraperAddon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(selectedAddonId, scraperAddon,
+ OnlyEnabled::CHOICE_YES))
+ {
+ m_artistscraper = std::dynamic_pointer_cast<CScraper>(scraperAddon);
+ SetupView();
+ SetFocus(settingId);
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "{} - Could not get addon: {}", __FUNCTION__, selectedAddonId);
+ }
+ }
+ }
+ else if (settingId == SETTING_ALBUMSCRAPER_SETTINGS)
+ CGUIDialogAddonSettings::ShowForAddon(m_albumscraper, false);
+ else if (settingId == SETTING_ARTISTSCRAPER_SETTINGS)
+ CGUIDialogAddonSettings::ShowForAddon(m_artistscraper, false);
+ else if (settingId == CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER)
+ {
+ VECSOURCES shares;
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+ CServiceBroker::GetMediaManager().GetRemovableDrives(shares);
+ std::string strDirectory = m_strArtistInfoPath;
+ 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(20223), strDirectory, true))
+ {
+ if (!strDirectory.empty())
+ {
+ m_strArtistInfoPath = strDirectory;
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, strDirectory);
+ SetFocus(CSettings::CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ }
+ }
+ }
+}
+
+bool CGUIDialogInfoProviderSettings::Save()
+{
+ if (m_showSingleScraper)
+ return true; //Save done by caller of ::Show
+
+ // Save default settings for fetching additional information and art
+ CLog::Log(LOGINFO, "{} called", __FUNCTION__);
+ // Save Fetch addiitional info during update
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ settings->SetBool(CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO, m_fetchInfo);
+ // Save default scrapers and addon setting values
+ settings->SetString(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, m_albumscraper->ID());
+ m_albumscraper->SaveSettings();
+ settings->SetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, m_artistscraper->ID());
+ m_artistscraper->SaveSettings();
+ // Save artist information folder
+ settings->SetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, m_strArtistInfoPath);
+ settings->Save();
+
+ return true;
+}
+
+void CGUIDialogInfoProviderSettings::SetupView()
+{
+ CGUIDialogSettingsManualBase::SetupView();
+
+ SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186);
+ SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222);
+
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, m_strArtistInfoPath);
+
+ if (!m_showSingleScraper)
+ {
+ SetHeading(38330);
+ if (!m_fetchInfo)
+ {
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, false);
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, false);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, false);
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, false);
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, false);
+ }
+ else
+ { // Album scraper
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, true);
+ if (m_albumscraper && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_albumscraper->ID()))
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, m_albumscraper->Name());
+ if (m_albumscraper && m_albumscraper->HasSettings())
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, true);
+ else
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, g_localizeStrings.Get(231)); //Set label2 to "None"
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, false);
+ }
+ // Artist scraper
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, true);
+ if (m_artistscraper && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_artistscraper->ID()))
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, m_artistscraper->Name());
+ if (m_artistscraper && m_artistscraper->HasSettings())
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, true);
+ else
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, g_localizeStrings.Get(231)); //Set label2 to "None"
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, false);
+ }
+ // Artist Information Folder
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, true);
+ }
+ }
+ else if (m_singleScraperType == CONTENT_ALBUMS)
+ {
+ SetHeading(38331);
+ // Album scraper
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, true);
+ if (m_albumscraper && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_albumscraper->ID()))
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, m_albumscraper->Name());
+ if (m_albumscraper && m_albumscraper->HasSettings())
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, true);
+ else
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, g_localizeStrings.Get(231)); //Set label2 to "None"
+ ToggleState(SETTING_ALBUMSCRAPER_SETTINGS, false);
+ }
+ }
+ else
+ {
+ SetHeading(38332);
+ // Artist scraper
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, true);
+ if (m_artistscraper && !CServiceBroker::GetAddonMgr().IsAddonDisabled(m_artistscraper->ID()))
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, m_artistscraper->Name());
+ if (m_artistscraper && m_artistscraper->HasSettings())
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, true);
+ else
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, false);
+ }
+ else
+ {
+ SetLabel2(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, g_localizeStrings.Get(231)); //Set label2 to "None"
+ ToggleState(SETTING_ARTISTSCRAPER_SETTINGS, false);
+ }
+ // Artist Information Folder when default settings
+ ToggleState(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, m_applyToItems == INFOPROVIDER_DEFAULT);
+ }
+}
+
+void CGUIDialogInfoProviderSettings::InitializeSettings()
+{
+ CGUIDialogSettingsManualBase::InitializeSettings();
+
+ std::shared_ptr<CSettingCategory> category = AddCategory("infoprovidersettings", -1);
+ if (category == nullptr)
+ {
+ CLog::Log(LOGERROR, "{}: unable to setup settings", __FUNCTION__);
+ return;
+ }
+ std::shared_ptr<CSettingGroup> group1 = AddGroup(category);
+ if (group1 == nullptr)
+ {
+ CLog::Log(LOGERROR, "{}: unable to setup settings", __FUNCTION__);
+ return;
+ }
+
+ if (!m_showSingleScraper)
+ {
+ AddToggle(group1, CSettings::SETTING_MUSICLIBRARY_DOWNLOADINFO, 38333, SettingLevel::Basic, m_fetchInfo); // "Fetch additional information during scan"
+ }
+ else
+ {
+ TranslatableIntegerSettingOptions entries;
+ entries.clear();
+ if (m_singleScraperType == CONTENT_ALBUMS)
+ {
+ entries.push_back(TranslatableIntegerSettingOption(38066, INFOPROVIDER_THISITEM));
+ entries.push_back(TranslatableIntegerSettingOption(38067, INFOPROVIDER_ALLVIEW));
+ }
+ else
+ {
+ entries.push_back(TranslatableIntegerSettingOption(38064, INFOPROVIDER_THISITEM));
+ entries.push_back(TranslatableIntegerSettingOption(38065, INFOPROVIDER_ALLVIEW));
+ }
+ entries.push_back(TranslatableIntegerSettingOption(38063, INFOPROVIDER_DEFAULT));
+ AddList(group1, SETTING_APPLYTOITEMS, 38338, SettingLevel::Basic, m_applyToItems, entries, 38339); // "Apply settings to"
+ }
+
+ std::shared_ptr<CSettingGroup> group = AddGroup(category, 38337);
+ if (group == nullptr)
+ {
+ CLog::Log(LOGERROR, "{}: unable to setup settings", __FUNCTION__);
+ return;
+ }
+ std::shared_ptr<CSettingAction> subsetting;
+ if (!m_showSingleScraper || m_singleScraperType == CONTENT_ALBUMS)
+ {
+ AddButton(group, CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER, 38334, SettingLevel::Basic); //Provider for album information
+ subsetting = AddButton(group, SETTING_ALBUMSCRAPER_SETTINGS, 10004, SettingLevel::Basic); //"settings"
+ if (subsetting)
+ subsetting->SetParent(CSettings::SETTING_MUSICLIBRARY_ALBUMSSCRAPER);
+ }
+ if (!m_showSingleScraper || m_singleScraperType == CONTENT_ARTISTS)
+ {
+ AddButton(group, CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER, 38335, SettingLevel::Basic); //Provider for artist information
+ subsetting = AddButton(group, SETTING_ARTISTSCRAPER_SETTINGS, 10004, SettingLevel::Basic); //"settings"
+ if (subsetting)
+ subsetting->SetParent(CSettings::SETTING_MUSICLIBRARY_ARTISTSSCRAPER);
+
+ AddButton(group, CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER, 38336, SettingLevel::Basic);
+ }
+}
+
+void CGUIDialogInfoProviderSettings::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 CGUIDialogInfoProviderSettings::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 CGUIDialogInfoProviderSettings::SetFocus(const std::string &settingid)
+{
+ BaseSettingControlPtr settingControl = GetSettingControl(settingid);
+ if (settingControl != NULL && settingControl->GetControl() != NULL)
+ SET_CONTROL_FOCUS(settingControl->GetID(), 0);
+}
+
+void CGUIDialogInfoProviderSettings::ResetDefaults()
+{
+ m_showSingleScraper = false;
+ m_singleScraperType = CONTENT_NONE;
+ m_applyToItems = INFOPROVIDER_THISITEM;
+}
diff --git a/xbmc/music/dialogs/GUIDialogInfoProviderSettings.h b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.h
new file mode 100644
index 0000000..56a33b5
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogInfoProviderSettings.h
@@ -0,0 +1,92 @@
+/*
+ * 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 "addons/Addon.h"
+#include "addons/Scraper.h"
+#include "settings/dialogs/GUIDialogSettingsManualBase.h"
+
+#include <map>
+#include <utility>
+
+class CFileItemList;
+
+// Enumeration of what combination of items to apply the scraper settings
+enum INFOPROVIDERAPPLYOPTIONS
+{
+ INFOPROVIDER_DEFAULT = 0x0000,
+ INFOPROVIDER_ALLVIEW = 0x0001,
+ INFOPROVIDER_THISITEM = 0x0002
+};
+
+class CGUIDialogInfoProviderSettings : public CGUIDialogSettingsManualBase
+{
+public:
+ CGUIDialogInfoProviderSettings();
+
+ // specialization of CGUIWindow
+ bool HasListItems() const override { return true; }
+
+ const ADDON::ScraperPtr& GetAlbumScraper() const { return m_albumscraper; }
+ void SetAlbumScraper(ADDON::ScraperPtr scraper) { m_albumscraper = std::move(scraper); }
+ const ADDON::ScraperPtr& GetArtistScraper() const { return m_artistscraper; }
+ void SetArtistScraper(ADDON::ScraperPtr scraper) { m_artistscraper = std::move(scraper); }
+
+ /*! \brief Show dialog to change information provider for either artists or albums (not both).
+ Has a list to select how settings are to be applied - as system default, to just current item or to all the filtered items on the node.
+ This does not save the settings itself, that is left to the caller
+ \param scraper [in/out] the selected scraper addon and settings. Scraper content must be artists or albums.
+ \return 0 settings apply as system default, 1 to all items on node, 2 to just the selected item or -1 if dialog cancelled or error occurs
+ */
+ static int Show(ADDON::ScraperPtr& scraper);
+
+ /*! \brief Show dialog to change the music scraping settings including default information providers for both artists or albums.
+ This saves the settings when the dialog is confirmed.
+ \return true if the dialog is confirmed, false otherwise
+ */
+ static bool Show();
+
+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 SetFocus(const std::string &settingid);
+ void ResetDefaults();
+
+ /*!
+ * @brief The currently selected album scraper
+ */
+ ADDON::ScraperPtr m_albumscraper;
+ /*!
+ * @brief The currently selected artist scraper
+ */
+ ADDON::ScraperPtr m_artistscraper;
+
+ std::string m_strArtistInfoPath;
+ bool m_showSingleScraper = false;
+ CONTENT_TYPE m_singleScraperType = CONTENT_NONE;
+ bool m_fetchInfo;
+ unsigned int m_applyToItems = INFOPROVIDER_THISITEM;
+};
diff --git a/xbmc/music/dialogs/GUIDialogMusicInfo.cpp b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
new file mode 100644
index 0000000..a4b282a
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogMusicInfo.cpp
@@ -0,0 +1,1043 @@
+/*
+ * 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 "GUIDialogMusicInfo.h"
+
+#include "FileItem.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "TextureCache.h"
+#include "URL.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogProgress.h"
+#include "filesystem/Directory.h"
+#include "filesystem/MusicDatabaseDirectory/QueryParams.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "music/MusicDatabase.h"
+#include "music/MusicLibraryQueue.h"
+#include "music/MusicThumbLoader.h"
+#include "music/MusicUtils.h"
+#include "music/dialogs/GUIDialogSongInfo.h"
+#include "music/infoscanner/MusicInfoScanner.h"
+#include "music/tags/MusicInfoTag.h"
+#include "music/windows/GUIWindowMusicBase.h"
+#include "profiles/ProfileManager.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/FileExtensionProvider.h"
+#include "utils/FileUtils.h"
+#include "utils/ProgressJob.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+
+using namespace XFILE;
+using namespace MUSIC_INFO;
+using namespace MUSICDATABASEDIRECTORY;
+using namespace KODI::MESSAGING;
+
+#define CONTROL_BTN_REFRESH 6
+#define CONTROL_USERRATING 7
+#define CONTROL_BTN_PLAY 8
+#define CONTROL_BTN_GET_THUMB 10
+#define CONTROL_ARTISTINFO 12
+
+#define CONTROL_LIST 50
+
+#define TIME_TO_BUSY_DIALOG 500
+
+class CGetInfoJob : public CJob
+{
+public:
+ ~CGetInfoJob(void) override = default;
+
+ // Fetch full album/artist information including art types list
+ bool DoWork() override
+ {
+ CGUIDialogMusicInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().
+ GetWindow<CGUIDialogMusicInfo>(WINDOW_DIALOG_MUSIC_INFO);
+ if (!dialog)
+ return false;
+ if (dialog->IsCancelled())
+ return false;
+ CFileItemPtr m_item = dialog->GetCurrentListItem();
+ CMusicInfoTag& tag = *m_item->GetMusicInfoTag();
+
+ CMusicDatabase database;
+ database.Open();
+ // May only have partially populated music item, so fetch all artist or album data from db
+ if (tag.GetType() == MediaTypeArtist)
+ {
+ int artistId = tag.GetDatabaseId();
+ CArtist artist;
+ if (!database.GetArtist(artistId, artist))
+ return false;
+ tag.SetArtist(artist);
+ CMusicDatabase::SetPropertiesFromArtist(*m_item, artist);
+ m_item->SetLabel(artist.strArtist);
+
+ // Get artist folder where local art could be found
+ // Get the *name* of the folder for this artist within the Artist Info folder (may not exist).
+ // If there is no Artist Info folder specified in settings this will be blank
+ database.GetArtistPath(artist, artist.strPath);
+ // Get the old location for those album artists with a unique folder (local to music files)
+ // If there is no folder for the artist and *only* the artist this will be blank
+ std::string oldartistpath;
+ bool oldpathfound = database.GetOldArtistPath(artist.idArtist, oldartistpath);
+
+ // Set up path for *item folder when browsing for art, by default this is
+ // in the Artist Info Folder (when it exists), but could end up blank
+ std::string artistItemPath = artist.strPath;
+ if (!CDirectory::Exists(artistItemPath))
+ {
+ // Fall back local to music files (historic location for those album artists with a unique folder)
+ // although there may not be such a unique folder for the arist
+ if (oldpathfound)
+ artistItemPath = oldartistpath;
+ else
+ // Fall back further to browse the Artist Info Folder itself
+ artistItemPath = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ }
+ m_item->SetPath(artistItemPath);
+
+ // Store info as CArtist as well as item properties
+ dialog->SetArtist(artist, oldartistpath);
+
+ // Fetch artist discography as scraped from online sources, but always
+ // include all the albums in the music library
+ dialog->SetDiscography(database);
+ }
+ else
+ {
+ // tag.GetType == MediaTypeAlbum
+ int albumId = tag.GetDatabaseId();
+ CAlbum album;
+ if (!database.GetAlbum(albumId, album))
+ return false;
+ tag.SetAlbum(album);
+ CMusicDatabase::SetPropertiesFromAlbum(*m_item, album);
+
+ // Get album folder where local art could be found
+ database.GetAlbumPath(albumId, album.strPath);
+ // Set up path for *item folder when browsing for art
+ m_item->SetPath(album.strPath);
+ // Store info as CAlbum as well as item properties
+ dialog->SetAlbum(album, album.strPath);
+
+ // Set the list of songs and related art
+ dialog->SetSongs(album.songs);
+ }
+ database.Close();
+
+ /*
+ Load current art (to CGUIListItem.m_art)
+ For albums this includes related artist(s) art and artist fanart set as
+ fallback album fanart.
+ Clear item art first to ensure fresh not cached/partial art
+ */
+ m_item->ClearArt();
+ CMusicThumbLoader loader;
+ loader.LoadItem(m_item.get());
+
+ // Fill vector of possible art types with current art, when it exists,
+ // for display on the art type selection dialog
+ CFileItemList artlist;
+ MUSIC_UTILS::FillArtTypesList(*m_item, artlist);
+ dialog->SetArtTypeList(artlist);
+ if (dialog->IsCancelled())
+ return false;
+
+ // Tell waiting MusicDialog that job is complete
+ dialog->FetchComplete();
+
+ return true;
+ }
+};
+
+class CSetUserratingJob : public CJob
+{
+ int idAlbum;
+ int iUserrating;
+public:
+ CSetUserratingJob(int albumId, int userrating) :
+ idAlbum(albumId),
+ iUserrating(userrating)
+ { }
+
+ ~CSetUserratingJob(void) override = default;
+
+ bool DoWork(void) override
+ {
+ // Asynchronously update userrating in library
+ CMusicDatabase db;
+ if (db.Open())
+ {
+ db.SetAlbumUserrating(idAlbum, iUserrating);
+ db.Close();
+ }
+
+ return true;
+ }
+};
+
+class CRefreshInfoJob : public CProgressJob
+{
+public:
+ CRefreshInfoJob(CGUIDialogProgress* progressDialog)
+ : CProgressJob(nullptr)
+ {
+ if (progressDialog)
+ SetProgressIndicators(nullptr, progressDialog);
+ SetAutoClose(true);
+ }
+
+ ~CRefreshInfoJob(void) override = default;
+
+ // Refresh album/artist information including art types list
+ bool DoWork() override
+ {
+ CGUIDialogMusicInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().
+ GetWindow<CGUIDialogMusicInfo>(WINDOW_DIALOG_MUSIC_INFO);
+ if (!dialog)
+ return false;
+ if (dialog->IsCancelled())
+ return false;
+ CFileItemPtr m_item = dialog->GetCurrentListItem();
+ CMusicInfoTag& tag = *m_item->GetMusicInfoTag();
+ CArtist& m_artist = dialog->GetArtist();
+ CAlbum& m_album = dialog->GetAlbum();
+
+ CGUIDialogProgress* dlgProgress = GetProgressDialog();
+ CMusicDatabase database;
+ database.Open();
+ if (tag.GetType() == MediaTypeArtist)
+ {
+ ADDON::ScraperPtr scraper;
+ if (!database.GetScraper(m_artist.idArtist, CONTENT_ARTISTS, scraper))
+ return false;
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ database.ClearArtistLastScrapedTime(m_artist.idArtist);
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ CMusicInfoScanner scanner;
+ if (scanner.UpdateArtistInfo(m_artist, scraper, true, dlgProgress) != CInfoScanner::INFO_ADDED)
+ return false;
+ else
+ // Tell info dialog, so can show message
+ dialog->SetScrapedInfo(true);
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ //That changed DB and m_artist, now update dialog item with new info and art
+ tag.SetArtist(m_artist);
+ CMusicDatabase::SetPropertiesFromArtist(*m_item, m_artist);
+
+ // Fetch artist discography as scraped from online sources, but always
+ // include all the albums in the music library
+ dialog->SetDiscography(database);
+ }
+ else
+ {
+ // tag.GetType == MediaTypeAlbum
+ ADDON::ScraperPtr scraper;
+ if (!database.GetScraper(m_album.idAlbum, CONTENT_ALBUMS, scraper))
+ return false;
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ database.ClearAlbumLastScrapedTime(m_album.idAlbum);
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ CMusicInfoScanner scanner;
+ if (scanner.UpdateAlbumInfo(m_album, scraper, true, GetProgressDialog()) != CInfoScanner::INFO_ADDED)
+ return false;
+ else
+ // Tell info dialog, so can show message
+ dialog->SetScrapedInfo(true);
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ //That changed DB and m_album, now update dialog item with new info and art
+ // Album songs are unchanged by refresh (even with Musicbrainz sync?)
+ tag.SetAlbum(m_album);
+ CMusicDatabase::SetPropertiesFromAlbum(*m_item, m_album);
+
+ // Set the list of songs and related art
+ dialog->SetSongs(m_album.songs);
+ }
+ database.Close();
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ /*
+ Load current art (to CGUIListItem.m_art)
+ For albums this includes related artist(s) art and artist fanart set as
+ fallback album fanart.
+ Clear item art first to ensure fresh not cached/partial art
+ */
+ m_item->ClearArt();
+ CMusicThumbLoader loader;
+ loader.LoadItem(m_item.get());
+
+ if (dlgProgress->IsCanceled())
+ return false;
+ // Fill vector of possible art types with current art, when it exists,
+ // for display on the art type selection dialog
+ CFileItemList artlist;
+ MUSIC_UTILS::FillArtTypesList(*m_item, artlist);
+ dialog->SetArtTypeList(artlist);
+ if (dialog->IsCancelled())
+ return false;
+
+ // Tell waiting MusicDialog that job is complete
+ MarkFinished();
+ return true;
+ }
+};
+
+CGUIDialogMusicInfo::CGUIDialogMusicInfo(void)
+ : CGUIDialog(WINDOW_DIALOG_MUSIC_INFO, "DialogMusicInfo.xml"),
+ m_albumSongs(new CFileItemList),
+ m_item(new CFileItem),
+ m_artTypeList(new CFileItemList)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogMusicInfo::~CGUIDialogMusicInfo(void)
+{
+}
+
+bool CGUIDialogMusicInfo::OnMessage(CGUIMessage& message)
+{
+ switch ( message.GetMessage() )
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ m_artTypeList->Clear();
+ // For albums update user rating if it has changed
+ if (!m_bArtistInfo && m_startUserrating != m_item->GetMusicInfoTag()->GetUserrating())
+ {
+ m_hasUpdatedUserrating = true;
+
+ // Asynchronously update song userrating in library
+ CSetUserratingJob *job = new CSetUserratingJob(m_item->GetMusicInfoTag()->GetAlbumId(),
+ m_item->GetMusicInfoTag()->GetUserrating());
+ CServiceBroker::GetJobManager()->AddJob(job, nullptr);
+ }
+ if (m_hasRefreshed || m_hasUpdatedUserrating)
+ {
+ // Send a message to all windows to tell them to update the item.
+ // This communicates changes to the music lib window.
+ // The music lib window item is updated to but changes to the rating when it is the sort
+ // do not show on screen until refresh() that fetches the list from scratch, sorts etc.
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_item);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
+ OnMessage(msg);
+ m_albumSongs->Clear();
+ }
+ break;
+
+ case GUI_MSG_WINDOW_INIT:
+ {
+ CGUIDialog::OnMessage(message);
+ Update();
+ m_cancelled = false;
+ return true;
+ }
+ break;
+
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_USERRATING)
+ {
+ OnSetUserrating();
+ }
+ else if (iControl == CONTROL_BTN_REFRESH)
+ {
+ RefreshInfo();
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_GET_THUMB)
+ {
+ OnGetArt();
+ return true;
+ }
+ else if (iControl == CONTROL_ARTISTINFO)
+ {
+ if (!m_bArtistInfo)
+ OnArtistInfo(m_album.artistCredits[0].GetArtistId());
+ return true;
+ }
+ else if (iControl == CONTROL_LIST)
+ {
+ int iAction = message.GetParam1();
+ if (m_bArtistInfo && (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction))
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ int iItem = msg.GetParam1();
+ int id = -1;
+ if (iItem >= 0 && iItem < m_albumSongs->Size())
+ id = m_albumSongs->Get(iItem)->GetMusicInfoTag()->GetDatabaseId();
+ if (id > 0)
+ {
+ OnAlbumInfo(id);
+ return true;
+ }
+ }
+ }
+ else if (iControl == CONTROL_BTN_PLAY)
+ {
+ if (m_album.idAlbum >= 0)
+ {
+ // Play album
+ const std::string path = StringUtils::Format("musicdb://albums/{}", m_album.idAlbum);
+ OnPlayItem(std::make_shared<CFileItem>(path, m_album));
+ return true;
+ }
+ else
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ const int iItem = msg.GetParam1();
+ if (iItem >= 0 && iItem < m_albumSongs->Size())
+ {
+ // Play selected song
+ OnPlayItem(m_albumSongs->Get(iItem));
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ break;
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogMusicInfo::OnAction(const CAction &action)
+{
+ int userrating = m_item->GetMusicInfoTag()->GetUserrating();
+ if (action.GetID() == ACTION_INCREASE_RATING)
+ {
+ SetUserrating(userrating + 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_DECREASE_RATING)
+ {
+ SetUserrating(userrating - 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogMusicInfo::SetItem(CFileItem* item)
+{
+ *m_item = *item;
+ m_event.Reset();
+ m_cancelled = false; // Happens before win_init
+
+ // In a separate job fetch info and fill list of art types.
+ int jobid =
+ CServiceBroker::GetJobManager()->AddJob(new CGetInfoJob(), nullptr, CJob::PRIORITY_LOW);
+
+ // Wait to get all data before show, allowing user to cancel if fetch is slow
+ if (!CGUIDialogBusy::WaitOnEvent(m_event, TIME_TO_BUSY_DIALOG))
+ {
+ // Cancel job still waiting in queue (unlikely)
+ CServiceBroker::GetJobManager()->CancelJob(jobid);
+ // Flag to stop job already in progress
+ m_cancelled = true;
+ return false;
+ }
+
+ return true;
+}
+
+void CGUIDialogMusicInfo::SetAlbum(const CAlbum& album, const std::string &path)
+{
+ m_album = album;
+ m_item->SetPath(album.strPath);
+
+ m_startUserrating = m_album.iUserrating;
+ m_fallbackartpath.clear();
+ m_bArtistInfo = false;
+ m_hasUpdatedUserrating = false;
+ m_hasRefreshed = false;
+}
+
+void CGUIDialogMusicInfo::SetArtist(const CArtist& artist, const std::string &path)
+{
+ m_artist = artist;
+ m_fallbackartpath = path;
+ m_bArtistInfo = true;
+ m_hasRefreshed = false;
+}
+
+void CGUIDialogMusicInfo::SetSongs(const VECSONGS &songs) const
+{
+ m_albumSongs->Clear();
+ CMusicThumbLoader loader;
+ for (unsigned int i = 0; i < songs.size(); i++)
+ {
+ const CSong& song = songs[i];
+ CFileItemPtr item(new CFileItem(song));
+ // Load the song art and related artist(s) (that may be different from album artist) art
+ loader.LoadItem(item.get());
+ m_albumSongs->Add(item);
+ }
+}
+
+void CGUIDialogMusicInfo::SetDiscography(CMusicDatabase& database) const
+{
+ m_albumSongs->Clear();
+ database.GetArtistDiscography(m_artist.idArtist, *m_albumSongs);
+ CMusicThumbLoader loader;
+ for (const auto& item : *m_albumSongs)
+ {
+ // Load all the album art and related artist(s) art (could be other collaborating artists)
+ loader.LoadItem(item.get());
+ if (item->GetMusicInfoTag()->GetDatabaseId() == -1)
+ item->SetArt("thumb", "DefaultAlbumCover.png");
+ }
+}
+
+void CGUIDialogMusicInfo::Update()
+{
+ if (m_bArtistInfo)
+ {
+ SET_CONTROL_HIDDEN(CONTROL_ARTISTINFO);
+ SET_CONTROL_HIDDEN(CONTROL_USERRATING);
+
+ CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_albumSongs.get());
+ OnMessage(message);
+
+ }
+ else
+ {
+ SET_CONTROL_VISIBLE(CONTROL_ARTISTINFO);
+ SET_CONTROL_VISIBLE(CONTROL_USERRATING);
+
+ CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_albumSongs.get());
+ OnMessage(message);
+
+ }
+
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+
+ // Disable the Choose Art button if the user isn't allowed it
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB,
+ profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser);
+}
+
+void CGUIDialogMusicInfo::SetLabel(int iControl, const std::string& strLabel)
+{
+ if (strLabel.empty())
+ {
+ SET_CONTROL_LABEL(iControl, 416);
+ }
+ else
+ {
+ SET_CONTROL_LABEL(iControl, strLabel);
+ }
+}
+
+void CGUIDialogMusicInfo::OnInitWindow()
+{
+ SET_CONTROL_LABEL(CONTROL_BTN_REFRESH, 184);
+ SET_CONTROL_LABEL(CONTROL_USERRATING, 38023);
+ SET_CONTROL_LABEL(CONTROL_BTN_GET_THUMB, 13511);
+ SET_CONTROL_LABEL(CONTROL_ARTISTINFO, 21891);
+ SET_CONTROL_LABEL(CONTROL_BTN_PLAY, 208);
+
+ if (m_bArtistInfo)
+ {
+ SET_CONTROL_HIDDEN(CONTROL_ARTISTINFO);
+ SET_CONTROL_HIDDEN(CONTROL_USERRATING);
+ SET_CONTROL_HIDDEN(CONTROL_BTN_PLAY);
+ }
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogMusicInfo::FetchComplete()
+{
+ //Trigger the event to indicate data has been fetched
+ m_event.Set();
+}
+
+
+void CGUIDialogMusicInfo::RefreshInfo()
+{
+ // Double check we have permission (button should be hidden when not)
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+ if (!profileManager->GetCurrentProfile().canWriteDatabases() && !g_passwordManager.bMasterUser)
+ return;
+
+ // Check if scanning
+ if (CMusicLibraryQueue::GetInstance().IsScanningLibrary())
+ {
+ HELPERS::ShowOKDialogText(CVariant{ 189 }, CVariant{ 14057 });
+ return;
+ }
+
+ CGUIDialogProgress* dlgProgress = CServiceBroker::GetGUI()->GetWindowManager().
+ GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
+ if (!dlgProgress)
+ return;
+
+ if (m_bArtistInfo)
+ { // Show dialog box indicating we're searching for the artist
+ dlgProgress->SetHeading(CVariant{ 21889 });
+ dlgProgress->SetLine(0, CVariant{ m_artist.strArtist });
+ dlgProgress->SetLine(1, CVariant{ "" });
+ dlgProgress->SetLine(2, CVariant{ "" });
+ }
+ else
+ { // Show dialog box indicating we're searching for the album
+ dlgProgress->SetHeading(CVariant{ 185 });
+ dlgProgress->SetLine(0, CVariant{ m_album.strAlbum });
+ dlgProgress->SetLine(1, CVariant{ m_album.strArtistDesc });
+ dlgProgress->SetLine(2, CVariant{ "" });
+ }
+ dlgProgress->Open();
+
+ SetScrapedInfo(false);
+ // Start separate job to scrape info and fill list of art types.
+ CServiceBroker::GetJobManager()->AddJob(new CRefreshInfoJob(dlgProgress), nullptr,
+ CJob::PRIORITY_HIGH);
+
+ // Wait for refresh to complete or be canceled, but render every 10ms so that the
+ // pointer movements works on dialog even when job is reporting progress infrequently
+ if (dlgProgress)
+ dlgProgress->Wait(10);
+
+ if (dlgProgress->IsCanceled())
+ {
+ return;
+ }
+
+ // Show message when scraper was unsuccessful
+ if (!HasScrapedInfo())
+ {
+ if (m_bArtistInfo)
+ HELPERS::ShowOKDialogText(CVariant{ 21889 }, CVariant{ 20199 });
+ else
+ HELPERS::ShowOKDialogText(CVariant{ 185 }, CVariant{ 500 });
+ return;
+ }
+
+ // Show new values on screen
+ Update();
+ m_hasRefreshed = true;
+
+ if (dlgProgress)
+ dlgProgress->Close();
+}
+
+void CGUIDialogMusicInfo::SetUserrating(int userrating) const
+{
+ userrating = std::max(userrating, 0);
+ userrating = std::min(userrating, 10);
+ if (userrating != m_item->GetMusicInfoTag()->GetUserrating())
+ {
+ m_item->GetMusicInfoTag()->SetUserrating(userrating);
+ }
+}
+
+void CGUIDialogMusicInfo::OnAlbumInfo(int id)
+{
+ // Switch to show album info for given album ID
+ // Close current (artist) dialog to save art changes
+ Close(true);
+ ShowForAlbum(id);
+}
+
+void CGUIDialogMusicInfo::OnArtistInfo(int id)
+{
+ // Switch to show artist info for given artist ID
+ // Close current (album) dialog to save art and rating changes
+ Close(true);
+ ShowForArtist(id);
+}
+
+CFileItemPtr CGUIDialogMusicInfo::GetCurrentListItem(int offset)
+{
+ return m_item;
+}
+
+std::string CGUIDialogMusicInfo::GetContent()
+{
+ if (m_item->GetMusicInfoTag()->GetType() == MediaTypeArtist)
+ return "artists";
+ else
+ return "albums";
+}
+
+void CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
+{
+ std::string itemDir;
+ std::string artistFolder;
+
+ itemDir = item.GetPath();
+ if (item.HasMusicInfoTag())
+ {
+ if (item.GetMusicInfoTag()->GetType() == MediaTypeSong)
+ itemDir = URIUtils::GetParentPath(item.GetMusicInfoTag()->GetURL());
+
+ // For artist add Artist Info Folder path to browser sources
+ if (item.GetMusicInfoTag()->GetType() == MediaTypeArtist)
+ {
+ artistFolder = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_MUSICLIBRARY_ARTISTSFOLDER);
+ if (!artistFolder.empty() && artistFolder.compare(itemDir) == 0)
+ itemDir.clear(); // skip *item when artist not have a unique path
+ }
+ }
+ // Add "*Item folder" path to file browser sources
+ if (!itemDir.empty() && CDirectory::Exists(itemDir))
+ {
+ CMediaSource itemSource;
+ itemSource.strName = g_localizeStrings.Get(36041);
+ itemSource.strPath = itemDir;
+ sources.push_back(itemSource);
+ }
+
+ // For artist add Artist Info Folder path to browser sources
+ if (!artistFolder.empty() && CDirectory::Exists(artistFolder))
+ {
+ CMediaSource itemSource;
+ itemSource.strName = "* " + g_localizeStrings.Get(20223);
+ itemSource.strPath = artistFolder;
+ sources.push_back(itemSource);
+ }
+}
+
+void CGUIDialogMusicInfo::SetArtTypeList(CFileItemList& artlist)
+{
+ m_artTypeList->Clear();
+ m_artTypeList->Copy(artlist);
+}
+
+/*
+Allow user to choose artwork for the artist or album
+For each type of art the options are:
+1. Current art
+2. Local art (thumb found by filename)
+3. Remote art (scraped list of urls from online sources e.g. fanart.tv)
+5. Embedded art (@todo)
+6. None
+*/
+void CGUIDialogMusicInfo::OnGetArt()
+{
+ std::string type = MUSIC_UTILS::ShowSelectArtTypeDialog(*m_artTypeList);
+ if (type.empty())
+ return; // Cancelled
+
+ CFileItemList items;
+ CGUIListItem::ArtMap primeArt = m_item->GetArt(); // art without fallbacks
+ bool bHasArt = m_item->HasArt(type);
+ bool bFallback(false);
+ if (bHasArt)
+ {
+ // Check if that type of art is actually a fallback, e.g. artist fanart
+ CGUIListItem::ArtMap::const_iterator i = primeArt.find(type);
+ bFallback = (i == primeArt.end());
+ }
+
+ // Build list of possible images of that art type
+ if (bHasArt)
+ {
+ // Add item for current artwork
+ // For album it could be a fallback from artist
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", m_item->GetArt(type));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13512));
+ items.Add(item);
+ }
+
+ // Grab the thumbnails of this art type scraped from the web
+ std::vector<std::string> remotethumbs;
+ // Art type is encoded into the scraper XML as optional "aspect=" field
+ // Type "thumb" returns URLs for all types of art including those without aspect.
+ // Those URL without aspect are also returned for all other type values.
+ if (m_bArtistInfo)
+ m_artist.thumbURL.GetThumbUrls(remotethumbs, type);
+ else
+ m_album.thumbURL.GetThumbUrls(remotethumbs, type);
+
+ for (unsigned int i = 0; i < remotethumbs.size(); ++i)
+ {
+ std::string strItemPath;
+ strItemPath = StringUtils::Format("thumb://Remote{}", i);
+ CFileItemPtr item(new CFileItem(strItemPath, false));
+ item->SetArt("thumb", remotethumbs[i]);
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13513));
+
+ items.Add(item);
+ }
+
+ // Local art
+ std::string localArt;
+ std::vector<std::string> paths;
+ if (m_bArtistInfo)
+ {
+ // Individual artist subfolder within the Artist Information Folder
+ paths.emplace_back(m_artist.strPath);
+ // Fallback local to music files (when there is a unique folder)
+ paths.emplace_back(m_fallbackartpath);
+ }
+ else
+ // Album folder, when a unique one exists, no fallback
+ paths.emplace_back(m_album.strPath);
+ for (const auto& path : paths)
+ {
+ if (!localArt.empty() && CFileUtils::Exists(localArt))
+ break;
+ if (!path.empty())
+ {
+ CFileItem item(path, true);
+ if (type == "thumb")
+ // Local music thumbnail images named by <musicthumbs>
+ localArt = item.GetUserMusicThumb(true);
+ else
+ { // Check case and ext insenitively for local images with type as name
+ // e.g. <arttype>.jpg
+ CFileItemList items;
+ CDirectory::GetDirectory(path, items,
+ CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(),
+ DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_READ_CACHE | DIR_FLAG_NO_FILE_INFO);
+
+ for (int j = 0; j < items.Size(); j++)
+ {
+ std::string strCandidate = URIUtils::GetFileName(items[j]->GetPath());
+ URIUtils::RemoveExtension(strCandidate);
+ if (StringUtils::EqualsNoCase(strCandidate, type))
+ {
+ localArt = items[j]->GetPath();
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!localArt.empty() && CFileUtils::Exists(localArt))
+ {
+ CFileItemPtr item(new CFileItem("Local Art: " + localArt, false));
+ item->SetArt("thumb", localArt);
+ item->SetLabel(g_localizeStrings.Get(13514)); // "Local art"
+ items.Add(item);
+ }
+
+ // No art
+ if (bHasArt && !bFallback)
+ { // Actually has this type of art (not a fallback) so
+ // allow the user to delete it by selecting "no art".
+ CFileItemPtr item(new CFileItem("thumb://None", false));
+ if (m_bArtistInfo)
+ item->SetArt("icon", "DefaultArtist.png");
+ else
+ item->SetArt("icon", "DefaultAlbumCover.png");
+ item->SetLabel(g_localizeStrings.Get(13515));
+ items.Add(item);
+ }
+
+ //! @todo: Add support for extracting embedded art from song files to use for album
+
+ // Clear local images of this type from cache so user will see any recent
+ // local file changes immediately
+ for (auto& item : items)
+ {
+ // Skip images from remote sources, recache done by refresh (could be slow)
+ if (StringUtils::StartsWith(item->GetPath(), "thumb://Remote"))
+ continue;
+ std::string thumb(item->GetArt("thumb"));
+ if (thumb.empty())
+ continue;
+ CURL url(CTextureUtils::UnwrapImageURL(thumb));
+ // Skip images from remote sources (current thumb could be remote)
+ if (url.IsProtocol("http") || url.IsProtocol("https"))
+ continue;
+ CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
+ // Remove any thumbnail of local image too (created when browsing files)
+ std::string thumbthumb(CTextureUtils::GetWrappedThumbURL(thumb));
+ CServiceBroker::GetTextureCache()->ClearCachedImage(thumbthumb);
+ }
+
+ // Show list of possible art for user selection
+ // Note that during selection thumbs of *all* images shown are cached. When
+ // browsing folders there could be many irrelevant thumbs cached that are
+ // never used by Kodi again, but there is no obvious way to clear these
+ // thumbs from the cache automatically.
+ std::string result;
+ VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("music"));
+ CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_item);
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
+ result != "thumb://Current")
+ {
+ // User didn't choose the one they have.
+ // Overwrite with the new art or clear it
+ std::string newArt;
+ if (StringUtils::StartsWith(result, "thumb://Remote"))
+ {
+ int number = atoi(result.substr(14).c_str());
+ newArt = remotethumbs[number];
+ }
+ else if (result == "thumb://Thumb")
+ newArt = m_item->GetArt("thumb");
+ else if (StringUtils::StartsWith(result, "Local Art: "))
+ newArt = localArt;
+ else if (CFileUtils::Exists(result))
+ newArt = result;
+ else // none
+ newArt.clear();
+
+ // Asynchronously update that type of art in the database and then
+ // refresh artist, album and fallback art of currently playing song
+ MUSIC_UTILS::UpdateArtJob(m_item, type, newArt);
+
+ // Update local item and art list with current art
+ m_item->SetArt(type, newArt);
+ for (const auto& artitem : *m_artTypeList)
+ {
+ if (artitem->GetProperty("artType") == type)
+ {
+ artitem->SetArt("thumb", newArt);
+ break;
+ }
+ }
+
+ // Get new artwork to show in other places e.g. on music lib window, but this does
+ // not update artist, album or fallback art for the currently playing song as it
+ // is a different item with different ID and media type
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_item);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+
+ // Re-open the art type selection dialog as we come back from
+ // the image selection dialog
+ OnGetArt();
+}
+
+void CGUIDialogMusicInfo::OnSetUserrating() const
+{
+ int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_item->GetMusicInfoTag()->GetUserrating());
+ if (userrating < 0) // Nothing selected, so rating unchanged
+ return;
+
+ SetUserrating(userrating);
+}
+
+
+void CGUIDialogMusicInfo::ShowForAlbum(int idAlbum)
+{
+ std::string path = StringUtils::Format("musicdb://albums/{}", idAlbum);
+ CFileItem item(path, true); // An album, but IsAlbum() not set as didn't use SetAlbum()
+ ShowFor(&item);
+}
+
+void CGUIDialogMusicInfo::ShowForArtist(int idArtist)
+{
+ std::string path = StringUtils::Format("musicdb://artists/{}", idArtist);
+ CFileItem item(path, true);
+ ShowFor(&item);
+}
+
+void CGUIDialogMusicInfo::ShowFor(CFileItem* pItem)
+{
+ if (pItem->IsParentFolder() || URIUtils::IsSpecial(pItem->GetPath()) ||
+ StringUtils::StartsWithNoCase(pItem->GetPath(), "musicsearch://"))
+ return; // nothing to do
+
+ if (!pItem->m_bIsFolder)
+ { // Show Song information dialog
+ CGUIDialogSongInfo::ShowFor(pItem);
+ return;
+ }
+
+ CFileItem musicitem("musicdb://", true);
+
+ // We have a folder album/artist info dialog only shown for db items
+ // or for music video with artist/album in music library
+ if (pItem->IsMusicDb())
+ {
+ if (!pItem->HasMusicInfoTag() || pItem->GetMusicInfoTag()->GetDatabaseId() < 1)
+ {
+ // Maybe only path is set, then set MusicInfoTag
+ CQueryParams params;
+ CDirectoryNode::GetDatabaseInfo(pItem->GetPath(), params);
+ if (params.GetArtistId() > 0)
+ pItem->GetMusicInfoTag()->SetDatabaseId(params.GetArtistId(), MediaTypeArtist);
+ else if (params.GetAlbumId() > 0)
+ pItem->GetMusicInfoTag()->SetDatabaseId(params.GetAlbumId(), MediaTypeAlbum);
+ else
+ return; // nothing to do
+ }
+ musicitem.SetFromMusicInfoTag(*pItem->GetMusicInfoTag());
+ }
+ else if (pItem->HasProperty("artist_musicid"))
+ {
+ musicitem.GetMusicInfoTag()->SetDatabaseId(pItem->GetProperty("artist_musicid").asInteger32(),
+ MediaTypeArtist);
+ }
+ else if (pItem->HasProperty("album_musicid"))
+ {
+ musicitem.GetMusicInfoTag()->SetDatabaseId(pItem->GetProperty("album_musicid").asInteger32(),
+ MediaTypeAlbum);
+ }
+ else
+ return; // nothing to do
+
+
+ CGUIDialogMusicInfo *pDlgMusicInfo = CServiceBroker::GetGUI()->GetWindowManager().
+ GetWindow<CGUIDialogMusicInfo>(WINDOW_DIALOG_MUSIC_INFO);
+ if (pDlgMusicInfo)
+ {
+ if (pDlgMusicInfo->SetItem(&musicitem))
+ {
+ pDlgMusicInfo->Open();
+ if (pItem->GetMusicInfoTag()->GetType() == MediaTypeAlbum &&
+ pDlgMusicInfo->HasUpdatedUserrating())
+ {
+ auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowMusicBase>(WINDOW_MUSIC_NAV);
+ if (window)
+ window->RefreshContent("albums");
+ }
+ }
+ }
+}
+
+void CGUIDialogMusicInfo::OnPlayItem(const std::shared_ptr<CFileItem>& item)
+{
+ Close(true);
+ MUSIC_UTILS::PlayItem(item);
+}
diff --git a/xbmc/music/dialogs/GUIDialogMusicInfo.h b/xbmc/music/dialogs/GUIDialogMusicInfo.h
new file mode 100644
index 0000000..781a7d1
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogMusicInfo.h
@@ -0,0 +1,80 @@
+/*
+ * 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 "MediaSource.h"
+#include "guilib/GUIDialog.h"
+#include "music/Album.h"
+#include "music/Artist.h"
+#include "music/Song.h"
+#include "threads/Event.h"
+
+#include <memory>
+
+class CFileItem;
+class CFileItemList;
+
+class CGUIDialogMusicInfo :
+ public CGUIDialog
+{
+public:
+ CGUIDialogMusicInfo(void);
+ ~CGUIDialogMusicInfo(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool OnAction(const CAction &action) override;
+ bool SetItem(CFileItem* item);
+ void SetAlbum(const CAlbum& album, const std::string &path);
+ void SetArtist(const CArtist& artist, const std::string &path);
+ bool HasUpdatedUserrating() const { return m_hasUpdatedUserrating; }
+ bool HasRefreshed() const { return m_hasRefreshed; }
+
+ bool HasListItems() const override { return true; }
+ CFileItemPtr GetCurrentListItem(int offset = 0) override;
+ std::string GetContent();
+ static void AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item);
+ void SetDiscography(CMusicDatabase& database) const;
+ void SetSongs(const VECSONGS &songs) const;
+ void SetArtTypeList(CFileItemList& artlist);
+ void SetScrapedInfo(bool bScraped) { m_scraperAddInfo = bScraped; }
+ CArtist& GetArtist() { return m_artist; }
+ CAlbum& GetAlbum() { return m_album; }
+ bool IsArtistInfo() const { return m_bArtistInfo; }
+ bool IsCancelled() const { return m_cancelled; }
+ bool HasScrapedInfo() const { return m_scraperAddInfo; }
+ void FetchComplete();
+ void RefreshInfo();
+
+ static void ShowForAlbum(int idAlbum);
+ static void ShowForArtist(int idArtist);
+ static void ShowFor(CFileItem* pItem);
+protected:
+ void OnInitWindow() override;
+ void Update();
+ void SetLabel(int iControl, const std::string& strLabel);
+ void OnGetArt();
+ void OnAlbumInfo(int id);
+ void OnArtistInfo(int id);
+ void OnSetUserrating() const;
+ void SetUserrating(int userrating) const;
+ void OnPlayItem(const std::shared_ptr<CFileItem>& item);
+
+ CAlbum m_album;
+ CArtist m_artist;
+ int m_startUserrating = -1;
+ bool m_hasUpdatedUserrating = false;
+ bool m_hasRefreshed = false;
+ bool m_bArtistInfo = false;
+ bool m_cancelled = false;
+ bool m_scraperAddInfo = false;
+ std::unique_ptr<CFileItemList> m_albumSongs;
+ std::shared_ptr<CFileItem> m_item;
+ std::unique_ptr<CFileItemList> m_artTypeList;
+ CEvent m_event;
+ std::string m_fallbackartpath;
+};
diff --git a/xbmc/music/dialogs/GUIDialogMusicOSD.cpp b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp
new file mode 100644
index 0000000..6cd110b
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogMusicOSD.cpp
@@ -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.
+ */
+
+#include "GUIDialogMusicOSD.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "addons/addoninfo/AddonType.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "input/InputManager.h"
+#include "input/Key.h"
+#include "settings/Settings.h"
+#include "settings/SettingsComponent.h"
+
+#define CONTROL_VIS_BUTTON 500
+#define CONTROL_LOCK_BUTTON 501
+
+CGUIDialogMusicOSD::CGUIDialogMusicOSD(void)
+ : CGUIDialog(WINDOW_DIALOG_MUSIC_OSD, "MusicOSD.xml")
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogMusicOSD::~CGUIDialogMusicOSD(void) = default;
+
+bool CGUIDialogMusicOSD::OnMessage(CGUIMessage &message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_CLICKED:
+ {
+ unsigned int iControl = message.GetSenderId();
+ if (iControl == CONTROL_VIS_BUTTON)
+ {
+ std::string addonID;
+ if (CGUIWindowAddonBrowser::SelectAddonID(ADDON::AddonType::VISUALIZATION, addonID, true) ==
+ 1)
+ {
+ const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
+ settings->SetString(CSettings::SETTING_MUSICPLAYER_VISUALISATION, addonID);
+ settings->Save();
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_VISUALISATION_RELOAD, 0, 0);
+ }
+ }
+ else if (iControl == CONTROL_LOCK_BUTTON)
+ {
+ CGUIMessage msg(GUI_MSG_VISUALISATION_ACTION, 0, 0, ACTION_VIS_PRESET_LOCK);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+ return true;
+ }
+ break;
+ }
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogMusicOSD::OnAction(const CAction &action)
+{
+ switch (action.GetID())
+ {
+ case ACTION_SHOW_OSD:
+ Close();
+ return true;
+ default:
+ break;
+ }
+
+ return CGUIDialog::OnAction(action);
+}
+
+void CGUIDialogMusicOSD::FrameMove()
+{
+ if (m_autoClosing)
+ {
+ // check for movement of mouse or a submenu open
+ if (CServiceBroker::GetInputManager().IsMouseActive() ||
+ CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_VIS_SETTINGS) ||
+ CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_VIS_PRESET_LIST) ||
+ CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_PVR_RADIO_RDS_INFO))
+ // extend show time by original value
+ SetAutoClose(m_showDuration);
+ }
+ CGUIDialog::FrameMove();
+}
diff --git a/xbmc/music/dialogs/GUIDialogMusicOSD.h b/xbmc/music/dialogs/GUIDialogMusicOSD.h
new file mode 100644
index 0000000..99a208c
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogMusicOSD.h
@@ -0,0 +1,22 @@
+/*
+ * 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/GUIDialog.h"
+
+class CGUIDialogMusicOSD :
+ public CGUIDialog
+{
+public:
+ CGUIDialogMusicOSD(void);
+ ~CGUIDialogMusicOSD(void) override;
+ bool OnMessage(CGUIMessage &message) override;
+ bool OnAction(const CAction &action) override;
+ void FrameMove() override;
+};
diff --git a/xbmc/music/dialogs/GUIDialogSongInfo.cpp b/xbmc/music/dialogs/GUIDialogSongInfo.cpp
new file mode 100644
index 0000000..8efca94
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogSongInfo.cpp
@@ -0,0 +1,523 @@
+/*
+ * 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 "GUIDialogSongInfo.h"
+
+#include "GUIDialogMusicInfo.h"
+#include "GUIPassword.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "TextureCache.h"
+#include "Util.h"
+#include "dialogs/GUIDialogBusy.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "music/MusicDatabase.h"
+#include "music/MusicUtils.h"
+#include "music/tags/MusicInfoTag.h"
+#include "music/windows/GUIWindowMusicBase.h"
+#include "profiles/ProfileManager.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/SettingsComponent.h"
+#include "storage/MediaManager.h"
+#include "utils/FileUtils.h"
+
+#define CONTROL_BTN_REFRESH 6
+#define CONTROL_USERRATING 7
+#define CONTROL_BTN_PLAY 8
+#define CONTROL_BTN_GET_THUMB 10
+#define CONTROL_ALBUMINFO 12
+
+#define CONTROL_LIST 50
+
+#define TIME_TO_BUSY_DIALOG 500
+
+
+
+class CGetSongInfoJob : public CJob
+{
+public:
+ ~CGetSongInfoJob(void) override = default;
+
+ // Fetch full song information including art types list
+ bool DoWork() override
+ {
+ CGUIDialogSongInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSongInfo>(WINDOW_DIALOG_SONG_INFO);
+ if (!dialog)
+ return false;
+ if (dialog->IsCancelled())
+ return false;
+ CFileItemPtr m_song = dialog->GetCurrentListItem();
+
+ // Fetch tag data from library using filename of item path, or scanning file
+ // (if item does not already have this loaded)
+ if (!m_song->LoadMusicTag())
+ {
+ // Stop SongInfoDialog waiting
+ dialog->FetchComplete();
+ return false;
+ }
+ if (dialog->IsCancelled())
+ return false;
+ // Fetch album and primary song artist data from library as properties
+ // and lyrics by scanning tags from file
+ MUSIC_INFO::CMusicInfoLoader::LoadAdditionalTagInfo(m_song.get());
+ if (dialog->IsCancelled())
+ return false;
+
+ // Get album path (for use in browsing art selection)
+ std::string albumpath;
+ CMusicDatabase db;
+ db.Open();
+ db.GetAlbumPath(m_song->GetMusicInfoTag()->GetAlbumId(), albumpath);
+ m_song->SetProperty("album_path", albumpath);
+ db.Close();
+ if (dialog->IsCancelled())
+ return false;
+
+ // Load song art.
+ // For songs in library this includes related album and artist(s) art.
+ // Also fetches artist art for non library songs when artist can be found
+ // uniquely by name, otherwise just embedded or cached thumb is fetched.
+ CMusicThumbLoader loader;
+ loader.LoadItem(m_song.get());
+ if (dialog->IsCancelled())
+ return false;
+
+ // For songs in library fill vector of possible art types, with current art when it exists
+ // for display on the art type selection dialog
+ CFileItemList artlist;
+ MUSIC_UTILS::FillArtTypesList(*m_song, artlist);
+ dialog->SetArtTypeList(artlist);
+ if (dialog->IsCancelled())
+ return false;
+
+ // Tell waiting SongInfoDialog that job is complete
+ dialog->FetchComplete();
+
+ return true;
+ }
+};
+
+CGUIDialogSongInfo::CGUIDialogSongInfo(void)
+ : CGUIDialog(WINDOW_DIALOG_SONG_INFO, "DialogMusicInfo.xml")
+ , m_song(new CFileItem)
+{
+ m_cancelled = false;
+ m_hasUpdatedUserrating = false;
+ m_startUserrating = -1;
+ m_artTypeList.Clear();
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+CGUIDialogSongInfo::~CGUIDialogSongInfo(void) = default;
+
+bool CGUIDialogSongInfo::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_WINDOW_DEINIT:
+ {
+ m_artTypeList.Clear();
+ if (m_startUserrating != m_song->GetMusicInfoTag()->GetUserrating())
+ {
+ m_hasUpdatedUserrating = true;
+
+ // Asynchronously update song userrating in library
+ MUSIC_UTILS::UpdateSongRatingJob(m_song, m_song->GetMusicInfoTag()->GetUserrating());
+
+ // Send a message to all windows to tell them to update the fileitem
+ // This communicates the rating change to the music lib window, current playlist and OSD.
+ // The music lib window item is updated to but changes to the rating when it is the sort
+ // do not show on screen until refresh() that fetches the list from scratch, sorts etc.
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ }
+ CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
+ OnMessage(msg);
+ break;
+ }
+ case GUI_MSG_WINDOW_INIT:
+ CGUIDialog::OnMessage(message);
+ Update();
+ m_cancelled = false;
+ break;
+
+ case GUI_MSG_CLICKED:
+ {
+ int iControl = message.GetSenderId();
+ if (iControl == CONTROL_USERRATING)
+ {
+ OnSetUserrating();
+ }
+ else if (iControl == CONTROL_ALBUMINFO)
+ {
+ CGUIDialogMusicInfo::ShowForAlbum(m_albumId);
+ return true;
+ }
+ else if (iControl == CONTROL_BTN_GET_THUMB)
+ {
+ OnGetArt();
+ return true;
+ }
+ else if (iControl == CONTROL_LIST)
+ {
+ int iAction = message.GetParam1();
+ if ((ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction))
+ {
+ CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ int iItem = msg.GetParam1();
+ if (iItem < 0 || iItem >= static_cast<int>(m_song->GetMusicInfoTag()->GetContributors().size()))
+ break;
+ int idArtist = m_song->GetMusicInfoTag()->GetContributors()[iItem].GetArtistId();
+ if (idArtist > 0)
+ CGUIDialogMusicInfo::ShowForArtist(idArtist);
+ return true;
+ }
+ }
+ else if (iControl == CONTROL_BTN_PLAY)
+ {
+ OnPlaySong(m_song);
+ return true;
+ }
+ return false;
+ }
+ break;
+ }
+
+ return CGUIDialog::OnMessage(message);
+}
+
+bool CGUIDialogSongInfo::OnAction(const CAction& action)
+{
+ int userrating = m_song->GetMusicInfoTag()->GetUserrating();
+ if (action.GetID() == ACTION_INCREASE_RATING)
+ {
+ SetUserrating(userrating + 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_DECREASE_RATING)
+ {
+ SetUserrating(userrating - 1);
+ return true;
+ }
+ else if (action.GetID() == ACTION_SHOW_INFO)
+ {
+ Close();
+ return true;
+ }
+ return CGUIDialog::OnAction(action);
+}
+
+bool CGUIDialogSongInfo::OnBack(int actionID)
+{
+ m_cancelled = true;
+ return CGUIDialog::OnBack(actionID);
+}
+
+void CGUIDialogSongInfo::FetchComplete()
+{
+ //Trigger the event to indicate data has been fetched
+ m_event.Set();
+}
+
+void CGUIDialogSongInfo::OnInitWindow()
+{
+ // Enable album info button when we know album
+ m_albumId = m_song->GetMusicInfoTag()->GetAlbumId();
+
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_ALBUMINFO, m_albumId > 0);
+
+ // Disable music user rating button for plugins as they don't have tables to save this
+ if (m_song->IsPlugin())
+ CONTROL_DISABLE(CONTROL_USERRATING);
+ else
+ CONTROL_ENABLE(CONTROL_USERRATING);
+
+ // Disable the Choose Art button if the user isn't allowed it
+ const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager();
+ CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB,
+ profileManager->GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser);
+
+ SET_CONTROL_HIDDEN(CONTROL_BTN_REFRESH);
+ SET_CONTROL_LABEL(CONTROL_USERRATING, 38023);
+ SET_CONTROL_LABEL(CONTROL_BTN_GET_THUMB, 13511);
+ SET_CONTROL_LABEL(CONTROL_ALBUMINFO, 10523);
+ SET_CONTROL_LABEL(CONTROL_BTN_PLAY, 208);
+
+ CGUIDialog::OnInitWindow();
+}
+
+void CGUIDialogSongInfo::Update()
+{
+ CFileItemList items;
+ for (const auto& contributor : m_song->GetMusicInfoTag()->GetContributors())
+ {
+ auto item = std::make_shared<CFileItem>(contributor.GetRoleDesc());
+ item->SetLabel2(contributor.GetArtist());
+ item->GetMusicInfoTag()->SetDatabaseId(contributor.GetArtistId(), MediaTypeArtist);
+ items.Add(std::move(item));
+ }
+ CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, &items);
+ OnMessage(message);
+}
+
+void CGUIDialogSongInfo::SetUserrating(int userrating)
+{
+ userrating = std::max(userrating, 0);
+ userrating = std::min(userrating, 10);
+ if (userrating != m_song->GetMusicInfoTag()->GetUserrating())
+ {
+ m_song->GetMusicInfoTag()->SetUserrating(userrating);
+ }
+}
+
+bool CGUIDialogSongInfo::SetSong(CFileItem* item)
+{
+ *m_song = *item;
+ m_event.Reset();
+ m_cancelled = false; // SetSong happens before win_init
+ // In a separate job fetch song info and fill list of art types.
+ int jobid =
+ CServiceBroker::GetJobManager()->AddJob(new CGetSongInfoJob(), nullptr, CJob::PRIORITY_LOW);
+
+ // Wait to get all data before show, allowing user to cancel if fetch is slow
+ if (!CGUIDialogBusy::WaitOnEvent(m_event, TIME_TO_BUSY_DIALOG))
+ {
+ // Cancel job still waiting in queue (unlikely)
+ CServiceBroker::GetJobManager()->CancelJob(jobid);
+ // Flag to stop job already in progress
+ m_cancelled = true;
+ return false;
+ }
+
+ // Store initial userrating
+ m_startUserrating = m_song->GetMusicInfoTag()->GetUserrating();
+ m_hasUpdatedUserrating = false;
+ return true;
+}
+
+void CGUIDialogSongInfo::SetArtTypeList(CFileItemList& artlist)
+{
+ m_artTypeList.Copy(artlist);
+}
+
+CFileItemPtr CGUIDialogSongInfo::GetCurrentListItem(int offset)
+{
+ return m_song;
+}
+
+std::string CGUIDialogSongInfo::GetContent()
+{
+ return "songs";
+}
+
+/*
+ Allow user to choose artwork for the song
+ For each type of art the options are:
+ 1. Current art
+ 2. Local art (thumb found by filename)
+ 3. Embedded art (@todo)
+ 4. None
+ Note that songs are not scraped, hence there is no list of urls for possible remote art
+*/
+void CGUIDialogSongInfo::OnGetArt()
+{
+ std::string type = MUSIC_UTILS::ShowSelectArtTypeDialog(m_artTypeList);
+ if (type.empty())
+ return; // Cancelled
+
+ CFileItemList items;
+ CGUIListItem::ArtMap primeArt = m_song->GetArt(); // Song art without fallbacks
+ bool bHasArt = m_song->HasArt(type);
+ bool bFallback(false);
+ if (bHasArt)
+ {
+ // Check if that type of art is actually a fallback, e.g. album thumb or artist fanart
+ CGUIListItem::ArtMap::const_iterator i = primeArt.find(type);
+ bFallback = (i == primeArt.end());
+ }
+
+ // Build list of possible images of that art type
+ if (bHasArt)
+ {
+ // Add item for current artwork, could a fallback from album/artist
+ CFileItemPtr item(new CFileItem("thumb://Current", false));
+ item->SetArt("thumb", m_song->GetArt(type));
+ item->SetArt("icon", "DefaultPicture.png");
+ item->SetLabel(g_localizeStrings.Get(13512)); //! @todo: label fallback art so user knows?
+ items.Add(item);
+ }
+ else if (m_song->HasArt("thumb"))
+ { // For missing art of that type add the thumb (when it exists and not a fallback)
+ CGUIListItem::ArtMap::const_iterator i = primeArt.find("thumb");
+ if (i != primeArt.end())
+ {
+ CFileItemPtr item(new CFileItem("thumb://Thumb", false));
+ item->SetArt("thumb", m_song->GetArt("thumb"));
+ item->SetArt("icon", "DefaultAlbumCover.png");
+ item->SetLabel(g_localizeStrings.Get(21371));
+ items.Add(item);
+ }
+ }
+
+ std::string localThumb;
+ if (type == "thumb")
+ { // Local thumb type art held in <filename>.tbn (for non-library items)
+ localThumb = m_song->GetUserMusicThumb(true);
+ if (m_song->IsMusicDb())
+ {
+ CFileItem item(m_song->GetMusicInfoTag()->GetURL(), false);
+ localThumb = item.GetUserMusicThumb(true);
+ }
+ if (CFileUtils::Exists(localThumb))
+ {
+ CFileItemPtr item(new CFileItem("thumb://Local", false));
+ item->SetArt("thumb", localThumb);
+ item->SetLabel(g_localizeStrings.Get(20017));
+ items.Add(item);
+ }
+ }
+
+ // Clear these local images from cache so user will see any recent
+ // local file changes immediately
+ for (auto& item : items)
+ {
+ std::string thumb(item->GetArt("thumb"));
+ if (thumb.empty())
+ continue;
+ CServiceBroker::GetTextureCache()->ClearCachedImage(thumb);
+ // Remove any thumbnail of local image too (created when browsing files)
+ std::string thumbthumb(CTextureUtils::GetWrappedThumbURL(thumb));
+ CServiceBroker::GetTextureCache()->ClearCachedImage(thumbthumb);
+ }
+
+ if (bHasArt && !bFallback)
+ { // Actually has this type of art (not a fallback) so
+ // allow the user to delete it by selecting "no art".
+ CFileItemPtr item(new CFileItem("thumb://None", false));
+ item->SetArt("thumb", "DefaultAlbumCover.png");
+ item->SetLabel(g_localizeStrings.Get(13515));
+ items.Add(item);
+ }
+
+ //! @todo: Add support for extracting embedded art
+
+ // Show list of possible art for user selection
+ std::string result;
+ VECSOURCES sources(*CMediaSourceSettings::GetInstance().GetSources("music"));
+ // Add album folder as source (could be disc set)
+ std::string albumpath = m_song->GetProperty("album_path").asString();
+ if (!albumpath.empty())
+ {
+ CFileItem pathItem(albumpath, true);
+ CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, pathItem);
+ }
+ else // Add parent folder of song
+ CGUIDialogMusicInfo::AddItemPathToFileBrowserSources(sources, *m_song);
+ CServiceBroker::GetMediaManager().GetLocalDrives(sources);
+ if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
+ result != "thumb://Current")
+ {
+ // User didn't choose the one they have, or the fallback image.
+ // Overwrite with the new art or clear it
+ std::string newArt;
+ if (result == "thumb://Thumb")
+ newArt = m_song->GetArt("thumb");
+ else if (result == "thumb://Local")
+ newArt = localThumb;
+// else if (result == "thumb://Embedded")
+// newArt = embeddedArt;
+ else if (CFileUtils::Exists(result))
+ newArt = result;
+ else // none
+ newArt.clear();
+
+ // Asynchronously update that type of art in the database
+ MUSIC_UTILS::UpdateArtJob(m_song, type, newArt);
+
+ // Update local song with current art
+ if (newArt.empty())
+ {
+ // Remove that type of art from the song
+ primeArt.erase(type);
+ m_song->SetArt(primeArt);
+ }
+ else
+ // Add or modify the type of art
+ m_song->SetArt(type, newArt);
+
+ // Update local art list with current art
+ // Show any fallback art when song art removed
+ if (newArt.empty() && m_song->HasArt(type))
+ newArt = m_song->GetArt(type);
+ for (const auto& artitem : m_artTypeList)
+ {
+ if (artitem->GetProperty("artType") == type)
+ {
+ artitem->SetArt("thumb", newArt);
+ break;
+ }
+ }
+
+ // Get new artwork to show in other places e.g. on music lib window,
+ // current playlist and player OSD.
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_song);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+
+ }
+
+ // Re-open the art type selection dialog as we come back from
+ // the image selection dialog
+ OnGetArt();
+}
+
+void CGUIDialogSongInfo::OnSetUserrating()
+{
+ int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_song->GetMusicInfoTag()->GetUserrating());
+ if (userrating < 0) // Nothing selected, so rating unchanged
+ return;
+
+ SetUserrating(userrating);
+}
+
+void CGUIDialogSongInfo::ShowFor(CFileItem* pItem)
+{
+ if (pItem->m_bIsFolder)
+ return;
+ if (!pItem->IsMusicDb())
+ pItem->LoadMusicTag();
+ if (!pItem->HasMusicInfoTag())
+ return;
+
+ CGUIDialogSongInfo *dialog = CServiceBroker::GetGUI()->GetWindowManager().
+ GetWindow<CGUIDialogSongInfo>(WINDOW_DIALOG_SONG_INFO);
+ if (dialog)
+ {
+ if (dialog->SetSong(pItem)) // Fetch full song info asynchronously
+ {
+ dialog->Open();
+ if (dialog->HasUpdatedUserrating())
+ {
+ auto window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowMusicBase>(WINDOW_MUSIC_NAV);
+ if (window)
+ window->RefreshContent("songs");
+ }
+ }
+ }
+}
+
+void CGUIDialogSongInfo::OnPlaySong(const std::shared_ptr<CFileItem>& item)
+{
+ Close(true);
+ MUSIC_UTILS::PlayItem(item);
+}
diff --git a/xbmc/music/dialogs/GUIDialogSongInfo.h b/xbmc/music/dialogs/GUIDialogSongInfo.h
new file mode 100644
index 0000000..274f1ac
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogSongInfo.h
@@ -0,0 +1,54 @@
+/*
+ * 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 "FileItem.h"
+#include "guilib/GUIDialog.h"
+#include "threads/Event.h"
+
+#include <memory>
+
+class CGUIDialogSongInfo :
+ public CGUIDialog
+{
+public:
+ CGUIDialogSongInfo(void);
+ ~CGUIDialogSongInfo(void) override;
+ bool OnMessage(CGUIMessage& message) override;
+ bool SetSong(CFileItem* item);
+ void SetArtTypeList(CFileItemList& artlist);
+ bool OnAction(const CAction& action) override;
+ bool OnBack(int actionID) override;
+ bool HasUpdatedUserrating() const { return m_hasUpdatedUserrating; }
+
+ bool HasListItems() const override { return true; }
+ CFileItemPtr GetCurrentListItem(int offset = 0) override;
+ std::string GetContent();
+ //const CFileItemList& CurrentDirectory() const { return m_artTypeList; }
+ bool IsCancelled() const { return m_cancelled; }
+ void FetchComplete();
+
+ static void ShowFor(CFileItem* pItem);
+protected:
+ void OnInitWindow() override;
+ void Update();
+ void OnGetArt();
+ void SetUserrating(int userrating);
+ void OnSetUserrating();
+ void OnPlaySong(const std::shared_ptr<CFileItem>& item);
+
+ CFileItemPtr m_song;
+ CFileItemList m_artTypeList;
+ CEvent m_event;
+ int m_startUserrating;
+ bool m_cancelled;
+ bool m_hasUpdatedUserrating;
+ long m_albumId = -1;
+
+};
diff --git a/xbmc/music/dialogs/GUIDialogVisualisationPresetList.cpp b/xbmc/music/dialogs/GUIDialogVisualisationPresetList.cpp
new file mode 100644
index 0000000..b5f2607
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogVisualisationPresetList.cpp
@@ -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.
+ */
+
+#include "GUIDialogVisualisationPresetList.h"
+
+#include "FileItem.h"
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIVisualisationControl.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "input/Key.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+
+CGUIDialogVisualisationPresetList::CGUIDialogVisualisationPresetList()
+ : CGUIDialogSelect(WINDOW_DIALOG_VIS_PRESET_LIST)
+{
+ m_loadType = KEEP_IN_MEMORY;
+}
+
+bool CGUIDialogVisualisationPresetList::OnMessage(CGUIMessage &message)
+{
+ switch (message.GetMessage())
+ {
+ case GUI_MSG_VISUALISATION_UNLOADING:
+ ClearVisualisation();
+ break;
+ }
+ return CGUIDialogSelect::OnMessage(message);
+}
+
+void CGUIDialogVisualisationPresetList::OnSelect(int idx)
+{
+ if (m_viz)
+ m_viz->SetPreset(idx);
+}
+
+void CGUIDialogVisualisationPresetList::ClearVisualisation()
+{
+ m_viz = nullptr;
+ Reset();
+}
+
+void CGUIDialogVisualisationPresetList::SetVisualisation(CGUIVisualisationControl* vis)
+{
+ m_viz = vis;
+ Reset();
+ if (!m_viz)
+ { // No viz, but show something if this dialog activated
+ SetHeading(CVariant{ 10122 });
+ CFileItem item(g_localizeStrings.Get(13389));
+ Add(item);
+ }
+ else
+ {
+ SetUseDetails(false);
+ SetMultiSelection(false);
+ SetHeading(CVariant{StringUtils::Format(g_localizeStrings.Get(13407), m_viz->Name())});
+ std::vector<std::string> presets;
+ if (m_viz->GetPresetList(presets))
+ {
+ for (const auto& preset : presets)
+ {
+ CFileItem item(preset);
+ item.RemoveExtension();
+ Add(item);
+ }
+ SetSelected(m_viz->GetActivePreset());
+ }
+ else
+ { // Viz does not have any presets
+ // "There are no presets available for this visualisation"
+ CFileItem item(g_localizeStrings.Get(13389));
+ Add(item);
+ }
+ }
+}
+
+void CGUIDialogVisualisationPresetList::OnInitWindow()
+{
+ CGUIMessage msg(GUI_MSG_GET_VISUALISATION, 0, 0);
+ CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
+ SetVisualisation(static_cast<CGUIVisualisationControl*>(msg.GetPointer()));
+ CGUIDialogSelect::OnInitWindow();
+}
+
+void CGUIDialogVisualisationPresetList::OnDeinitWindow(int nextWindowID)
+{
+ ClearVisualisation();
+ CGUIDialogSelect::OnDeinitWindow(nextWindowID);
+}
diff --git a/xbmc/music/dialogs/GUIDialogVisualisationPresetList.h b/xbmc/music/dialogs/GUIDialogVisualisationPresetList.h
new file mode 100644
index 0000000..c832163
--- /dev/null
+++ b/xbmc/music/dialogs/GUIDialogVisualisationPresetList.h
@@ -0,0 +1,32 @@
+/*
+ * 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 "dialogs/GUIDialogSelect.h"
+#include "guilib/GUIDialog.h"
+
+class CGUIVisualisationControl;
+class CFileItemList;
+
+class CGUIDialogVisualisationPresetList : public CGUIDialogSelect
+{
+public:
+ CGUIDialogVisualisationPresetList();
+ bool OnMessage(CGUIMessage &message) override;
+
+protected:
+ void OnInitWindow() override;
+ void OnDeinitWindow(int nextWindowID) override;
+ void OnSelect(int idx) override;
+
+private:
+ void ClearVisualisation();
+ void SetVisualisation(CGUIVisualisationControl *addon);
+ CGUIVisualisationControl* m_viz = nullptr;
+};