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.
9 #include "ContextMenuManager.h"
11 #include "ContextMenuItem.h"
12 #include "ContextMenus.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"
35 using namespace ADDON
;
38 const CContextMenuItem
CContextMenuManager::MAIN
= CContextMenuItem::CreateGroup("", "", "kodi.core.main", "");
39 const CContextMenuItem
CContextMenuManager::MANAGE
= CContextMenuItem::CreateGroup("", "", "kodi.core.manage", "");
42 CContextMenuManager::CContextMenuManager(CAddonMgr
& addonMgr
)
43 : m_addonMgr(addonMgr
) {}
45 CContextMenuManager::~CContextMenuManager()
50 void CContextMenuManager::Deinit()
52 CPVRContextMenuManager::GetInstance().Events().Unsubscribe(this);
53 m_addonMgr
.Events().Unsubscribe(this);
57 void CContextMenuManager::Init()
59 m_addonMgr
.Events().Subscribe(this, &CContextMenuManager::OnEvent
);
60 CPVRContextMenuManager::GetInstance().Events().Subscribe(this, &CContextMenuManager::OnPVREvent
);
62 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
64 std::make_shared
<CONTEXTMENU::CVideoBrowse
>(),
65 std::make_shared
<CONTEXTMENU::CVideoChooseVersion
>(),
66 std::make_shared
<CONTEXTMENU::CVideoPlayVersionUsing
>(),
67 std::make_shared
<CONTEXTMENU::CVideoResume
>(),
68 std::make_shared
<CONTEXTMENU::CVideoPlay
>(),
69 std::make_shared
<CONTEXTMENU::CVideoPlayUsing
>(),
70 std::make_shared
<CONTEXTMENU::CVideoPlayAndQueue
>(),
71 std::make_shared
<CONTEXTMENU::CVideoPlayNext
>(),
72 std::make_shared
<CONTEXTMENU::CVideoQueue
>(),
73 std::make_shared
<CONTEXTMENU::CMusicBrowse
>(),
74 std::make_shared
<CONTEXTMENU::CMusicPlay
>(),
75 std::make_shared
<CONTEXTMENU::CMusicPlayUsing
>(),
76 std::make_shared
<CONTEXTMENU::CMusicPlayNext
>(),
77 std::make_shared
<CONTEXTMENU::CMusicQueue
>(),
78 std::make_shared
<CONTEXTMENU::CAddonInfo
>(),
79 std::make_shared
<CONTEXTMENU::CEnableAddon
>(),
80 std::make_shared
<CONTEXTMENU::CDisableAddon
>(),
81 std::make_shared
<CONTEXTMENU::CAddonSettings
>(),
82 std::make_shared
<CONTEXTMENU::CCheckForUpdates
>(),
83 std::make_shared
<CONTEXTMENU::CEpisodeInfo
>(),
84 std::make_shared
<CONTEXTMENU::CMovieInfo
>(),
85 std::make_shared
<CONTEXTMENU::CMovieSetInfo
>(),
86 std::make_shared
<CONTEXTMENU::CMusicVideoInfo
>(),
87 std::make_shared
<CONTEXTMENU::CTVShowInfo
>(),
88 std::make_shared
<CONTEXTMENU::CSeasonInfo
>(),
89 std::make_shared
<CONTEXTMENU::CAlbumInfo
>(),
90 std::make_shared
<CONTEXTMENU::CArtistInfo
>(),
91 std::make_shared
<CONTEXTMENU::CSongInfo
>(),
92 std::make_shared
<CONTEXTMENU::CVideoMarkWatched
>(),
93 std::make_shared
<CONTEXTMENU::CVideoMarkUnWatched
>(),
94 std::make_shared
<CONTEXTMENU::CVideoRemoveResumePoint
>(),
95 std::make_shared
<CONTEXTMENU::CEjectDisk
>(),
96 std::make_shared
<CONTEXTMENU::CEjectDrive
>(),
97 std::make_shared
<CONTEXTMENU::CFavouritesTargetBrowse
>(),
98 std::make_shared
<CONTEXTMENU::CFavouritesTargetResume
>(),
99 std::make_shared
<CONTEXTMENU::CFavouritesTargetPlay
>(),
100 std::make_shared
<CONTEXTMENU::CFavouritesTargetInfo
>(),
101 std::make_shared
<CONTEXTMENU::CMoveUpFavourite
>(),
102 std::make_shared
<CONTEXTMENU::CMoveDownFavourite
>(),
103 std::make_shared
<CONTEXTMENU::CChooseThumbnailForFavourite
>(),
104 std::make_shared
<CONTEXTMENU::CRenameFavourite
>(),
105 std::make_shared
<CONTEXTMENU::CRemoveFavourite
>(),
106 std::make_shared
<CONTEXTMENU::CAddRemoveFavourite
>(),
107 std::make_shared
<CONTEXTMENU::CFavouritesTargetContextMenu
>(),
112 const std::vector
<std::shared_ptr
<IContextMenuItem
>> pvrItems(CPVRContextMenuManager::GetInstance().GetMenuItems());
113 for (const auto &item
: pvrItems
)
114 m_items
.emplace_back(item
);
117 void CContextMenuManager::ReloadAddonItems()
120 m_addonMgr
.GetAddons(addons
, AddonType::CONTEXTMENU_ITEM
);
122 std::vector
<CContextMenuItem
> addonItems
;
123 for (const auto& addon
: addons
)
125 auto items
= std::static_pointer_cast
<CContextMenuAddon
>(addon
)->GetItems();
126 for (auto& item
: items
)
128 auto it
= std::find(addonItems
.begin(), addonItems
.end(), item
);
129 if (it
== addonItems
.end())
130 addonItems
.push_back(item
);
134 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
135 m_addonItems
= std::move(addonItems
);
137 CLog::Log(LOGDEBUG
, "ContextMenuManager: addon menus reloaded.");
140 void CContextMenuManager::OnEvent(const ADDON::AddonEvent
& event
)
142 if (typeid(event
) == typeid(AddonEvents::ReInstalled
) ||
143 typeid(event
) == typeid(AddonEvents::UnInstalled
))
147 else if (typeid(event
) == typeid(AddonEvents::Enabled
))
150 if (m_addonMgr
.GetAddon(event
.addonId
, addon
, AddonType::CONTEXTMENU_ITEM
,
151 OnlyEnabled::CHOICE_YES
))
153 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
154 auto items
= std::static_pointer_cast
<CContextMenuAddon
>(addon
)->GetItems();
155 for (auto& item
: items
)
157 auto it
= std::find(m_addonItems
.begin(), m_addonItems
.end(), item
);
158 if (it
== m_addonItems
.end())
159 m_addonItems
.push_back(item
);
161 CLog::Log(LOGDEBUG
, "ContextMenuManager: loaded {}.", event
.addonId
);
164 else if (typeid(event
) == typeid(AddonEvents::Disabled
))
166 if (m_addonMgr
.HasType(event
.addonId
, AddonType::CONTEXTMENU_ITEM
))
173 void CContextMenuManager::OnPVREvent(const PVRContextMenuEvent
& event
)
175 switch (event
.action
)
177 case PVRContextMenuEventAction::ADD_ITEM
:
179 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
180 m_items
.emplace_back(event
.item
);
183 case PVRContextMenuEventAction::REMOVE_ITEM
:
185 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
186 auto it
= std::find(m_items
.begin(), m_items
.end(), event
.item
);
187 if (it
!= m_items
.end())
197 bool CContextMenuManager::IsVisible(
198 const CContextMenuItem
& menuItem
, const CContextMenuItem
& root
, const CFileItem
& fileItem
) const
200 if (menuItem
.GetLabel(fileItem
).empty() || !root
.IsParentOf(menuItem
))
203 if (menuItem
.IsGroup())
205 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
206 return std::any_of(m_addonItems
.begin(), m_addonItems
.end(),
207 [&](const CContextMenuItem
& other
){ return menuItem
.IsParentOf(other
) && other
.IsVisible(fileItem
); });
210 return menuItem
.IsVisible(fileItem
);
213 bool CContextMenuManager::HasItems(const CFileItem
& fileItem
, const CContextMenuItem
& root
) const
215 //! @todo implement group support
216 if (&root
== &CContextMenuManager::MAIN
)
218 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
219 return std::any_of(m_items
.cbegin(), m_items
.cend(),
220 [&fileItem
](const std::shared_ptr
<const IContextMenuItem
>& menu
) {
221 return menu
->IsVisible(fileItem
);
227 ContextMenuView
CContextMenuManager::GetItems(const CFileItem
& fileItem
,
228 const CContextMenuItem
& root
) const
230 ContextMenuView result
;
231 //! @todo implement group support
232 if (&root
== &CContextMenuManager::MAIN
)
234 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
235 std::copy_if(m_items
.begin(), m_items
.end(), std::back_inserter(result
),
236 [&](const std::shared_ptr
<IContextMenuItem
>& menu
){ return menu
->IsVisible(fileItem
); });
241 bool CContextMenuManager::HasAddonItems(const CFileItem
& fileItem
,
242 const CContextMenuItem
& root
) const
244 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
245 return std::any_of(m_addonItems
.cbegin(), m_addonItems
.cend(),
246 [this, root
, &fileItem
](const CContextMenuItem
& menu
) {
247 return IsVisible(menu
, root
, fileItem
);
251 ContextMenuView
CContextMenuManager::GetAddonItems(const CFileItem
& fileItem
,
252 const CContextMenuItem
& root
) const
254 ContextMenuView result
;
256 std::unique_lock
<CCriticalSection
> lock(m_criticalSection
);
257 for (const auto& menu
: m_addonItems
)
258 if (IsVisible(menu
, root
, fileItem
))
259 result
.emplace_back(new CContextMenuItem(menu
));
262 if (&root
== &CContextMenuManager::MANAGE
)
264 std::sort(result
.begin(), result
.end(),
265 [&](const ContextMenuView::value_type
& lhs
, const ContextMenuView::value_type
& rhs
)
267 return lhs
->GetLabel(fileItem
) < rhs
->GetLabel(fileItem
);
274 bool CONTEXTMENU::HasAnyMenuItemsFor(const std::shared_ptr
<CFileItem
>& fileItem
,
275 const CContextMenuItem
& root
)
280 if (fileItem
->HasProperty("contextmenulabel(0)"))
283 const CContextMenuManager
& contextMenuManager
= CServiceBroker::GetContextMenuManager();
284 return (contextMenuManager
.HasItems(*fileItem
, root
) ||
285 contextMenuManager
.HasAddonItems(*fileItem
, root
));
288 bool CONTEXTMENU::ShowFor(const std::shared_ptr
<CFileItem
>& fileItem
, const CContextMenuItem
& root
)
293 const CContextMenuManager
&contextMenuManager
= CServiceBroker::GetContextMenuManager();
295 auto menuItems
= contextMenuManager
.GetItems(*fileItem
, root
);
296 for (auto&& item
: contextMenuManager
.GetAddonItems(*fileItem
, root
))
297 menuItems
.emplace_back(std::move(item
));
299 CContextButtons buttons
;
300 // compute fileitem property-based contextmenu items
301 // unless we're browsing a child menu item
302 if (!root
.HasParent())
305 while (fileItem
->HasProperty(StringUtils::Format("contextmenulabel({})", i
)))
307 buttons
.emplace_back(
309 fileItem
->GetProperty(StringUtils::Format("contextmenulabel({})", i
)).asString());
313 const int propertyMenuSize
= buttons
.size();
315 if (menuItems
.empty() && propertyMenuSize
== 0)
318 buttons
.reserve(menuItems
.size());
319 for (size_t i
= 0; i
< menuItems
.size(); ++i
)
320 buttons
.Add(i
, menuItems
[i
]->GetLabel(*fileItem
));
322 int selected
= CGUIDialogContextMenu::Show(buttons
);
323 if (selected
< 0 || selected
>= static_cast<int>(buttons
.size()))
326 if (selected
< propertyMenuSize
)
328 CServiceBroker::GetAppMessenger()->SendMsg(
329 TMSG_EXECUTE_BUILT_IN
, -1, -1, nullptr,
330 fileItem
->GetProperty(StringUtils::Format("contextmenuaction({})", selected
)).asString());
334 return menuItems
[selected
- propertyMenuSize
]->IsGroup()
335 ? ShowFor(fileItem
, static_cast<const CContextMenuItem
&>(
336 *menuItems
[selected
- propertyMenuSize
]))
337 : menuItems
[selected
- propertyMenuSize
]->Execute(fileItem
);
340 bool CONTEXTMENU::LoopFrom(const IContextMenuItem
& menu
, const std::shared_ptr
<CFileItem
>& fileItem
)
345 return ShowFor(fileItem
, static_cast<const CContextMenuItem
&>(menu
));
346 return menu
.Execute(fileItem
);