/* * Copyright (C) 2013-2015 Team XBMC * http://xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ #include "ContextMenuManager.h" #include "ContextMenuItem.h" #include "addons/Addon.h" #include "addons/ContextMenuAddon.h" #include "addons/ContextMenus.h" #include "addons/IAddon.h" #include "music/ContextMenus.h" #include "video/ContextMenus.h" #include "utils/log.h" #include "ServiceBroker.h" #include #include #include #include using namespace ADDON; 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() { m_addonMgr.Events().Unsubscribe(this); m_items.clear(); } CContextMenuManager& CContextMenuManager::GetInstance() { return CServiceBroker::GetContextMenuManager(); } void CContextMenuManager::Init() { m_addonMgr.Events().Subscribe(this, &CContextMenuManager::OnEvent); CSingleLock lock(m_criticalSection); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); m_items.push_back(boost::make_shared()); ReloadAddonItems(); } void CContextMenuManager::ReloadAddonItems() { VECADDONS addons; m_addonMgr.GetAddons(addons, ADDON_CONTEXT_ITEM); std::vector addonItems; for (VECADDONS::const_iterator ait = addons.begin(); ait != addons.end(); ++ait) { const ADDON::AddonPtr &addon = *ait; std::vector items = boost::static_pointer_cast(addon)->GetItems(); for (std::vector::iterator iit = items.begin(); iit != items.end(); ++iit) { CContextMenuItem &item = *iit; std::vector::iterator it = std::find(addonItems.begin(), addonItems.end(), item); if (it == addonItems.end()) addonItems.push_back(item); } } CSingleLock lock(m_criticalSection); m_addonItems = boost::move(addonItems); CLog::Log(LOGDEBUG, "ContextMenuManager: addon menus reloaded."); } bool removeItemIf(const CContextMenuItem& item, const std::vector& menuItems) { if (item.IsGroup()) return false; //keep in case other items use them return std::find(menuItems.begin(), menuItems.end(), item) != menuItems.end(); } bool CContextMenuManager::Unload(const CContextMenuAddon& addon) { CSingleLock lock(m_criticalSection); const std::vector menuItems = addon.GetItems(); std::vector::iterator it = std::remove_if(m_addonItems.begin(), m_addonItems.end(), boost::bind(removeItemIf, _1, boost::cref(menuItems))); m_addonItems.erase(it, m_addonItems.end()); CLog::Log(LOGDEBUG, "ContextMenuManager: %s unloaded.", addon.ID().c_str()); return true; } void CContextMenuManager::OnEvent(const ADDON::AddonEvent& event) { if (typeid(event) == typeid(AddonEvents::InstalledChanged)) { ReloadAddonItems(); } else if (const ADDON::AddonEvents::Enabled *enableEvent = dynamic_cast(&event)) { AddonPtr addon; if (m_addonMgr.GetAddon(enableEvent->id, addon, ADDON_CONTEXT_ITEM)) { CSingleLock lock(m_criticalSection); std::vector items = boost::static_pointer_cast(addon)->GetItems(); for (std::vector::iterator iit = items.begin(); iit != items.end(); ++iit) { CContextMenuItem &item = *iit; std::vector::iterator 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 %s.", enableEvent->id.c_str()); } } } bool isItemParentAndVisible(const CContextMenuItem& other, const CContextMenuItem &menuItem, const CFileItem &fileItem) { return menuItem.IsParentOf(other) && other.IsVisible(fileItem); } 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()) { CSingleLock lock(m_criticalSection); return boost::algorithm::any_of(m_addonItems, boost::bind(isItemParentAndVisible, _1, boost::cref(menuItem), boost::cref(fileItem))); } return menuItem.IsVisible(fileItem); } bool isItemVisible(const boost::shared_ptr &menu, const CFileItem &fileItem) { return menu->IsVisible(fileItem); } ContextMenuView CContextMenuManager::GetItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const { ContextMenuView result; //! @todo implement group support if (&root == &MAIN) { CSingleLock lock(m_criticalSection); boost::algorithm::copy_if(m_items, std::back_inserter(result), boost::bind(isItemVisible, _1, boost::cref(fileItem))); } return result; } bool sortByLabel(const ContextMenuView::value_type& lhs, const ContextMenuView::value_type& rhs, const CFileItem &fileItem) { return lhs->GetLabel(fileItem) < rhs->GetLabel(fileItem); } ContextMenuView CContextMenuManager::GetAddonItems(const CFileItem& fileItem, const CContextMenuItem& root /*= MAIN*/) const { ContextMenuView result; { CSingleLock lock(m_criticalSection); for (std::vector::const_iterator it = m_addonItems.begin(); it != m_addonItems.end(); ++it) if (IsVisible(*it, root, fileItem)) result.push_back(boost::shared_ptr(new CContextMenuItem(*it))); } if (&root == &MAIN || &root == &MANAGE) { std::sort(result.begin(), result.end(), boost::bind(sortByLabel, _1, _2, boost::cref(fileItem))); } return result; } bool CONTEXTMENU::ShowFor(const CFileItemPtr& fileItem, const CContextMenuItem& root) { if (!fileItem) return false; ContextMenuView menuItems = CContextMenuManager::GetInstance().GetItems(*fileItem, root); ContextMenuView vecAddonItems = CContextMenuManager::GetInstance().GetAddonItems(*fileItem, root); for (ContextMenuView::const_iterator it = vecAddonItems.begin(); it != vecAddonItems.end(); ++it) menuItems.push_back(boost::move(*it)); if (menuItems.empty()) return true; CContextButtons buttons; 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(menuItems.size())) return false; return menuItems[selected]->IsGroup() ? ShowFor(fileItem, static_cast(*menuItems[selected])) : menuItems[selected]->Execute(fileItem); } bool CONTEXTMENU::LoopFrom(const IContextMenuItem& menu, const CFileItemPtr& fileItem) { if (!fileItem) return false; if (menu.IsGroup()) return ShowFor(fileItem, static_cast(menu)); return menu.Execute(fileItem); }