summaryrefslogtreecommitdiffstats
path: root/xbmc/ContextMenuManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/ContextMenuManager.cpp')
-rw-r--r--xbmc/ContextMenuManager.cpp293
1 files changed, 293 insertions, 0 deletions
diff --git a/xbmc/ContextMenuManager.cpp b/xbmc/ContextMenuManager.cpp
new file mode 100644
index 0000000..0efff74
--- /dev/null
+++ b/xbmc/ContextMenuManager.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2013-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ContextMenuManager.h"
+
+#include "ContextMenuItem.h"
+#include "ContextMenus.h"
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "addons/Addon.h"
+#include "addons/AddonEvents.h"
+#include "addons/AddonManager.h"
+#include "addons/ContextMenuAddon.h"
+#include "addons/ContextMenus.h"
+#include "addons/IAddon.h"
+#include "addons/addoninfo/AddonType.h"
+#include "dialogs/GUIDialogContextMenu.h"
+#include "favourites/ContextMenus.h"
+#include "messaging/ApplicationMessenger.h"
+#include "music/ContextMenus.h"
+#include "pvr/PVRContextMenus.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+#include "video/ContextMenus.h"
+
+#include <iterator>
+#include <mutex>
+
+using namespace ADDON;
+using namespace PVR;
+
+const CContextMenuItem CContextMenuManager::MAIN = CContextMenuItem::CreateGroup("", "", "kodi.core.main", "");
+const CContextMenuItem CContextMenuManager::MANAGE = CContextMenuItem::CreateGroup("", "", "kodi.core.manage", "");
+
+
+CContextMenuManager::CContextMenuManager(CAddonMgr& addonMgr)
+ : m_addonMgr(addonMgr) {}
+
+CContextMenuManager::~CContextMenuManager()
+{
+ Deinit();
+}
+
+void CContextMenuManager::Deinit()
+{
+ CPVRContextMenuManager::GetInstance().Events().Unsubscribe(this);
+ m_addonMgr.Events().Unsubscribe(this);
+ m_items.clear();
+}
+
+void CContextMenuManager::Init()
+{
+ m_addonMgr.Events().Subscribe(this, &CContextMenuManager::OnEvent);
+ CPVRContextMenuManager::GetInstance().Events().Subscribe(this, &CContextMenuManager::OnPVREvent);
+
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ m_items = {
+ std::make_shared<CONTEXTMENU::CVideoBrowse>(),
+ std::make_shared<CONTEXTMENU::CVideoResume>(),
+ std::make_shared<CONTEXTMENU::CVideoPlay>(),
+ std::make_shared<CONTEXTMENU::CVideoPlayAndQueue>(),
+ std::make_shared<CONTEXTMENU::CVideoPlayNext>(),
+ std::make_shared<CONTEXTMENU::CVideoQueue>(),
+ std::make_shared<CONTEXTMENU::CMusicBrowse>(),
+ std::make_shared<CONTEXTMENU::CMusicPlay>(),
+ std::make_shared<CONTEXTMENU::CMusicPlayNext>(),
+ std::make_shared<CONTEXTMENU::CMusicQueue>(),
+ std::make_shared<CONTEXTMENU::CAddonInfo>(),
+ std::make_shared<CONTEXTMENU::CEnableAddon>(),
+ std::make_shared<CONTEXTMENU::CDisableAddon>(),
+ std::make_shared<CONTEXTMENU::CAddonSettings>(),
+ std::make_shared<CONTEXTMENU::CCheckForUpdates>(),
+ std::make_shared<CONTEXTMENU::CEpisodeInfo>(),
+ std::make_shared<CONTEXTMENU::CMovieInfo>(),
+ std::make_shared<CONTEXTMENU::CMusicVideoInfo>(),
+ std::make_shared<CONTEXTMENU::CTVShowInfo>(),
+ std::make_shared<CONTEXTMENU::CAlbumInfo>(),
+ std::make_shared<CONTEXTMENU::CArtistInfo>(),
+ std::make_shared<CONTEXTMENU::CSongInfo>(),
+ std::make_shared<CONTEXTMENU::CVideoMarkWatched>(),
+ std::make_shared<CONTEXTMENU::CVideoMarkUnWatched>(),
+ std::make_shared<CONTEXTMENU::CVideoRemoveResumePoint>(),
+ std::make_shared<CONTEXTMENU::CEjectDisk>(),
+ std::make_shared<CONTEXTMENU::CEjectDrive>(),
+ std::make_shared<CONTEXTMENU::CMoveUpFavourite>(),
+ std::make_shared<CONTEXTMENU::CMoveDownFavourite>(),
+ std::make_shared<CONTEXTMENU::CChooseThumbnailForFavourite>(),
+ std::make_shared<CONTEXTMENU::CRenameFavourite>(),
+ std::make_shared<CONTEXTMENU::CRemoveFavourite>(),
+ std::make_shared<CONTEXTMENU::CAddRemoveFavourite>(),
+ };
+
+ ReloadAddonItems();
+
+ const std::vector<std::shared_ptr<IContextMenuItem>> pvrItems(CPVRContextMenuManager::GetInstance().GetMenuItems());
+ for (const auto &item : pvrItems)
+ m_items.emplace_back(item);
+}
+
+void CContextMenuManager::ReloadAddonItems()
+{
+ VECADDONS addons;
+ m_addonMgr.GetAddons(addons, AddonType::CONTEXTMENU_ITEM);
+
+ std::vector<CContextMenuItem> addonItems;
+ for (const auto& addon : addons)
+ {
+ auto items = std::static_pointer_cast<CContextMenuAddon>(addon)->GetItems();
+ for (auto& item : items)
+ {
+ auto it = std::find(addonItems.begin(), addonItems.end(), item);
+ if (it == addonItems.end())
+ addonItems.push_back(item);
+ }
+ }
+
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ m_addonItems = std::move(addonItems);
+
+ CLog::Log(LOGDEBUG, "ContextMenuManager: addon menus reloaded.");
+}
+
+void CContextMenuManager::OnEvent(const ADDON::AddonEvent& event)
+{
+ if (typeid(event) == typeid(AddonEvents::ReInstalled) ||
+ typeid(event) == typeid(AddonEvents::UnInstalled))
+ {
+ ReloadAddonItems();
+ }
+ else if (typeid(event) == typeid(AddonEvents::Enabled))
+ {
+ AddonPtr addon;
+ if (m_addonMgr.GetAddon(event.addonId, addon, AddonType::CONTEXTMENU_ITEM,
+ OnlyEnabled::CHOICE_YES))
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ auto items = std::static_pointer_cast<CContextMenuAddon>(addon)->GetItems();
+ for (auto& item : items)
+ {
+ auto it = std::find(m_addonItems.begin(), m_addonItems.end(), item);
+ if (it == m_addonItems.end())
+ m_addonItems.push_back(item);
+ }
+ CLog::Log(LOGDEBUG, "ContextMenuManager: loaded {}.", event.addonId);
+ }
+ }
+ else if (typeid(event) == typeid(AddonEvents::Disabled))
+ {
+ if (m_addonMgr.HasType(event.addonId, AddonType::CONTEXTMENU_ITEM))
+ {
+ ReloadAddonItems();
+ }
+ }
+}
+
+void CContextMenuManager::OnPVREvent(const PVRContextMenuEvent& event)
+{
+ switch (event.action)
+ {
+ case PVRContextMenuEventAction::ADD_ITEM:
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ m_items.emplace_back(event.item);
+ break;
+ }
+ case PVRContextMenuEventAction::REMOVE_ITEM:
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ auto it = std::find(m_items.begin(), m_items.end(), event.item);
+ if (it != m_items.end())
+ m_items.erase(it);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+bool CContextMenuManager::IsVisible(
+ const CContextMenuItem& menuItem, const CContextMenuItem& root, const CFileItem& fileItem) const
+{
+ if (menuItem.GetLabel(fileItem).empty() || !root.IsParentOf(menuItem))
+ return false;
+
+ if (menuItem.IsGroup())
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ return std::any_of(m_addonItems.begin(), m_addonItems.end(),
+ [&](const CContextMenuItem& other){ return menuItem.IsParentOf(other) && other.IsVisible(fileItem); });
+ }
+
+ return menuItem.IsVisible(fileItem);
+}
+
+ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
+{
+ ContextMenuView result;
+ //! @todo implement group support
+ if (&root == &MAIN)
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ std::copy_if(m_items.begin(), m_items.end(), std::back_inserter(result),
+ [&](const std::shared_ptr<IContextMenuItem>& menu){ return menu->IsVisible(fileItem); });
+ }
+ return result;
+}
+
+ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
+{
+ ContextMenuView result;
+ {
+ std::unique_lock<CCriticalSection> lock(m_criticalSection);
+ for (const auto& menu : m_addonItems)
+ if (IsVisible(menu, root, fileItem))
+ result.emplace_back(new CContextMenuItem(menu));
+ }
+
+ if (&root == &MANAGE)
+ {
+ std::sort(result.begin(), result.end(),
+ [&](const ContextMenuView::value_type& lhs, const ContextMenuView::value_type& rhs)
+ {
+ return lhs->GetLabel(fileItem) < rhs->GetLabel(fileItem);
+ }
+ );
+ }
+ return result;
+}
+
+bool CONTEXTMENU::ShowFor(const std::shared_ptr<CFileItem>& fileItem, const CContextMenuItem& root)
+{
+ if (!fileItem)
+ return false;
+
+ const CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager();
+
+ auto menuItems = contextMenuManager.GetItems(*fileItem, root);
+ for (auto&& item : contextMenuManager.GetAddonItems(*fileItem, root))
+ menuItems.emplace_back(std::move(item));
+
+ CContextButtons buttons;
+ // compute fileitem property-based contextmenu items
+ {
+ int i = 0;
+ while (fileItem->HasProperty(StringUtils::Format("contextmenulabel({})", i)))
+ {
+ buttons.emplace_back(
+ ~buttons.size(),
+ fileItem->GetProperty(StringUtils::Format("contextmenulabel({})", i)).asString());
+ ++i;
+ }
+ }
+ const int propertyMenuSize = buttons.size();
+
+ if (menuItems.empty() && propertyMenuSize == 0)
+ return true;
+
+ buttons.reserve(menuItems.size());
+ for (size_t i = 0; i < menuItems.size(); ++i)
+ buttons.Add(i, menuItems[i]->GetLabel(*fileItem));
+
+ int selected = CGUIDialogContextMenu::Show(buttons);
+ if (selected < 0 || selected >= static_cast<int>(buttons.size()))
+ return false;
+
+ if (selected < propertyMenuSize)
+ {
+ CServiceBroker::GetAppMessenger()->SendMsg(
+ TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
+ fileItem->GetProperty(StringUtils::Format("contextmenuaction({})", selected)).asString());
+ return true;
+ }
+
+ return menuItems[selected - propertyMenuSize]->IsGroup()
+ ? ShowFor(fileItem, static_cast<const CContextMenuItem&>(
+ *menuItems[selected - propertyMenuSize]))
+ : menuItems[selected - propertyMenuSize]->Execute(fileItem);
+}
+
+bool CONTEXTMENU::LoopFrom(const IContextMenuItem& menu, const std::shared_ptr<CFileItem>& fileItem)
+{
+ if (!fileItem)
+ return false;
+ if (menu.IsGroup())
+ return ShowFor(fileItem, static_cast<const CContextMenuItem&>(menu));
+ return menu.Execute(fileItem);
+}