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"
34 using namespace ADDON
;
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()
49 void CContextMenuManager::Deinit()
51 CPVRContextMenuManager::GetInstance().Events().Unsubscribe(this);
52 m_addonMgr
.Events().Unsubscribe(this);
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
);
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
>(),
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()
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
))
135 else if (typeid(event
) == typeid(AddonEvents::Enabled
))
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
))
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
);
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())
185 bool CContextMenuManager::IsVisible(
186 const CContextMenuItem
& menuItem
, const CContextMenuItem
& root
, const CFileItem
& fileItem
) const
188 if (menuItem
.GetLabel(fileItem
).empty() || !root
.IsParentOf(menuItem
))
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
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
); });
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
);
236 bool CONTEXTMENU::ShowFor(const std::shared_ptr
<CFileItem
>& fileItem
, const CContextMenuItem
& root
)
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
251 while (fileItem
->HasProperty(StringUtils::Format("contextmenulabel({})", i
)))
253 buttons
.emplace_back(
255 fileItem
->GetProperty(StringUtils::Format("contextmenulabel({})", i
)).asString());
259 const int propertyMenuSize
= buttons
.size();
261 if (menuItems
.empty() && propertyMenuSize
== 0)
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()))
272 if (selected
< propertyMenuSize
)
274 CServiceBroker::GetAppMessenger()->SendMsg(
275 TMSG_EXECUTE_BUILT_IN
, -1, -1, nullptr,
276 fileItem
->GetProperty(StringUtils::Format("contextmenuaction({})", selected
)).asString());
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
)
291 return ShowFor(fileItem
, static_cast<const CContextMenuItem
&>(menu
));
292 return menu
.Execute(fileItem
);