[filesystem][SpecialProtocol] Removed assert from GetPath
[xbmc.git] / xbmc / ContextMenuManager.cpp
blob0efff747320e9275315258391508918e73cfd732
1 /*
2 * Copyright (C) 2013-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "ContextMenuManager.h"
11 #include "ContextMenuItem.h"
12 #include "ContextMenus.h"
13 #include "FileItem.h"
14 #include "ServiceBroker.h"
15 #include "addons/Addon.h"
16 #include "addons/AddonEvents.h"
17 #include "addons/AddonManager.h"
18 #include "addons/ContextMenuAddon.h"
19 #include "addons/ContextMenus.h"
20 #include "addons/IAddon.h"
21 #include "addons/addoninfo/AddonType.h"
22 #include "dialogs/GUIDialogContextMenu.h"
23 #include "favourites/ContextMenus.h"
24 #include "messaging/ApplicationMessenger.h"
25 #include "music/ContextMenus.h"
26 #include "pvr/PVRContextMenus.h"
27 #include "utils/StringUtils.h"
28 #include "utils/log.h"
29 #include "video/ContextMenus.h"
31 #include <iterator>
32 #include <mutex>
34 using namespace ADDON;
35 using namespace PVR;
37 const CContextMenuItem CContextMenuManager::MAIN = CContextMenuItem::CreateGroup("", "", "kodi.core.main", "");
38 const CContextMenuItem CContextMenuManager::MANAGE = CContextMenuItem::CreateGroup("", "", "kodi.core.manage", "");
41 CContextMenuManager::CContextMenuManager(CAddonMgr& addonMgr)
42 : m_addonMgr(addonMgr) {}
44 CContextMenuManager::~CContextMenuManager()
46 Deinit();
49 void CContextMenuManager::Deinit()
51 CPVRContextMenuManager::GetInstance().Events().Unsubscribe(this);
52 m_addonMgr.Events().Unsubscribe(this);
53 m_items.clear();
56 void CContextMenuManager::Init()
58 m_addonMgr.Events().Subscribe(this, &CContextMenuManager::OnEvent);
59 CPVRContextMenuManager::GetInstance().Events().Subscribe(this, &CContextMenuManager::OnPVREvent);
61 std::unique_lock<CCriticalSection> lock(m_criticalSection);
62 m_items = {
63 std::make_shared<CONTEXTMENU::CVideoBrowse>(),
64 std::make_shared<CONTEXTMENU::CVideoResume>(),
65 std::make_shared<CONTEXTMENU::CVideoPlay>(),
66 std::make_shared<CONTEXTMENU::CVideoPlayAndQueue>(),
67 std::make_shared<CONTEXTMENU::CVideoPlayNext>(),
68 std::make_shared<CONTEXTMENU::CVideoQueue>(),
69 std::make_shared<CONTEXTMENU::CMusicBrowse>(),
70 std::make_shared<CONTEXTMENU::CMusicPlay>(),
71 std::make_shared<CONTEXTMENU::CMusicPlayNext>(),
72 std::make_shared<CONTEXTMENU::CMusicQueue>(),
73 std::make_shared<CONTEXTMENU::CAddonInfo>(),
74 std::make_shared<CONTEXTMENU::CEnableAddon>(),
75 std::make_shared<CONTEXTMENU::CDisableAddon>(),
76 std::make_shared<CONTEXTMENU::CAddonSettings>(),
77 std::make_shared<CONTEXTMENU::CCheckForUpdates>(),
78 std::make_shared<CONTEXTMENU::CEpisodeInfo>(),
79 std::make_shared<CONTEXTMENU::CMovieInfo>(),
80 std::make_shared<CONTEXTMENU::CMusicVideoInfo>(),
81 std::make_shared<CONTEXTMENU::CTVShowInfo>(),
82 std::make_shared<CONTEXTMENU::CAlbumInfo>(),
83 std::make_shared<CONTEXTMENU::CArtistInfo>(),
84 std::make_shared<CONTEXTMENU::CSongInfo>(),
85 std::make_shared<CONTEXTMENU::CVideoMarkWatched>(),
86 std::make_shared<CONTEXTMENU::CVideoMarkUnWatched>(),
87 std::make_shared<CONTEXTMENU::CVideoRemoveResumePoint>(),
88 std::make_shared<CONTEXTMENU::CEjectDisk>(),
89 std::make_shared<CONTEXTMENU::CEjectDrive>(),
90 std::make_shared<CONTEXTMENU::CMoveUpFavourite>(),
91 std::make_shared<CONTEXTMENU::CMoveDownFavourite>(),
92 std::make_shared<CONTEXTMENU::CChooseThumbnailForFavourite>(),
93 std::make_shared<CONTEXTMENU::CRenameFavourite>(),
94 std::make_shared<CONTEXTMENU::CRemoveFavourite>(),
95 std::make_shared<CONTEXTMENU::CAddRemoveFavourite>(),
98 ReloadAddonItems();
100 const std::vector<std::shared_ptr<IContextMenuItem>> pvrItems(CPVRContextMenuManager::GetInstance().GetMenuItems());
101 for (const auto &item : pvrItems)
102 m_items.emplace_back(item);
105 void CContextMenuManager::ReloadAddonItems()
107 VECADDONS addons;
108 m_addonMgr.GetAddons(addons, AddonType::CONTEXTMENU_ITEM);
110 std::vector<CContextMenuItem> addonItems;
111 for (const auto& addon : addons)
113 auto items = std::static_pointer_cast<CContextMenuAddon>(addon)->GetItems();
114 for (auto& item : items)
116 auto it = std::find(addonItems.begin(), addonItems.end(), item);
117 if (it == addonItems.end())
118 addonItems.push_back(item);
122 std::unique_lock<CCriticalSection> lock(m_criticalSection);
123 m_addonItems = std::move(addonItems);
125 CLog::Log(LOGDEBUG, "ContextMenuManager: addon menus reloaded.");
128 void CContextMenuManager::OnEvent(const ADDON::AddonEvent& event)
130 if (typeid(event) == typeid(AddonEvents::ReInstalled) ||
131 typeid(event) == typeid(AddonEvents::UnInstalled))
133 ReloadAddonItems();
135 else if (typeid(event) == typeid(AddonEvents::Enabled))
137 AddonPtr addon;
138 if (m_addonMgr.GetAddon(event.addonId, addon, AddonType::CONTEXTMENU_ITEM,
139 OnlyEnabled::CHOICE_YES))
141 std::unique_lock<CCriticalSection> lock(m_criticalSection);
142 auto items = std::static_pointer_cast<CContextMenuAddon>(addon)->GetItems();
143 for (auto& item : items)
145 auto it = std::find(m_addonItems.begin(), m_addonItems.end(), item);
146 if (it == m_addonItems.end())
147 m_addonItems.push_back(item);
149 CLog::Log(LOGDEBUG, "ContextMenuManager: loaded {}.", event.addonId);
152 else if (typeid(event) == typeid(AddonEvents::Disabled))
154 if (m_addonMgr.HasType(event.addonId, AddonType::CONTEXTMENU_ITEM))
156 ReloadAddonItems();
161 void CContextMenuManager::OnPVREvent(const PVRContextMenuEvent& event)
163 switch (event.action)
165 case PVRContextMenuEventAction::ADD_ITEM:
167 std::unique_lock<CCriticalSection> lock(m_criticalSection);
168 m_items.emplace_back(event.item);
169 break;
171 case PVRContextMenuEventAction::REMOVE_ITEM:
173 std::unique_lock<CCriticalSection> lock(m_criticalSection);
174 auto it = std::find(m_items.begin(), m_items.end(), event.item);
175 if (it != m_items.end())
176 m_items.erase(it);
177 break;
180 default:
181 break;
185 bool CContextMenuManager::IsVisible(
186 const CContextMenuItem& menuItem, const CContextMenuItem& root, const CFileItem& fileItem) const
188 if (menuItem.GetLabel(fileItem).empty() || !root.IsParentOf(menuItem))
189 return false;
191 if (menuItem.IsGroup())
193 std::unique_lock<CCriticalSection> lock(m_criticalSection);
194 return std::any_of(m_addonItems.begin(), m_addonItems.end(),
195 [&](const CContextMenuItem& other){ return menuItem.IsParentOf(other) && other.IsVisible(fileItem); });
198 return menuItem.IsVisible(fileItem);
201 ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
203 ContextMenuView result;
204 //! @todo implement group support
205 if (&root == &MAIN)
207 std::unique_lock<CCriticalSection> lock(m_criticalSection);
208 std::copy_if(m_items.begin(), m_items.end(), std::back_inserter(result),
209 [&](const std::shared_ptr<IContextMenuItem>& menu){ return menu->IsVisible(fileItem); });
211 return result;
214 ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const
216 ContextMenuView result;
218 std::unique_lock<CCriticalSection> lock(m_criticalSection);
219 for (const auto& menu : m_addonItems)
220 if (IsVisible(menu, root, fileItem))
221 result.emplace_back(new CContextMenuItem(menu));
224 if (&root == &MANAGE)
226 std::sort(result.begin(), result.end(),
227 [&](const ContextMenuView::value_type& lhs, const ContextMenuView::value_type& rhs)
229 return lhs->GetLabel(fileItem) < rhs->GetLabel(fileItem);
233 return result;
236 bool CONTEXTMENU::ShowFor(const std::shared_ptr<CFileItem>& fileItem, const CContextMenuItem& root)
238 if (!fileItem)
239 return false;
241 const CContextMenuManager &contextMenuManager = CServiceBroker::GetContextMenuManager();
243 auto menuItems = contextMenuManager.GetItems(*fileItem, root);
244 for (auto&& item : contextMenuManager.GetAddonItems(*fileItem, root))
245 menuItems.emplace_back(std::move(item));
247 CContextButtons buttons;
248 // compute fileitem property-based contextmenu items
250 int i = 0;
251 while (fileItem->HasProperty(StringUtils::Format("contextmenulabel({})", i)))
253 buttons.emplace_back(
254 ~buttons.size(),
255 fileItem->GetProperty(StringUtils::Format("contextmenulabel({})", i)).asString());
256 ++i;
259 const int propertyMenuSize = buttons.size();
261 if (menuItems.empty() && propertyMenuSize == 0)
262 return true;
264 buttons.reserve(menuItems.size());
265 for (size_t i = 0; i < menuItems.size(); ++i)
266 buttons.Add(i, menuItems[i]->GetLabel(*fileItem));
268 int selected = CGUIDialogContextMenu::Show(buttons);
269 if (selected < 0 || selected >= static_cast<int>(buttons.size()))
270 return false;
272 if (selected < propertyMenuSize)
274 CServiceBroker::GetAppMessenger()->SendMsg(
275 TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
276 fileItem->GetProperty(StringUtils::Format("contextmenuaction({})", selected)).asString());
277 return true;
280 return menuItems[selected - propertyMenuSize]->IsGroup()
281 ? ShowFor(fileItem, static_cast<const CContextMenuItem&>(
282 *menuItems[selected - propertyMenuSize]))
283 : menuItems[selected - propertyMenuSize]->Execute(fileItem);
286 bool CONTEXTMENU::LoopFrom(const IContextMenuItem& menu, const std::shared_ptr<CFileItem>& fileItem)
288 if (!fileItem)
289 return false;
290 if (menu.IsGroup())
291 return ShowFor(fileItem, static_cast<const CContextMenuItem&>(menu));
292 return menu.Execute(fileItem);