diff options
Diffstat (limited to 'xbmc/favourites')
-rw-r--r-- | xbmc/favourites/CMakeLists.txt | 17 | ||||
-rw-r--r-- | xbmc/favourites/ContextMenus.cpp | 80 | ||||
-rw-r--r-- | xbmc/favourites/ContextMenus.h | 76 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesService.cpp | 273 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesService.h | 56 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesURL.cpp | 205 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesURL.h | 66 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesUtils.cpp | 124 | ||||
-rw-r--r-- | xbmc/favourites/FavouritesUtils.h | 24 | ||||
-rw-r--r-- | xbmc/favourites/GUIDialogFavourites.cpp | 213 | ||||
-rw-r--r-- | xbmc/favourites/GUIDialogFavourites.h | 43 | ||||
-rw-r--r-- | xbmc/favourites/GUIViewStateFavourites.cpp | 28 | ||||
-rw-r--r-- | xbmc/favourites/GUIViewStateFavourites.h | 24 | ||||
-rw-r--r-- | xbmc/favourites/GUIWindowFavourites.cpp | 189 | ||||
-rw-r--r-- | xbmc/favourites/GUIWindowFavourites.h | 33 |
15 files changed, 1451 insertions, 0 deletions
diff --git a/xbmc/favourites/CMakeLists.txt b/xbmc/favourites/CMakeLists.txt new file mode 100644 index 0000000..beee8bd --- /dev/null +++ b/xbmc/favourites/CMakeLists.txt @@ -0,0 +1,17 @@ +set(SOURCES ContextMenus.cpp + GUIDialogFavourites.cpp + GUIViewStateFavourites.cpp + GUIWindowFavourites.cpp + FavouritesService.cpp + FavouritesURL.cpp + FavouritesUtils.cpp) + +set(HEADERS ContextMenus.h + GUIDialogFavourites.h + GUIViewStateFavourites.h + GUIWindowFavourites.h + FavouritesService.h + FavouritesURL.h + FavouritesUtils.h) + +core_add_library(favourites) diff --git a/xbmc/favourites/ContextMenus.cpp b/xbmc/favourites/ContextMenus.cpp new file mode 100644 index 0000000..821841f --- /dev/null +++ b/xbmc/favourites/ContextMenus.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ContextMenus.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "favourites/FavouritesService.h" +#include "favourites/FavouritesUtils.h" +#include "utils/URIUtils.h" + +namespace CONTEXTMENU +{ + bool CFavouriteContextMenuAction::IsVisible(const CFileItem& item) const + { + return URIUtils::IsProtocol(item.GetPath(), "favourites"); + } + + bool CFavouriteContextMenuAction::Execute(const std::shared_ptr<CFileItem>& item) const + { + CFileItemList items; + CServiceBroker::GetFavouritesService().GetAll(items); + for (const auto& favourite : items) + { + if (favourite->GetPath() == item->GetPath()) + { + if (DoExecute(items, favourite)) + return CServiceBroker::GetFavouritesService().Save(items); + } + } + return false; + } + + bool CMoveUpFavourite::DoExecute(CFileItemList& items, + const std::shared_ptr<CFileItem>& item) const + { + return FAVOURITES_UTILS::MoveItem(items, item, -1); + } + + bool CMoveUpFavourite::IsVisible(const CFileItem& item) const + { + return CFavouriteContextMenuAction::IsVisible(item) && + FAVOURITES_UTILS::ShouldEnableMoveItems(); + } + + bool CMoveDownFavourite::DoExecute(CFileItemList& items, + const std::shared_ptr<CFileItem>& item) const + { + return FAVOURITES_UTILS::MoveItem(items, item, +1); + } + + bool CMoveDownFavourite::IsVisible(const CFileItem& item) const + { + return CFavouriteContextMenuAction::IsVisible(item) && + FAVOURITES_UTILS::ShouldEnableMoveItems(); + } + + bool CRemoveFavourite::DoExecute(CFileItemList& items, + const std::shared_ptr<CFileItem>& item) const + { + return FAVOURITES_UTILS::RemoveItem(items, item); + } + + bool CRenameFavourite::DoExecute(CFileItemList&, const std::shared_ptr<CFileItem>& item) const + { + return FAVOURITES_UTILS::ChooseAndSetNewName(*item); + } + + bool CChooseThumbnailForFavourite::DoExecute(CFileItemList&, + const std::shared_ptr<CFileItem>& item) const + { + return FAVOURITES_UTILS::ChooseAndSetNewThumbnail(*item); + } + +} // namespace CONTEXTMENU diff --git a/xbmc/favourites/ContextMenus.h b/xbmc/favourites/ContextMenus.h new file mode 100644 index 0000000..fe1edc8 --- /dev/null +++ b/xbmc/favourites/ContextMenus.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "ContextMenuItem.h" + +#include <memory> + +class CFileItemList; + +namespace CONTEXTMENU +{ + +class CFavouriteContextMenuAction : public CStaticContextMenuAction +{ +public: + explicit CFavouriteContextMenuAction(uint32_t label) : CStaticContextMenuAction(label) {} + bool IsVisible(const CFileItem& item) const override; + bool Execute(const std::shared_ptr<CFileItem>& item) const override; + +protected: + ~CFavouriteContextMenuAction() override = default; + virtual bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const = 0; +}; + +class CMoveUpFavourite : public CFavouriteContextMenuAction +{ +public: + CMoveUpFavourite() : CFavouriteContextMenuAction(13332) {} // Move up + bool IsVisible(const CFileItem& item) const override; + +protected: + bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override; +}; + +class CMoveDownFavourite : public CFavouriteContextMenuAction +{ +public: + CMoveDownFavourite() : CFavouriteContextMenuAction(13333) {} // Move down + bool IsVisible(const CFileItem& item) const override; + +protected: + bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override; +}; + +class CRemoveFavourite : public CFavouriteContextMenuAction +{ +public: + CRemoveFavourite() : CFavouriteContextMenuAction(15015) {} // Remove +protected: + bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override; +}; + +class CRenameFavourite : public CFavouriteContextMenuAction +{ +public: + CRenameFavourite() : CFavouriteContextMenuAction(118) {} // Rename +protected: + bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override; +}; + +class CChooseThumbnailForFavourite : public CFavouriteContextMenuAction +{ +public: + CChooseThumbnailForFavourite() : CFavouriteContextMenuAction(20019) {} // Choose thumbnail +protected: + bool DoExecute(CFileItemList& items, const std::shared_ptr<CFileItem>& item) const override; +}; + +} diff --git a/xbmc/favourites/FavouritesService.cpp b/xbmc/favourites/FavouritesService.cpp new file mode 100644 index 0000000..8659445 --- /dev/null +++ b/xbmc/favourites/FavouritesService.cpp @@ -0,0 +1,273 @@ +/* + * 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 "FavouritesService.h" + +#include "FileItem.h" +#include "GUIPassword.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "favourites/FavouritesURL.h" +#include "profiles/ProfileManager.h" +#include "settings/SettingsComponent.h" +#include "utils/ContentUtils.h" +#include "utils/FileUtils.h" +#include "utils/URIUtils.h" +#include "utils/XBMCTinyXML.h" +#include "utils/log.h" + +#include <mutex> + +namespace +{ +bool IsMediasourceOfFavItemUnlocked(const std::shared_ptr<CFileItem>& item) +{ + if (!item) + { + CLog::Log(LOGERROR, "{}: No item passed (nullptr).", __func__); + return true; + } + + if (!item->IsFavourite()) + { + CLog::Log(LOGERROR, "{}: Wrong item passed (not a favourite).", __func__); + return true; + } + + const auto settingsComponent = CServiceBroker::GetSettingsComponent(); + if (!settingsComponent) + { + CLog::Log(LOGERROR, "{}: returned nullptr.", __func__); + return true; + } + + const auto profileManager = settingsComponent->GetProfileManager(); + if (!profileManager) + { + CLog::Log(LOGERROR, "{}: returned nullptr.", __func__); + return true; + } + + const CFavouritesURL url(item->GetPath()); + if (!url.IsValid()) + { + CLog::Log(LOGERROR, "{}: Invalid exec string (syntax error).", __func__); + return true; + } + + const CFavouritesURL::Action action = url.GetAction(); + + if (action != CFavouritesURL::Action::PLAY_MEDIA && + action != CFavouritesURL::Action::SHOW_PICTURE) + return true; + + const CFileItem itemToCheck(url.GetTarget(), url.IsDir()); + + if (action == CFavouritesURL::Action::PLAY_MEDIA) + { + if (itemToCheck.IsVideo()) + { + if (!profileManager->GetCurrentProfile().videoLocked()) + return g_passwordManager.IsMediaFileUnlocked("video", itemToCheck.GetPath()); + + return false; + } + else if (itemToCheck.IsAudio()) + { + if (!profileManager->GetCurrentProfile().musicLocked()) + return g_passwordManager.IsMediaFileUnlocked("music", itemToCheck.GetPath()); + + return false; + } + } + else if (action == CFavouritesURL::Action::SHOW_PICTURE && itemToCheck.IsPicture()) + { + if (!profileManager->GetCurrentProfile().picturesLocked()) + return g_passwordManager.IsMediaFileUnlocked("pictures", itemToCheck.GetPath()); + + return false; + } + + return true; +} + +bool LoadFromFile(const std::string& strPath, CFileItemList& items) +{ + CXBMCTinyXML doc; + if (!doc.LoadFile(strPath)) + { + CLog::Log(LOGERROR, "Unable to load {} (row {} column {})", strPath, doc.Row(), doc.Column()); + return false; + } + TiXmlElement *root = doc.RootElement(); + if (!root || strcmp(root->Value(), "favourites")) + { + CLog::Log(LOGERROR, "Favourites.xml doesn't contain the <favourites> root element"); + return false; + } + + TiXmlElement *favourite = root->FirstChildElement("favourite"); + while (favourite) + { + // format: + // <favourite name="Cool Video" thumb="foo.jpg">PlayMedia(c:\videos\cool_video.avi)</favourite> + // <favourite name="My Album" thumb="bar.tbn">ActivateWindow(MyMusic,c:\music\my album)</favourite> + // <favourite name="Apple Movie Trailers" thumb="path_to_thumb.png">RunScript(special://xbmc/scripts/apple movie trailers/default.py)</favourite> + const char *name = favourite->Attribute("name"); + const char *thumb = favourite->Attribute("thumb"); + if (name && favourite->FirstChild()) + { + const std::string favURL( + CFavouritesURL(CExecString(favourite->FirstChild()->Value())).GetURL()); + if (!items.Contains(favURL)) + { + const CFileItemPtr item(std::make_shared<CFileItem>(name)); + item->SetPath(favURL); + if (thumb) + item->SetArt("thumb", thumb); + items.Add(item); + } + } + favourite = favourite->NextSiblingElement("favourite"); + } + return true; +} +} // unnamed namespace + +CFavouritesService::CFavouritesService(std::string userDataFolder) : m_favourites("favourites://") +{ + ReInit(std::move(userDataFolder)); +} + +void CFavouritesService::ReInit(std::string userDataFolder) +{ + m_userDataFolder = std::move(userDataFolder); + m_favourites.Clear(); + m_favourites.SetContent("favourites"); + + std::string favourites = "special://xbmc/system/favourites.xml"; + if (CFileUtils::Exists(favourites)) + LoadFromFile(favourites, m_favourites); + else + CLog::Log(LOGDEBUG, "CFavourites::Load - no system favourites found, skipping"); + + favourites = URIUtils::AddFileToFolder(m_userDataFolder, "favourites.xml"); + if (CFileUtils::Exists(favourites)) + LoadFromFile(favourites, m_favourites); + else + CLog::Log(LOGDEBUG, "CFavourites::Load - no userdata favourites found, skipping"); +} + +bool CFavouritesService::Persist() +{ + CXBMCTinyXML doc; + TiXmlElement xmlRootElement("favourites"); + TiXmlNode *rootNode = doc.InsertEndChild(xmlRootElement); + if (!rootNode) + return false; + + for (const auto& item : m_favourites) + { + TiXmlElement favNode("favourite"); + favNode.SetAttribute("name", item->GetLabel().c_str()); + if (item->HasArt("thumb")) + favNode.SetAttribute("thumb", item->GetArt("thumb").c_str()); + + TiXmlText execute(CFavouritesURL(item->GetPath()).GetExecString()); + favNode.InsertEndChild(execute); + rootNode->InsertEndChild(favNode); + } + + auto path = URIUtils::AddFileToFolder(m_userDataFolder, "favourites.xml"); + return doc.SaveFile(path); +} + +bool CFavouritesService::Save(const CFileItemList& items) +{ + { + std::unique_lock<CCriticalSection> lock(m_criticalSection); + m_favourites.Clear(); + m_favourites.Copy(items); + Persist(); + } + OnUpdated(); + return true; +} + +void CFavouritesService::OnUpdated() +{ + m_events.Publish(FavouritesUpdated{}); +} + +std::string CFavouritesService::GetFavouritesUrl(const CFileItem& item, int contextWindow) const +{ + return CFavouritesURL(item, contextWindow).GetURL(); +} + +bool CFavouritesService::AddOrRemove(const CFileItem& item, int contextWindow) +{ + auto favUrl = GetFavouritesUrl(item, contextWindow); + { + std::unique_lock<CCriticalSection> lock(m_criticalSection); + CFileItemPtr match = m_favourites.Get(favUrl); + if (match) + { // remove the item + m_favourites.Remove(match.get()); + } + else + { // create our new favourite item + const CFileItemPtr favourite(std::make_shared<CFileItem>(item.GetLabel())); + if (item.GetLabel().empty()) + favourite->SetLabel(CUtil::GetTitleFromPath(item.GetPath(), item.m_bIsFolder)); + favourite->SetArt("thumb", ContentUtils::GetPreferredArtImage(item)); + favourite->SetPath(favUrl); + m_favourites.Add(favourite); + } + Persist(); + } + OnUpdated(); + return true; +} + +bool CFavouritesService::IsFavourited(const CFileItem& item, int contextWindow) const +{ + std::unique_lock<CCriticalSection> lock(m_criticalSection); + return m_favourites.Contains(GetFavouritesUrl(item, contextWindow)); +} + +void CFavouritesService::GetAll(CFileItemList& items) const +{ + std::unique_lock<CCriticalSection> lock(m_criticalSection); + items.Clear(); + if (g_passwordManager.IsMasterLockUnlocked(false)) // don't prompt + { + items.Copy(m_favourites, true); // copy items + } + else + { + for (const auto& fav : m_favourites) + { + if (IsMediasourceOfFavItemUnlocked(fav)) + items.Add(fav); + } + } + + int index = 0; + for (const auto& item : items) + { + const CFavouritesURL favURL(item->GetPath()); + item->SetProperty("favourite.action", favURL.GetActionLabel()); + item->SetProperty("favourite.provider", favURL.GetProviderLabel()); + item->SetProperty("favourite.index", index++); + } +} + +void CFavouritesService::RefreshFavourites() +{ + m_events.Publish(FavouritesUpdated{}); +} diff --git a/xbmc/favourites/FavouritesService.h b/xbmc/favourites/FavouritesService.h new file mode 100644 index 0000000..5a16459 --- /dev/null +++ b/xbmc/favourites/FavouritesService.h @@ -0,0 +1,56 @@ +/* + * 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 "threads/CriticalSection.h" +#include "utils/EventStream.h" + +#include <string> +#include <vector> + +class CFavouritesService +{ +public: + explicit CFavouritesService(std::string userDataFolder); + virtual ~CFavouritesService() = default; + + /** For profiles*/ + void ReInit(std::string userDataFolder); + + bool IsFavourited(const CFileItem& item, int contextWindow) const; + void GetAll(CFileItemList& items) const; + bool AddOrRemove(const CFileItem& item, int contextWindow); + bool Save(const CFileItemList& items); + + /*! \brief Refresh favourites for directory providers, e.g. the GUI needs to be updated + */ + void RefreshFavourites(); + + struct FavouritesUpdated { }; + + CEventStream<FavouritesUpdated>& Events() { return m_events; } + +private: + CFavouritesService() = delete; + CFavouritesService(const CFavouritesService&) = delete; + CFavouritesService& operator=(const CFavouritesService&) = delete; + CFavouritesService(CFavouritesService&&) = delete; + CFavouritesService& operator=(CFavouritesService&&) = delete; + + void OnUpdated(); + bool Persist(); + std::string GetFavouritesUrl(const CFileItem &item, int contextWindow) const; + + std::string m_userDataFolder; + CFileItemList m_favourites; + CEventSource<FavouritesUpdated> m_events; + mutable CCriticalSection m_criticalSection; +}; + diff --git a/xbmc/favourites/FavouritesURL.cpp b/xbmc/favourites/FavouritesURL.cpp new file mode 100644 index 0000000..a277256 --- /dev/null +++ b/xbmc/favourites/FavouritesURL.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2022 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 "FavouritesURL.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "URL.h" +#include "addons/AddonManager.h" +#include "addons/IAddon.h" +#include "guilib/LocalizeStrings.h" +#include "input/WindowTranslator.h" +#include "utils/ExecString.h" +#include "utils/StringUtils.h" +#include "utils/SystemInfo.h" +#include "utils/URIUtils.h" +#include "utils/log.h" + +#include <vector> + +namespace +{ +std::string GetActionString(CFavouritesURL::Action action) +{ + switch (action) + { + case CFavouritesURL::Action::ACTIVATE_WINDOW: + return "activatewindow"; + case CFavouritesURL::Action::PLAY_MEDIA: + return "playmedia"; + case CFavouritesURL::Action::SHOW_PICTURE: + return "showpicture"; + case CFavouritesURL::Action::RUN_SCRIPT: + return "runscript"; + case CFavouritesURL::Action::RUN_ADDON: + return "runaddon"; + case CFavouritesURL::Action::START_ANDROID_ACTIVITY: + return "startandroidactivity"; + default: + return {}; + } +} + +CFavouritesURL::Action GetActionId(const std::string& actionString) +{ + if (actionString == "activatewindow") + return CFavouritesURL::Action::ACTIVATE_WINDOW; + else if (actionString == "playmedia") + return CFavouritesURL::Action::PLAY_MEDIA; + else if (actionString == "showpicture") + return CFavouritesURL::Action::SHOW_PICTURE; + else if (actionString == "runscript") + return CFavouritesURL::Action::RUN_SCRIPT; + else if (actionString == "runaddon") + return CFavouritesURL::Action::RUN_ADDON; + else if (actionString == "startandroidactivity") + return CFavouritesURL::Action::START_ANDROID_ACTIVITY; + else + return CFavouritesURL::Action::UNKNOWN; +} +} // namespace + +CFavouritesURL::CFavouritesURL(const std::string& favouritesURL) +{ + const CURL url(favouritesURL); + if (url.GetProtocol() != "favourites") + { + CLog::LogF(LOGERROR, "Invalid protocol"); + return; + } + + m_exec = CExecString(CURL::Decode(url.GetHostName())); + if (m_exec.IsValid()) + m_valid = Parse(GetActionId(m_exec.GetFunction()), m_exec.GetParams()); +} + +CFavouritesURL::CFavouritesURL(const CExecString& execString) : m_exec(execString) +{ + if (m_exec.IsValid()) + m_valid = Parse(GetActionId(m_exec.GetFunction()), m_exec.GetParams()); +} + +CFavouritesURL::CFavouritesURL(Action action, const std::vector<std::string>& params) + : m_exec(GetActionString(action), params) +{ + if (m_exec.IsValid()) + m_valid = Parse(action, params); +} + +CFavouritesURL::CFavouritesURL(const CFileItem& item, int contextWindow) + : m_exec(item, std::to_string(contextWindow)) +{ + if (m_exec.IsValid()) + m_valid = Parse(GetActionId(m_exec.GetFunction()), m_exec.GetParams()); +} + +bool CFavouritesURL::Parse(CFavouritesURL::Action action, const std::vector<std::string>& params) +{ + m_action = action; + + bool pathIsAddonID = false; + + switch (action) + { + case Action::ACTIVATE_WINDOW: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + + // mandatory: window name/id + m_windowId = CWindowTranslator::TranslateWindow(params[0]); + + if (params.size() > 1) + { + // optional: target url/path + m_target = StringUtils::DeParamify(params[1]); + } + m_actionLabel = + StringUtils::Format(g_localizeStrings.Get(15220), // Show content in '<windowname>' + g_localizeStrings.Get(m_windowId)); + break; + case Action::PLAY_MEDIA: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15218); // Play media + break; + case Action::SHOW_PICTURE: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15219); // Show picture + break; + case Action::RUN_SCRIPT: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15221); // Execute script + pathIsAddonID = true; + break; + case Action::RUN_ADDON: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15223); // Execute add-on + pathIsAddonID = true; + break; + case Action::START_ANDROID_ACTIVITY: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15222); // Execute Android app + break; + default: + if (params.empty()) + { + CLog::LogF(LOGERROR, "Missing parameter"); + return false; + } + m_action = CFavouritesURL::Action::UNKNOWN; + m_target = StringUtils::DeParamify(params[0]); + m_actionLabel = g_localizeStrings.Get(15224); // Other / Unknown + break; + } + + m_path = StringUtils::Format("favourites://{}", CURL::Encode(GetExecString())); + m_isDir = URIUtils::HasSlashAtEnd(m_target, true); + + if (pathIsAddonID || URIUtils::IsPlugin(m_target)) + { + // get the add-on name + const std::string plugin = pathIsAddonID ? m_target : CURL(m_target).GetHostName(); + + ADDON::AddonPtr addon; + CServiceBroker::GetAddonMgr().GetAddon(plugin, addon, ADDON::OnlyEnabled::CHOICE_NO); + if (addon) + m_providerLabel = addon->Name(); + } + if (m_providerLabel.empty()) + m_providerLabel = CSysInfo::GetAppName(); + + return true; +} diff --git a/xbmc/favourites/FavouritesURL.h b/xbmc/favourites/FavouritesURL.h new file mode 100644 index 0000000..7261484 --- /dev/null +++ b/xbmc/favourites/FavouritesURL.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "utils/ExecString.h" + +#include <string> +#include <vector> + +class CFileItem; + +class CFavouritesURL +{ +public: + enum class Action + { + UNKNOWN, + ACTIVATE_WINDOW, + PLAY_MEDIA, + SHOW_PICTURE, + RUN_SCRIPT, + RUN_ADDON, + START_ANDROID_ACTIVITY, + }; + + explicit CFavouritesURL(const std::string& favouritesURL); + explicit CFavouritesURL(const CExecString& execString); + CFavouritesURL(Action action, const std::vector<std::string>& params); + CFavouritesURL(const CFileItem& item, int contextWindow); + + virtual ~CFavouritesURL() = default; + + std::string GetURL() const { return m_path; } + + bool IsValid() const { return m_valid && m_exec.IsValid(); } + + bool IsDir() const { return m_isDir; } + + std::string GetExecString() const { return m_exec.GetExecString(); } + Action GetAction() const { return m_action; } + std::vector<std::string> GetParams() const { return m_exec.GetParams(); } + std::string GetTarget() const { return m_target; } + int GetWindowID() const { return m_windowId; } + std::string GetActionLabel() const { return m_actionLabel; } + std::string GetProviderLabel() const { return m_providerLabel; } + +private: + bool Parse(CFavouritesURL::Action action, const std::vector<std::string>& params); + + CExecString m_exec; + + bool m_valid{false}; + std::string m_path; + Action m_action{Action::UNKNOWN}; + std::string m_target; + int m_windowId{-1}; + bool m_isDir{false}; + std::string m_actionLabel; + std::string m_providerLabel; +}; diff --git a/xbmc/favourites/FavouritesUtils.cpp b/xbmc/favourites/FavouritesUtils.cpp new file mode 100644 index 0000000..9a055c5 --- /dev/null +++ b/xbmc/favourites/FavouritesUtils.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2023 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 "FavouritesUtils.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "favourites/GUIWindowFavourites.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "storage/MediaManager.h" +#include "utils/Variant.h" +#include "view/GUIViewState.h" + +#include <string> + +namespace FAVOURITES_UTILS +{ +bool ChooseAndSetNewName(CFileItem& item) +{ + std::string label = item.GetLabel(); + if (CGUIKeyboardFactory::ShowAndGetInput(label, CVariant{g_localizeStrings.Get(16008)}, + false)) // Enter new title + { + item.SetLabel(label); + return true; + } + return false; +} + +bool ChooseAndSetNewThumbnail(CFileItem& item) +{ + CFileItemList prefilledItems; + if (item.HasArt("thumb")) + { + const auto current = std::make_shared<CFileItem>("thumb://Current", false); + current->SetArt("thumb", item.GetArt("thumb")); + current->SetLabel(g_localizeStrings.Get(20016)); // Current thumb + prefilledItems.Add(current); + } + + const auto none = std::make_shared<CFileItem>("thumb://None", false); + none->SetArt("icon", item.GetArt("icon")); + none->SetLabel(g_localizeStrings.Get(20018)); // No thumb + prefilledItems.Add(none); + + std::string thumb; + VECSOURCES sources; + CServiceBroker::GetMediaManager().GetLocalDrives(sources); + if (CGUIDialogFileBrowser::ShowAndGetImage(prefilledItems, sources, g_localizeStrings.Get(1030), + thumb)) // Browse for image + { + item.SetArt("thumb", thumb); + return true; + } + return false; +} + +bool MoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item, int amount) +{ + if (items.Size() < 2 || amount == 0) + return false; + + int itemPos = -1; + for (const auto& i : items) + { + itemPos++; + + if (i->GetPath() == item->GetPath()) + break; + } + + if (itemPos < 0 || itemPos >= items.Size()) + return false; + + int nextItem = (itemPos + amount) % items.Size(); + if (nextItem < 0) + { + const auto itemToAdd(item); + items.Remove(itemPos); + items.Add(itemToAdd); + } + else if (nextItem == 0) + { + const auto itemToAdd(item); + items.Remove(itemPos); + items.AddFront(itemToAdd, 0); + } + else + { + items.Swap(itemPos, nextItem); + } + return true; +} + +bool RemoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item) +{ + int iBefore = items.Size(); + items.Remove(item.get()); + return items.Size() == iBefore - 1; +} + +bool ShouldEnableMoveItems() +{ + auto& mgr = CServiceBroker::GetGUI()->GetWindowManager(); + CGUIWindowFavourites* window = mgr.GetWindow<CGUIWindowFavourites>(WINDOW_FAVOURITES); + if (window && window->IsActive()) + { + const CGUIViewState* state = window->GetViewState(); + if (state && state->GetSortMethod().sortBy != SortByUserPreference) + return false; // in favs window, allow move only if current sort method is by user preference + } + return true; +} + +} // namespace FAVOURITES_UTILS diff --git a/xbmc/favourites/FavouritesUtils.h b/xbmc/favourites/FavouritesUtils.h new file mode 100644 index 0000000..0388b4d --- /dev/null +++ b/xbmc/favourites/FavouritesUtils.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <memory> + +class CFileItem; +class CFileItemList; + +namespace FAVOURITES_UTILS +{ +bool ChooseAndSetNewName(CFileItem& item); +bool ChooseAndSetNewThumbnail(CFileItem& item); +bool MoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item, int amount); +bool RemoveItem(CFileItemList& items, const std::shared_ptr<CFileItem>& item); +bool ShouldEnableMoveItems(); + +} // namespace FAVOURITES_UTILS diff --git a/xbmc/favourites/GUIDialogFavourites.cpp b/xbmc/favourites/GUIDialogFavourites.cpp new file mode 100644 index 0000000..7af6d29 --- /dev/null +++ b/xbmc/favourites/GUIDialogFavourites.cpp @@ -0,0 +1,213 @@ +/* + * 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 "GUIDialogFavourites.h" + +#include "ContextMenuManager.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "favourites/FavouritesURL.h" +#include "favourites/FavouritesUtils.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "input/actions/ActionIDs.h" + +#define FAVOURITES_LIST 450 + +CGUIDialogFavourites::CGUIDialogFavourites() : + CGUIDialog(WINDOW_DIALOG_FAVOURITES, "DialogFavourites.xml"), + m_favouritesService(CServiceBroker::GetFavouritesService()) +{ + m_favourites = new CFileItemList; + m_loadType = KEEP_IN_MEMORY; +} + +CGUIDialogFavourites::~CGUIDialogFavourites(void) +{ + delete m_favourites; +} + +bool CGUIDialogFavourites::OnMessage(CGUIMessage &message) +{ + if (message.GetMessage() == GUI_MSG_CLICKED) + { + if (message.GetSenderId() == FAVOURITES_LIST) + { + int item = GetSelectedItem(); + int action = message.GetParam1(); + if (action == ACTION_SELECT_ITEM || action == ACTION_MOUSE_LEFT_CLICK) + OnClick(item); + else if (action == ACTION_MOVE_ITEM_UP) + OnMoveItem(item, -1); + else if (action == ACTION_MOVE_ITEM_DOWN) + OnMoveItem(item, 1); + else if (action == ACTION_CONTEXT_MENU || action == ACTION_MOUSE_RIGHT_CLICK) + OnPopupMenu(item); + else if (action == ACTION_DELETE_ITEM) + OnDelete(item); + else + return false; + return true; + } + } + else if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT) + { + CGUIDialog::OnMessage(message); + // clear our favourites + CGUIMessage message(GUI_MSG_LABEL_RESET, GetID(), FAVOURITES_LIST); + OnMessage(message); + m_favourites->Clear(); + return true; + } + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogFavourites::OnInitWindow() +{ + m_favouritesService.GetAll(*m_favourites); + UpdateList(); + CGUIWindow::OnInitWindow(); +} + +int CGUIDialogFavourites::GetSelectedItem() +{ + CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), FAVOURITES_LIST); + OnMessage(message); + return message.GetParam1(); +} + +void CGUIDialogFavourites::OnClick(int item) +{ + if (item < 0 || item >= m_favourites->Size()) + return; + + CGUIMessage message(GUI_MSG_EXECUTE, 0, GetID()); + message.SetStringParam(CFavouritesURL(*(*m_favourites)[item], GetID()).GetExecString()); + + Close(); + + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message); +} + +void CGUIDialogFavourites::OnPopupMenu(int item) +{ + if (item < 0 || item >= m_favourites->Size()) + return; + + // highlight the item + (*m_favourites)[item]->Select(true); + + CContextButtons choices; + if (m_favourites->Size() > 1) + { + choices.Add(1, 13332); // Move up + choices.Add(2, 13333); // Move down + } + choices.Add(3, 20019); // Choose thumbnail + choices.Add(4, 118); // Rename + choices.Add(5, 15015); // Remove + + CFileItemPtr itemPtr = m_favourites->Get(item); + + //temporary workaround until the context menu ids are removed + const int addonItemOffset = 10000; + + auto addonItems = CServiceBroker::GetContextMenuManager().GetAddonItems(*itemPtr); + + for (size_t i = 0; i < addonItems.size(); ++i) + choices.Add(addonItemOffset + i, addonItems[i]->GetLabel(*itemPtr)); + + int button = CGUIDialogContextMenu::ShowAndGetChoice(choices); + + // unhighlight the item + (*m_favourites)[item]->Select(false); + + if (button == 1) + OnMoveItem(item, -1); + else if (button == 2) + OnMoveItem(item, +1); + else if (button == 3) + OnSetThumb(item); + else if (button == 4) + OnRename(item); + else if (button == 5) + OnDelete(item); + else if (button >= addonItemOffset) + CONTEXTMENU::LoopFrom(*addonItems.at(button - addonItemOffset), itemPtr); +} + +void CGUIDialogFavourites::OnMoveItem(int item, int amount) +{ + if (item < 0 || item >= m_favourites->Size() || m_favourites->Size() <= 1 || 0 == amount) return; + + int nextItem = (item + amount) % m_favourites->Size(); + if (nextItem < 0) nextItem += m_favourites->Size(); + + m_favourites->Swap(item, nextItem); + m_favouritesService.Save(*m_favourites); + + CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), FAVOURITES_LIST, nextItem); + OnMessage(message); + + UpdateList(); +} + +void CGUIDialogFavourites::OnDelete(int item) +{ + if (item < 0 || item >= m_favourites->Size()) + return; + m_favourites->Remove(item); + m_favouritesService.Save(*m_favourites); + + CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), FAVOURITES_LIST, item < m_favourites->Size() ? item : item - 1); + OnMessage(message); + + UpdateList(); +} + +void CGUIDialogFavourites::OnRename(int item) +{ + if (item < 0 || item >= m_favourites->Size()) + return; + + if (FAVOURITES_UTILS::ChooseAndSetNewName(*(*m_favourites)[item])) + { + m_favouritesService.Save(*m_favourites); + UpdateList(); + } +} + +void CGUIDialogFavourites::OnSetThumb(int item) +{ + if (item < 0 || item >= m_favourites->Size()) + return; + + if (FAVOURITES_UTILS::ChooseAndSetNewThumbnail(*(*m_favourites)[item])) + { + m_favouritesService.Save(*m_favourites); + UpdateList(); + } +} + +void CGUIDialogFavourites::UpdateList() +{ + int currentItem = GetSelectedItem(); + CGUIMessage message(GUI_MSG_LABEL_BIND, GetID(), FAVOURITES_LIST, currentItem >= 0 ? currentItem : 0, 0, m_favourites); + OnMessage(message); +} + +CFileItemPtr CGUIDialogFavourites::GetCurrentListItem(int offset) +{ + int currentItem = GetSelectedItem(); + if (currentItem < 0 || !m_favourites->Size()) return CFileItemPtr(); + + int item = (currentItem + offset) % m_favourites->Size(); + if (item < 0) item += m_favourites->Size(); + return (*m_favourites)[item]; +} diff --git a/xbmc/favourites/GUIDialogFavourites.h b/xbmc/favourites/GUIDialogFavourites.h new file mode 100644 index 0000000..72d26bd --- /dev/null +++ b/xbmc/favourites/GUIDialogFavourites.h @@ -0,0 +1,43 @@ +/* + * 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 "favourites/FavouritesService.h" +#include "guilib/GUIDialog.h" + +class CFileItem; +class CFileItemList; + +class CGUIDialogFavourites : + public CGUIDialog +{ +public: + CGUIDialogFavourites(void); + ~CGUIDialogFavourites(void) override; + bool OnMessage(CGUIMessage &message) override; + void OnInitWindow() override; + + CFileItemPtr GetCurrentListItem(int offset = 0) override; + + bool HasListItems() const override { return true; } + +protected: + int GetSelectedItem(); + void OnClick(int item); + void OnPopupMenu(int item); + void OnMoveItem(int item, int amount); + void OnDelete(int item); + void OnRename(int item); + void OnSetThumb(int item); + void UpdateList(); + +private: + CFileItemList* m_favourites; + CFavouritesService& m_favouritesService; +}; diff --git a/xbmc/favourites/GUIViewStateFavourites.cpp b/xbmc/favourites/GUIViewStateFavourites.cpp new file mode 100644 index 0000000..7bb5ff1 --- /dev/null +++ b/xbmc/favourites/GUIViewStateFavourites.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 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 "GUIViewStateFavourites.h" + +#include "FileItem.h" +#include "guilib/WindowIDs.h" + +CGUIViewStateFavourites::CGUIViewStateFavourites(const CFileItemList& items) : CGUIViewState(items) +{ + AddSortMethod(SortByUserPreference, 19349, + LABEL_MASKS("%L", "", "%L", "")); // Label, empty | Label, empty + AddSortMethod(SortByLabel, 551, LABEL_MASKS("%L", "", "%L", "")); // Label, empty | Label, empty + + SetSortMethod(SortByUserPreference); + + LoadViewState(items.GetPath(), WINDOW_FAVOURITES); +} + +void CGUIViewStateFavourites::SaveViewState() +{ + SaveViewToDb(m_items.GetPath(), WINDOW_FAVOURITES); +} diff --git a/xbmc/favourites/GUIViewStateFavourites.h b/xbmc/favourites/GUIViewStateFavourites.h new file mode 100644 index 0000000..aad4444 --- /dev/null +++ b/xbmc/favourites/GUIViewStateFavourites.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 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 "view/GUIViewState.h" + +class CFileItemList; + +class CGUIViewStateFavourites : public CGUIViewState +{ +public: + CGUIViewStateFavourites(const CFileItemList& items); + ~CGUIViewStateFavourites() override = default; + +protected: + void SaveViewState() override; + bool HideParentDirItems() override { return true; }; +}; diff --git a/xbmc/favourites/GUIWindowFavourites.cpp b/xbmc/favourites/GUIWindowFavourites.cpp new file mode 100644 index 0000000..0d43b59 --- /dev/null +++ b/xbmc/favourites/GUIWindowFavourites.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2022 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 "GUIWindowFavourites.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "favourites/FavouritesURL.h" +#include "favourites/FavouritesUtils.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/ApplicationMessenger.h" +#include "utils/PlayerUtils.h" +#include "utils/StringUtils.h" + +CGUIWindowFavourites::CGUIWindowFavourites() + : CGUIMediaWindow(WINDOW_FAVOURITES, "MyFavourites.xml") +{ + m_loadType = KEEP_IN_MEMORY; + CServiceBroker::GetFavouritesService().Events().Subscribe( + this, &CGUIWindowFavourites::OnFavouritesEvent); +} + +CGUIWindowFavourites::~CGUIWindowFavourites() +{ + CServiceBroker::GetFavouritesService().Events().Unsubscribe(this); +} + +void CGUIWindowFavourites::OnFavouritesEvent(const CFavouritesService::FavouritesUpdated& event) +{ + CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, 0); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); +} + +namespace +{ +bool ExecuteAction(const std::string& execute) +{ + if (!execute.empty()) + { + CGUIMessage message(GUI_MSG_EXECUTE, 0, 0); + message.SetStringParam(execute); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(message); + return true; + } + return false; +} +} // namespace + +bool CGUIWindowFavourites::OnSelect(int item) +{ + if (item < 0 || item >= m_vecItems->Size()) + return false; + + return ExecuteAction(CFavouritesURL(*(*m_vecItems)[item], GetID()).GetExecString()); +} + +bool CGUIWindowFavourites::OnAction(const CAction& action) +{ + const int selectedItem = m_viewControl.GetSelectedItem(); + if (selectedItem < 0 || selectedItem >= m_vecItems->Size()) + return false; + + if (action.GetID() == ACTION_PLAYER_PLAY) + { + const CFavouritesURL favURL((*m_vecItems)[selectedItem]->GetPath()); + if (!favURL.IsValid()) + return false; + + // If action is playmedia, just play it + if (favURL.GetAction() == CFavouritesURL::Action::PLAY_MEDIA) + return ExecuteAction(favURL.GetExecString()); + + // Resolve and check the target + const auto item = std::make_shared<CFileItem>(favURL.GetTarget(), favURL.IsDir()); + if (CPlayerUtils::IsItemPlayable(*item)) + { + CFavouritesURL target(*item, {}); + if (target.GetAction() == CFavouritesURL::Action::PLAY_MEDIA) + { + return ExecuteAction(target.GetExecString()); + } + else + { + // build and execute a playmedia execute string + target = CFavouritesURL(CFavouritesURL::Action::PLAY_MEDIA, + {StringUtils::Paramify(item->GetPath())}); + return ExecuteAction(target.GetExecString()); + } + } + return false; + } + else if (action.GetID() == ACTION_MOVE_ITEM_UP) + { + if (FAVOURITES_UTILS::ShouldEnableMoveItems()) + return MoveItem(selectedItem, -1); + } + else if (action.GetID() == ACTION_MOVE_ITEM_DOWN) + { + if (FAVOURITES_UTILS::ShouldEnableMoveItems()) + return MoveItem(selectedItem, +1); + } + else if (action.GetID() == ACTION_DELETE_ITEM) + { + return RemoveItem(selectedItem); + } + + return CGUIMediaWindow::OnAction(action); +} + +bool CGUIWindowFavourites::OnMessage(CGUIMessage& message) +{ + bool ret = false; + + switch (message.GetMessage()) + { + case GUI_MSG_REFRESH_LIST: + { + const int size = m_vecItems->Size(); + int selected = m_viewControl.GetSelectedItem(); + if (m_vecItems->Size() > 0 && selected == size - 1) + --selected; // remove of last item, select the new last item after refresh + + Refresh(true); + + if (m_vecItems->Size() < size) + { + // item removed. select item after the removed item + m_viewControl.SetSelectedItem(selected); + } + + ret = true; + break; + } + } + + return ret || CGUIMediaWindow::OnMessage(message); +} + +bool CGUIWindowFavourites::Update(const std::string& strDirectory, + bool updateFilterPath /* = true */) +{ + std::string directory = strDirectory; + if (directory.empty()) + directory = "favourites://"; + + return CGUIMediaWindow::Update(directory, updateFilterPath); +} + +bool CGUIWindowFavourites::MoveItem(int item, int amount) +{ + if (item < 0 || item >= m_vecItems->Size() || m_vecItems->Size() < 2 || amount == 0) + return false; + + if (FAVOURITES_UTILS::MoveItem(*m_vecItems, (*m_vecItems)[item], amount) && + CServiceBroker::GetFavouritesService().Save(*m_vecItems)) + { + int selected = item + amount; + if (selected >= m_vecItems->Size()) + selected = 0; + else if (selected < 0) + selected = m_vecItems->Size() - 1; + + m_viewControl.SetSelectedItem(selected); + return true; + } + + return false; +} + +bool CGUIWindowFavourites::RemoveItem(int item) +{ + if (item < 0 || item >= m_vecItems->Size()) + return false; + + if (FAVOURITES_UTILS::RemoveItem(*m_vecItems, (*m_vecItems)[item]) && + CServiceBroker::GetFavouritesService().Save(*m_vecItems)) + return true; + + return false; +} diff --git a/xbmc/favourites/GUIWindowFavourites.h b/xbmc/favourites/GUIWindowFavourites.h new file mode 100644 index 0000000..ec4ea32 --- /dev/null +++ b/xbmc/favourites/GUIWindowFavourites.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 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 "favourites/FavouritesService.h" +#include "windows/GUIMediaWindow.h" + +class CGUIWindowFavourites : public CGUIMediaWindow +{ +public: + CGUIWindowFavourites(); + ~CGUIWindowFavourites() override; + +protected: + std::string GetRootPath() const override { return "favourites://"; } + + bool OnSelect(int item) override; + bool OnAction(const CAction& action) override; + bool OnMessage(CGUIMessage& message) override; + + bool Update(const std::string& strDirectory, bool updateFilterPath = true) override; + +private: + void OnFavouritesEvent(const CFavouritesService::FavouritesUpdated& event); + bool MoveItem(int item, int amount); + bool RemoveItem(int item); +}; |