diff --git a/addons/xbmc.python/addon.xml b/addons/xbmc.python/addon.xml index 1eccd4284b19a..b5530fe74c685 100644 --- a/addons/xbmc.python/addon.xml +++ b/addons/xbmc.python/addon.xml @@ -10,4 +10,5 @@ + diff --git a/addons/xbmc.python/contextitem.xsd b/addons/xbmc.python/contextitem.xsd new file mode 100644 index 0000000000000..3c7c6715282b4 --- /dev/null +++ b/addons/xbmc.python/contextitem.xsd @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/language/English/strings.po b/language/English/strings.po index 717356a773f3c..c3c841273270e 100644 --- a/language/English/strings.po +++ b/language/English/strings.po @@ -12307,7 +12307,13 @@ msgctxt "#24024" msgid "Add-on disabled" msgstr "" -#empty strings from id 24025 to 24026 +#. Used as the type name for context item addons +#: xbmc/addons/Addons.cpp +msgctxt "#24025" +msgid "Context Items" +msgstr "" + +#empty string with id 24026 #: xbmc/addons/Addon.cpp msgctxt "#24027" diff --git a/xbmc/ContextMenuManager.cpp b/xbmc/ContextMenuManager.cpp new file mode 100644 index 0000000000000..127f29fff9c32 --- /dev/null +++ b/xbmc/ContextMenuManager.cpp @@ -0,0 +1,116 @@ +/* + * 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 "addons/Addon.h" +#include "addons/AddonManager.h" +#include "addons/ContextItemAddon.h" +#include "addons/IAddon.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#include "interfaces/python/ContextItemAddonInvoker.h" +#include "interfaces/python/XBPython.h" +#include "Util.h" +#include "utils/log.h" +#include "video/dialogs/GUIDialogVideoInfo.h" + +using namespace ADDON; + +typedef std::map::value_type ValueType; + + +CContextMenuManager::CContextMenuManager() + : m_iCurrentContextId(CONTEXT_BUTTON_FIRST_ADDON) +{ + Init(); +} + +CContextMenuManager& CContextMenuManager::Get() +{ + static CContextMenuManager mgr; + return mgr; +} + +void CContextMenuManager::Init() +{ + //Make sure we load all context items on first usage... + VECADDONS addons; + if (CAddonMgr::Get().GetAddons(ADDON_CONTEXT_ITEM, addons)) + { + for (const auto& addon : addons) + Register(std::static_pointer_cast(addon)); + } +} + +void CContextMenuManager::Register(const ContextItemAddonPtr& cm) +{ + if (!cm) + return; + m_contextAddons[m_iCurrentContextId++] = cm; +} + +bool CContextMenuManager::Unregister(const ContextItemAddonPtr& cm) +{ + if (!cm) + return false; + + auto it = std::find_if(m_contextAddons.begin(), m_contextAddons.end(), + [&](const ValueType& value){ return value.second->ID() == cm->ID(); }); + + if (it != m_contextAddons.end()) + { + m_contextAddons.erase(it); + return true; + } + return false; +} + +ContextItemAddonPtr CContextMenuManager::GetContextItemByID(unsigned int id) +{ + auto it = m_contextAddons.find(id); + if (it != m_contextAddons.end()) + return it->second; + return ContextItemAddonPtr(); +} + +void CContextMenuManager::AddVisibleItems(const CFileItemPtr& item, CContextButtons& list, const std::string& parent /* = "" */) +{ + if (!item) + return; + + for (const auto& kv : m_contextAddons) + { + if (kv.second->GetParent() == parent && kv.second->IsVisible(item)) + list.push_back(std::make_pair(kv.first, kv.second->GetLabel())); + } +} + +bool CContextMenuManager::Execute(unsigned int id, const CFileItemPtr& item) +{ + if (!item) + return false; + + const ContextItemAddonPtr addon = GetContextItemByID(id); + if (!addon || !addon->IsVisible(item)) + return false; + + LanguageInvokerPtr invoker(new CContextItemAddonInvoker(&g_pythonParser, item)); + return (CScriptInvocationManager::Get().ExecuteAsync(addon->LibPath(), invoker, addon) != -1); +} + diff --git a/xbmc/ContextMenuManager.h b/xbmc/ContextMenuManager.h new file mode 100644 index 0000000000000..2dff9dce87af7 --- /dev/null +++ b/xbmc/ContextMenuManager.h @@ -0,0 +1,78 @@ +#pragma once +/* + * 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 +#include "addons/ContextItemAddon.h" +#include "dialogs/GUIDialogContextMenu.h" + +#define CONTEXT_MENU_GROUP_MANAGE "kodi.core.manage" + +class CContextMenuManager +{ +public: + static CContextMenuManager& Get(); + + /*! + * \brief Executes a context menu item. + * \param id - id of the context button to execute. + * \param item - the currently selected item. + * \return true if executed successfully, false otherwise + */ + bool Execute(unsigned int id, const CFileItemPtr& item); + + /*! + * \brief Adds all registered context item to the list. + * \param item - the currently selected item. + * \param list - the context menu. + * \param parent - the ID of the context menu. Empty string if the root menu. + * CONTEXT_MENU_GROUP_MANAGE if the 'manage' submenu. + */ + void AddVisibleItems(const CFileItemPtr& item, CContextButtons& list, const std::string& parent = ""); + + /*! + * \brief Adds a context item to this manager. + * NOTE: only 'enabled' context addons should be added. + */ + void Register(const ADDON::ContextItemAddonPtr& cm); + + /*! + * \brief Removes a context addon from this manager. + */ + bool Unregister(const ADDON::ContextItemAddonPtr& cm); + +private: + CContextMenuManager(); + CContextMenuManager(const CContextMenuManager&); + CContextMenuManager const& operator=(CContextMenuManager const&); + virtual ~CContextMenuManager() {} + + void Init(); + + /*! + * \brief Get a context menu item by its assigned id. + * \param id - the button id of the context item. + * \return the addon or NULL if no item with given id is registered. + */ + ADDON::ContextItemAddonPtr GetContextItemByID(const unsigned int id); + + std::map m_contextAddons; + unsigned int m_iCurrentContextId; +}; diff --git a/xbmc/Makefile.in b/xbmc/Makefile.in index 86a9bd16a52c3..c95680118e8cc 100644 --- a/xbmc/Makefile.in +++ b/xbmc/Makefile.in @@ -5,6 +5,7 @@ SRCS=Application.cpp \ Autorun.cpp \ AutoSwitch.cpp \ BackgroundInfoLoader.cpp \ + ContextMenuManager.cpp \ CompileInfo.cpp \ CueDocument.cpp \ DatabaseManager.cpp \ diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp index 2def23005f307..2aa849f87b134 100644 --- a/xbmc/addons/Addon.cpp +++ b/xbmc/addons/Addon.cpp @@ -80,6 +80,7 @@ static const TypeMapping types[] = {"xbmc.python.library", ADDON_SCRIPT_LIBRARY, 24081, "DefaultAddonHelper.png" }, {"xbmc.python.module", ADDON_SCRIPT_MODULE, 24082, "DefaultAddonLibrary.png" }, {"xbmc.subtitle.module", ADDON_SUBTITLE_MODULE, 24012, "DefaultAddonSubtitles.png" }, + {"kodi.context.item", ADDON_CONTEXT_ITEM, 24025, "DefaultAddonContextItem.png" }, {"xbmc.gui.skin", ADDON_SKIN, 166, "DefaultAddonSkin.png" }, {"xbmc.webinterface", ADDON_WEB_INTERFACE, 199, "DefaultAddonWebSkin.png" }, {"xbmc.addon.repository", ADDON_REPOSITORY, 24011, "DefaultAddonRepository.png" }, @@ -360,6 +361,7 @@ void CAddon::BuildLibName(const cp_extension_t *extension) case ADDON_SUBTITLE_MODULE: case ADDON_PLUGIN: case ADDON_SERVICE: + case ADDON_CONTEXT_ITEM: ext = ADDON_PYTHON_EXT; break; default: @@ -394,6 +396,7 @@ void CAddon::BuildLibName(const cp_extension_t *extension) case ADDON_SERVICE: case ADDON_REPOSITORY: case ADDON_AUDIOENCODER: + case ADDON_CONTEXT_ITEM: { std::string temp = CAddonMgr::Get().GetExtValue(extension->configuration, "@library"); m_strLibName = temp; diff --git a/xbmc/addons/Addon.h b/xbmc/addons/Addon.h index 28d8eef8447c8..00e92f697899c 100644 --- a/xbmc/addons/Addon.h +++ b/xbmc/addons/Addon.h @@ -236,6 +236,7 @@ class CAddon : public IAddon bool m_settingsLoaded; bool m_userSettingsLoaded; + virtual void ClearStrings(); private: friend class CAddonMgr; AddonProps m_props; @@ -248,7 +249,7 @@ class CAddon : public IAddon void Disable() { m_enabled = false; ClearStrings();} virtual bool LoadStrings(); - virtual void ClearStrings(); + bool m_hasStrings; bool m_checkedStrings; bool m_hasSettings; diff --git a/xbmc/addons/AddonDatabase.cpp b/xbmc/addons/AddonDatabase.cpp index 6803aa5905b2d..69b47eafcee7c 100644 --- a/xbmc/addons/AddonDatabase.cpp +++ b/xbmc/addons/AddonDatabase.cpp @@ -25,6 +25,7 @@ #include "utils/StringUtils.h" #include "XBDateTime.h" #include "dbwrappers/dataset.h" +#include "addons/ContextItemAddon.h" using namespace ADDON; using namespace std; @@ -620,7 +621,8 @@ bool CAddonDatabase::DisableAddon(const std::string &addonID, bool disable /* = // If the addon is a special, call the disabled handler AddonPtr addon; if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) - || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false)) && addon) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) addon->OnDisabled(); return true; @@ -638,7 +640,8 @@ bool CAddonDatabase::DisableAddon(const std::string &addonID, bool disable /* = // If the addon is a special, call the enabled handler AddonPtr addon; if ((CAddonMgr::Get().GetAddon(addonID, addon, ADDON_SERVICE, false) - || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false)) && addon) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_PVRDLL, false) + || CAddonMgr::Get().GetAddon(addonID, addon, ADDON_CONTEXT_ITEM, false)) && addon) addon->OnEnabled(); } } diff --git a/xbmc/addons/AddonManager.cpp b/xbmc/addons/AddonManager.cpp index 471fa846f1235..b1c31b3670ecc 100644 --- a/xbmc/addons/AddonManager.cpp +++ b/xbmc/addons/AddonManager.cpp @@ -47,6 +47,7 @@ #include "Repository.h" #include "Skin.h" #include "Service.h" +#include "ContextItemAddon.h" #include "Util.h" #include "addons/Webinterface.h" @@ -173,6 +174,8 @@ AddonPtr CAddonMgr::Factory(const cp_extension_t *props) return AddonPtr(new CAddonLibrary(props)); case ADDON_REPOSITORY: return AddonPtr(new CRepository(props)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(props)); default: break; } @@ -682,6 +685,8 @@ AddonPtr CAddonMgr::AddonFromProps(AddonProps& addonProps) return AddonPtr(new CAudioEncoder(addonProps)); case ADDON_REPOSITORY: return AddonPtr(new CRepository(addonProps)); + case ADDON_CONTEXT_ITEM: + return AddonPtr(new CContextItemAddon(addonProps)); default: break; } diff --git a/xbmc/addons/ContextItemAddon.cpp b/xbmc/addons/ContextItemAddon.cpp new file mode 100644 index 0000000000000..4222936df9ebe --- /dev/null +++ b/xbmc/addons/ContextItemAddon.cpp @@ -0,0 +1,108 @@ +/* + * 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 "ContextItemAddon.h" +#include "AddonManager.h" +#include "ContextMenuManager.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "GUIInfoManager.h" +#include "interfaces/info/InfoBool.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "video/dialogs/GUIDialogVideoInfo.h" +#include + +using namespace std; + +namespace ADDON +{ + +CContextItemAddon::CContextItemAddon(const AddonProps &props) + : CAddon(props) +{ } + +CContextItemAddon::~CContextItemAddon() +{ } + +CContextItemAddon::CContextItemAddon(const cp_extension_t *ext) + : CAddon(ext) +{ + ELEMENTS items; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "item", items)) + { + cp_cfg_element_t *item = items[0]; + + m_label = CAddonMgr::Get().GetExtValue(item, "label"); + if (StringUtils::IsNaturalNumber(m_label)) + { + m_label = GetString(boost::lexical_cast(m_label.c_str())); + ClearStrings(); + } + + m_parent = CAddonMgr::Get().GetExtValue(item, "parent"); + + string visible = CAddonMgr::Get().GetExtValue(item, "visible"); + if (visible.empty()) + visible = "false"; + + m_visCondition = g_infoManager.Register(visible, 0); + } +} + +bool CContextItemAddon::OnPreInstall() +{ + return CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnPostInstall(bool restart, bool update) +{ + if (restart) + { + // need to grab the local addon so we have the correct library path to run + AddonPtr localAddon; + if (CAddonMgr::Get().GetAddon(ID(), localAddon, ADDON_CONTEXT_ITEM)) + { + ContextItemAddonPtr contextItem = std::dynamic_pointer_cast(localAddon); + if (contextItem) + CContextMenuManager::Get().Register(contextItem); + } + } +} + +void CContextItemAddon::OnPreUnInstall() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} + +void CContextItemAddon::OnDisabled() +{ + CContextMenuManager::Get().Unregister(std::dynamic_pointer_cast(shared_from_this())); +} +void CContextItemAddon::OnEnabled() +{ + CContextMenuManager::Get().Register(std::dynamic_pointer_cast(shared_from_this())); +} + +bool CContextItemAddon::IsVisible(const CFileItemPtr& item) const +{ + return item && m_visCondition->Get(item.get()); +} + +} diff --git a/xbmc/addons/ContextItemAddon.h b/xbmc/addons/ContextItemAddon.h new file mode 100644 index 0000000000000..628fdcd9ceeb0 --- /dev/null +++ b/xbmc/addons/ContextItemAddon.h @@ -0,0 +1,72 @@ +#pragma once +/* + * 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 +#include +#include "Addon.h" + +class CFileItem; +typedef std::shared_ptr CFileItemPtr; + +namespace INFO +{ + class InfoBool; + typedef std::shared_ptr InfoPtr; +} + +namespace ADDON +{ + class CContextItemAddon : public CAddon + { + public: + CContextItemAddon(const cp_extension_t *ext); + CContextItemAddon(const AddonProps &props); + virtual ~CContextItemAddon(); + + const std::string& GetLabel() const { return m_label; } + + /*! + * \brief Get the parent category of this context item. + * + * \details Returns empty string if at root level or + * CONTEXT_MENU_GROUP_MANAGE when it should be in the 'manage' submenu. + */ + const std::string& GetParent() const { return m_parent; } + + /*! + * \brief Returns true if this contex menu should be visible for given item. + */ + bool IsVisible(const CFileItemPtr& item) const; + + virtual bool OnPreInstall(); + virtual void OnPostInstall(bool restart, bool update); + virtual void OnPreUnInstall(); + virtual void OnDisabled(); + virtual void OnEnabled(); + + private: + std::string m_label; + std::string m_parent; + INFO::InfoPtr m_visCondition; + }; + + typedef std::shared_ptr ContextItemAddonPtr; +} diff --git a/xbmc/addons/IAddon.h b/xbmc/addons/IAddon.h index 4da99ebd1c1f0..425482fe5e6ee 100644 --- a/xbmc/addons/IAddon.h +++ b/xbmc/addons/IAddon.h @@ -50,6 +50,7 @@ namespace ADDON ADDON_WEB_INTERFACE, ADDON_SERVICE, ADDON_AUDIOENCODER, + ADDON_CONTEXT_ITEM, ADDON_VIDEO, // virtual addon types ADDON_AUDIO, ADDON_IMAGE, diff --git a/xbmc/addons/Makefile b/xbmc/addons/Makefile index 1dbf62e64ed4b..4cf5b17c4d154 100644 --- a/xbmc/addons/Makefile +++ b/xbmc/addons/Makefile @@ -10,6 +10,7 @@ SRCS=Addon.cpp \ AddonStatusHandler.cpp \ AddonVersion.cpp \ AudioEncoder.cpp \ + ContextItemAddon.cpp \ GUIDialogAddonInfo.cpp \ GUIDialogAddonSettings.cpp \ GUIViewStateAddonBrowser.cpp \ diff --git a/xbmc/dialogs/GUIDialogContextMenu.h b/xbmc/dialogs/GUIDialogContextMenu.h index ce0c2b7b86fbc..0d2a8cc8812c7 100644 --- a/xbmc/dialogs/GUIDialogContextMenu.h +++ b/xbmc/dialogs/GUIDialogContextMenu.h @@ -140,7 +140,11 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0, CONTEXT_BUTTON_USER7, CONTEXT_BUTTON_USER8, CONTEXT_BUTTON_USER9, - CONTEXT_BUTTON_USER10 + CONTEXT_BUTTON_USER10, + + //NOTE: this has to be the last in this enum, + //because this one, and the ones higher will be used by context addons + CONTEXT_BUTTON_FIRST_ADDON }; class CContextButtons : public std::vector< std::pair >