diff options
Diffstat (limited to '')
83 files changed, 28756 insertions, 0 deletions
diff --git a/xbmc/interfaces/legacy/Addon.cpp b/xbmc/interfaces/legacy/Addon.cpp new file mode 100644 index 0000000..4b4db1e --- /dev/null +++ b/xbmc/interfaces/legacy/Addon.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Addon.h" + +#include "GUIUserMessages.h" +#include "LanguageHook.h" +#include "ServiceBroker.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/gui/GUIDialogAddonSettings.h" +#include "addons/settings/AddonSettings.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "utils/StringUtils.h" + +using namespace ADDON; + +namespace XBMCAddon +{ + namespace xbmcaddon + { + String Addon::getDefaultId() { return languageHook == NULL ? emptyString : languageHook->GetAddonId(); } + + String Addon::getAddonVersion() { return languageHook == NULL ? emptyString : languageHook->GetAddonVersion(); } + + bool Addon::UpdateSettingInActiveDialog(const char* id, const String& value) + { + ADDON::AddonPtr addon(pAddon); + if (!CServiceBroker::GetGUI()->GetWindowManager().IsWindowActive(WINDOW_DIALOG_ADDON_SETTINGS)) + return false; + + CGUIDialogAddonSettings* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogAddonSettings>(WINDOW_DIALOG_ADDON_SETTINGS); + if (dialog->GetCurrentAddonID() != addon->ID()) + return false; + + CGUIMessage message(GUI_MSG_SETTING_UPDATED, 0, 0); + std::vector<std::string> params; + params.emplace_back(id); + params.push_back(value); + message.SetStringParams(params); + message.SetParam1(ADDON_SETTINGS_ID); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message, WINDOW_DIALOG_ADDON_SETTINGS); + + return true; + } + + Addon::Addon(const char* cid) + { + String id(cid ? cid : emptyString); + + // if the id wasn't passed then get the id from + // the global dictionary + if (id.empty()) + id = getDefaultId(); + + // if we still don't have an id then bail + if (id.empty()) + throw AddonException("No valid addon id could be obtained. None was passed and the script " + "wasn't executed in a normal Kodi manner."); + + if (!CServiceBroker::GetAddonMgr().GetAddon(id, pAddon, OnlyEnabled::CHOICE_YES)) + throw AddonException("Unknown addon id '%s'.", id.c_str()); + + CServiceBroker::GetAddonMgr().AddToUpdateableAddons(pAddon); + } + + Addon::~Addon() + { + CServiceBroker::GetAddonMgr().RemoveFromUpdateableAddons(pAddon); + } + + String Addon::getLocalizedString(int id) + { + return g_localizeStrings.GetAddonString(pAddon->ID(), id); + } + + Settings* Addon::getSettings() + { + return new Settings(pAddon->GetSettings()); + } + + String Addon::getSetting(const char* id) + { + return pAddon->GetSetting(id); + } + + bool Addon::getSettingBool(const char* id) + { + bool value = false; + if (!pAddon->GetSettingBool(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + return value; + } + + int Addon::getSettingInt(const char* id) + { + int value = 0; + if (!pAddon->GetSettingInt(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + return value; + } + + double Addon::getSettingNumber(const char* id) + { + double value = 0.0; + if (!pAddon->GetSettingNumber(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + return value; + } + + String Addon::getSettingString(const char* id) + { + std::string value; + if (!pAddon->GetSettingString(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + return value; + } + + void Addon::setSetting(const char* id, const String& value) + { + DelayedCallGuard dcguard(languageHook); + ADDON::AddonPtr addon(pAddon); + if (!UpdateSettingInActiveDialog(id, value)) + { + addon->UpdateSetting(id, value); + addon->SaveSettings(); + } + } + + bool Addon::setSettingBool(const char* id, bool value) + { + DelayedCallGuard dcguard(languageHook); + ADDON::AddonPtr addon(pAddon); + if (UpdateSettingInActiveDialog(id, value ? "true" : "false")) + return true; + + if (!addon->UpdateSettingBool(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + addon->SaveSettings(); + + return true; + } + + bool Addon::setSettingInt(const char* id, int value) + { + DelayedCallGuard dcguard(languageHook); + ADDON::AddonPtr addon(pAddon); + if (UpdateSettingInActiveDialog(id, std::to_string(value))) + return true; + + if (!addon->UpdateSettingInt(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + addon->SaveSettings(); + + return true; + } + + bool Addon::setSettingNumber(const char* id, double value) + { + DelayedCallGuard dcguard(languageHook); + ADDON::AddonPtr addon(pAddon); + if (UpdateSettingInActiveDialog(id, StringUtils::Format("{:f}", value))) + return true; + + if (!addon->UpdateSettingNumber(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + addon->SaveSettings(); + + return true; + } + + bool Addon::setSettingString(const char* id, const String& value) + { + DelayedCallGuard dcguard(languageHook); + ADDON::AddonPtr addon(pAddon); + if (UpdateSettingInActiveDialog(id, value)) + return true; + + if (!addon->UpdateSettingString(id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type"); + + addon->SaveSettings(); + + return true; + } + + void Addon::openSettings() + { + DelayedCallGuard dcguard(languageHook); + // show settings dialog + ADDON::AddonPtr addon(pAddon); + CGUIDialogAddonSettings::ShowForAddon(addon); + } + + String Addon::getAddonInfo(const char* id) + { + if (StringUtils::CompareNoCase(id, "author") == 0) + return pAddon->Author(); + else if (StringUtils::CompareNoCase(id, "changelog") == 0) + return pAddon->ChangeLog(); + else if (StringUtils::CompareNoCase(id, "description") == 0) + return pAddon->Description(); + else if (StringUtils::CompareNoCase(id, "disclaimer") == 0) + return pAddon->Disclaimer(); + else if (StringUtils::CompareNoCase(id, "fanart") == 0) + return pAddon->FanArt(); + else if (StringUtils::CompareNoCase(id, "icon") == 0) + return pAddon->Icon(); + else if (StringUtils::CompareNoCase(id, "id") == 0) + return pAddon->ID(); + else if (StringUtils::CompareNoCase(id, "name") == 0) + return pAddon->Name(); + else if (StringUtils::CompareNoCase(id, "path") == 0) + return pAddon->Path(); + else if (StringUtils::CompareNoCase(id, "profile") == 0) + return pAddon->Profile(); + else if (StringUtils::CompareNoCase(id, "stars") == 0) + return StringUtils::Format("-1"); + else if (StringUtils::CompareNoCase(id, "summary") == 0) + return pAddon->Summary(); + else if (StringUtils::CompareNoCase(id, "type") == 0) + return ADDON::CAddonInfo::TranslateType(pAddon->Type()); + else if (StringUtils::CompareNoCase(id, "version") == 0) + return pAddon->Version().asString(); + else + throw AddonException("'%s' is an invalid Id", id); + } + } +} diff --git a/xbmc/interfaces/legacy/Addon.h b/xbmc/interfaces/legacy/Addon.h new file mode 100644 index 0000000..97c598f --- /dev/null +++ b/xbmc/interfaces/legacy/Addon.h @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "AddonString.h" +#include "Exception.h" +#include "Settings.h" +#include "addons/IAddon.h" + +namespace XBMCAddon +{ + namespace xbmcaddon + { + XBMCCOMMONS_STANDARD_EXCEPTION(AddonException); + + /// + /// \addtogroup python_xbmcaddon + /// @{ + /// @brief **Kodi's addon class.** + /// + /// Offers classes and functions that manipulate the add-on settings, + /// information and localization. + /// + ///------------------------------------------------------------------------- + /// + /// \python_class{ xbmcaddon.Addon([id]) } + /// + /// Creates a new AddOn class. + /// + /// @param id [opt] string - id of the addon as + /// specified in [addon.xml](http://kodi.wiki/view/Addon.xml) + /// + /// @note Specifying the addon id is not needed.\n + /// Important however is that the addon folder has the same name as the AddOn + /// id provided in [addon.xml](http://kodi.wiki/view/Addon.xml).\n + /// You can optionally specify the addon id from another installed addon to + /// retrieve settings from it. + /// + /// + ///------------------------------------------------------------------------- + /// @python_v13 + /// **id** is optional as it will be auto detected for this add-on instance. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon = xbmcaddon.Addon() + /// self.Addon = xbmcaddon.Addon('script.foo.bar') + /// .. + /// ~~~~~~~~~~~~~ + /// + class Addon : public AddonClass + { + ADDON::AddonPtr pAddon; + + String getDefaultId(); + + String getAddonVersion(); + + bool UpdateSettingInActiveDialog(const char* id, const String& value); + + public: + explicit Addon(const char* id = NULL); + ~Addon() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getLocalizedString(id) } + /// Returns an addon's localized 'string'. + /// + /// @param id integer - id# for string you want to + /// localize. + /// @return Localized 'string' + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 + /// **id** is optional as it will be auto detected for this add-on instance. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// locstr = self.Addon.getLocalizedString(32000) + /// .. + /// ~~~~~~~~~~~~~ + /// + getLocalizedString(...); +#else + String getLocalizedString(int id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSettings() } + /// Returns a wrapper around the addon's settings. + /// + /// @return @ref python_settings wrapper + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings = self.Addon.getSettings() + /// .. + /// ~~~~~~~~~~~~~ + /// + getSettings(...); +#else + Settings* getSettings(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSetting(id) } + /// Returns the value of a setting as string. + /// + /// @param id string - id of the setting that the module + /// needs to access. + /// @return Setting as a string + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 + /// **id** is optional as it will be auto detected for this add-on instance. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// apikey = self.Addon.getSetting('apikey') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSetting(...); +#else + String getSetting(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSettingBool(id) } + /// Returns the value of a setting as a boolean. + /// + /// @param id string - id of the setting that the module + /// needs to access. + /// @return Setting as a boolean + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.getBool()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// enabled = self.Addon.getSettingBool('enabled') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSettingBool(...); +#else + bool getSettingBool(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSettingInt(id) } + /// Returns the value of a setting as an integer. + /// + /// @param id string - id of the setting that the module + /// needs to access. + /// @return Setting as an integer + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.getInt()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// max = self.Addon.getSettingInt('max') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSettingInt(...); +#else + int getSettingInt(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSettingNumber(id) } + /// Returns the value of a setting as a floating point number. + /// + /// @param id string - id of the setting that the module + /// needs to access. + /// @return Setting as a floating point number + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.getNumber()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// max = self.Addon.getSettingNumber('max') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSettingNumber(...); +#else + double getSettingNumber(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).getSettingString(id) } + /// Returns the value of a setting as a string. + /// + /// @param id string - id of the setting that the module + /// needs to access. + /// @return Setting as a string + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.getString()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// apikey = self.Addon.getSettingString('apikey') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSettingString(...); +#else + String getSettingString(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).setSetting(id, value) } + /// Sets a script setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value string - value of the setting. + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 + /// **id** is optional as it will be auto detected for this add-on instance. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.setSetting(id='username', value='teamkodi') + /// .. + /// ~~~~~~~~~~~~~ + /// + setSetting(...); +#else + void setSetting(const char* id, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).setSettingBool(id, value) } + /// Sets a script setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value boolean - value of the setting. + /// @return True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.setBool()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.setSettingBool(id='enabled', value=True) + /// .. + /// ~~~~~~~~~~~~~ + /// + setSettingBool(...); +#else + bool setSettingBool(const char* id, bool value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).setSettingInt(id, value) } + /// Sets a script setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value integer - value of the setting. + /// @return True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.setInt()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.setSettingInt(id='max', value=5) + /// .. + /// ~~~~~~~~~~~~~ + /// + setSettingInt(...); +#else + bool setSettingInt(const char* id, int value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).setSettingNumber(id, value) } + /// Sets a script setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value float - value of the setting. + /// @return True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.setNumber()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.setSettingNumber(id='max', value=5.5) + /// .. + /// ~~~~~~~~~~~~~ + /// + setSettingNumber(...); +#else + bool setSettingNumber(const char* id, double value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).setSettingString(id, value) } + /// Sets a script setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value string or unicode - value of the setting. + /// @return True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 + /// New function added. + /// @python_v20 Deprecated. Use **Settings.setString()** instead. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.setSettingString(id='username', value='teamkodi') + /// .. + /// ~~~~~~~~~~~~~ + /// + setSettingString(...); +#else + bool setSettingString(const char* id, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// @brief \python_func{ xbmcaddon.Addon([id]).openSettings() } + /// Opens this scripts settings dialog. + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.Addon.openSettings() + /// .. + /// ~~~~~~~~~~~~~ + /// + openSettings(); +#else + void openSettings(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcaddon + /// \anchor python_xbmcaddon_Addon + /// @brief \python_func{ xbmcaddon.Addon([id]).getAddonInfo(id) } + /// Returns the value of an addon property as a string. + /// + /// @param id string - id of the property that the + /// module needs to access. + /// @par Choices for the property are + /// | | | | | + /// |:-----------:|:-----------:|:-----------:|:-----------:| + /// | author | changelog | description | disclaimer | + /// | fanart | icon | id | name | + /// | path | profile | stars | summary | + /// | type | version | | | + /// @return AddOn property as a string + /// + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// version = self.Addon.getAddonInfo('version') + /// .. + /// ~~~~~~~~~~~~~ + /// + getAddonInfo(...); +#else + String getAddonInfo(const char* id); +#endif + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/AddonCallback.cpp b/xbmc/interfaces/legacy/AddonCallback.cpp new file mode 100644 index 0000000..c52ce80 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonCallback.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ +#include "AddonCallback.h" + +namespace XBMCAddon +{ + // need a place to put the vtab + AddonCallback::~AddonCallback() = default; + + void AddonCallback::invokeCallback(Callback* callback) + { + if (callback) + { + if (hasHandler()) + handler->invokeCallback(callback); + else + callback->executeCallback(); + } + } +} + diff --git a/xbmc/interfaces/legacy/AddonCallback.h b/xbmc/interfaces/legacy/AddonCallback.h new file mode 100644 index 0000000..dee5074 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonCallback.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "CallbackFunction.h" +#include "CallbackHandler.h" +#include "LanguageHook.h" + +namespace XBMCAddon +{ + + /** + * This class is the superclass for all API classes that are expected + * to be able to handle cross-language polymorphism. + */ + class AddonCallback : public AddonClass + { + protected: + AddonClass::Ref<CallbackHandler> handler; + + bool hasHandler() { return handler.isNotNull(); } + + inline AddonCallback() : handler(NULL) + { + // if there is a LanguageHook, it should be set already. + if (languageHook != NULL) + setHandler(languageHook->GetCallbackHandler()); + } + public: + + ~AddonCallback() override; + + inline void setHandler(CallbackHandler* _handler) { handler = _handler; } + void invokeCallback(Callback* callback); + }; +} diff --git a/xbmc/interfaces/legacy/AddonClass.cpp b/xbmc/interfaces/legacy/AddonClass.cpp new file mode 100644 index 0000000..ffdf3d4 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonClass.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonClass.h" +#ifdef XBMC_ADDON_DEBUG_MEMORY +#include "utils/log.h" +#endif +#include "LanguageHook.h" +#include "AddonUtils.h" + +using namespace XBMCAddonUtils; + +namespace XBMCAddon +{ + // need a place to put the vtab + AddonClass::~AddonClass() + { + m_isDeallocating= true; + + if (languageHook != NULL) + languageHook->Release(); + +#ifdef XBMC_ADDON_DEBUG_MEMORY + isDeleted = false; +#endif + } + + AddonClass::AddonClass() : refs(0L), + languageHook(NULL) + { +#ifdef XBMC_ADDON_DEBUG_MEMORY + isDeleted = false; +#endif + + // check to see if we have a language hook that was prepared for this instantiation + languageHook = LanguageHook::GetLanguageHook(); + if (languageHook != NULL) + { + languageHook->Acquire(); + + // here we assume the language hook was set for the single instantiation of + // this AddonClass (actually - its subclass - but whatever). So we + // will now reset the Tls. This avoids issues if the constructor of the + // subclass throws an exception. + LanguageHook::ClearLanguageHook(); + } + } + +#ifdef XBMC_ADDON_DEBUG_MEMORY + void AddonClass::Release() const + { + if (isDeleted) + CLog::Log(LOGERROR, "NEWADDON REFCNT Releasing dead class {} 0x{:x}", GetClassname(), + (long)(((void*)this))); + + long ct = --refs; +#ifdef LOG_LIFECYCLE_EVENTS + CLog::Log(LOGDEBUG, "NEWADDON REFCNT decrementing to {} on {} 0x{:x}", refs.load(), + GetClassname(), (long)(((void*)this))); +#endif + if(ct == 0) + { + const_cast<AddonClass*>(this)->isDeleted = true; + // we're faking a delete but not doing it so call the destructor explicitly + this->~AddonClass(); + } + } + + void AddonClass::Acquire() const + { + if (isDeleted) + CLog::Log(LOGERROR, "NEWADDON REFCNT Acquiring dead class {} 0x{:x}", GetClassname(), + (long)(((void*)this))); + +#ifdef LOG_LIFECYCLE_EVENTS + CLog::Log(LOGDEBUG, "NEWADDON REFCNT incrementing to {} on {} 0x{:x}", ++refs, GetClassname(), + (long)(((void*)this))); +#else + ++refs; +#endif + } +#endif +} + + diff --git a/xbmc/interfaces/legacy/AddonClass.h b/xbmc/interfaces/legacy/AddonClass.h new file mode 100644 index 0000000..f5de375 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonClass.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +/** + * Defining LOG_LIFECYCLE_EVENTS will log all instantiations, deletions + * and also reference countings (increments and decrements) that take + * place on any Addon* class. + * + * Comment out (or uncomment out) to change the setting. + */ +//#define LOG_LIFECYCLE_EVENTS + +/** + * Defining XBMC_ADDON_DEBUG_MEMORY will make the Acquire and Release + * methods virtual allow the developer to overload them in a sub-class + * and set breakpoints to aid in debugging. It will also cause the + * reference counting mechanism to never actually delete any AddonClass + * instance allowing for the tracking of more references to (supposedly) + * deallocated classes. + * + * Comment out (or uncomment out) to change the setting. + */ +//#define XBMC_ADDON_DEBUG_MEMORY + +#include "AddonString.h" + +#include <mutex> +#ifdef XBMC_ADDON_DEBUG_MEMORY +#include "utils/log.h" +#endif +#include "AddonUtils.h" + +#include <atomic> +#include <typeindex> + +namespace XBMCAddon +{ + class LanguageHook; + + /** + * This class is the superclass for all reference counted classes in the api. + * It provides a means for the bindings to handle all api objects generically. + * + * It also provides some means for debugging "lifecycle" events (see the above + * description of LOG_LIFECYCLE_EVENTS). + * + * If a scripting language bindings require specific handling there is a + * hook to add in these language specifics that can be set here. + */ + class AddonClass : public CCriticalSection + { + private: + mutable std::atomic<long> refs; + bool m_isDeallocating = false; + + // no copying + inline AddonClass(const AddonClass&) = delete; + +#ifdef XBMC_ADDON_DEBUG_MEMORY + bool isDeleted; +#endif + + protected: + LanguageHook* languageHook; + + /** + * This method is meant to be called from the destructor of the + * lowest level class. + * + * It's virtual because it's a convenient place to receive messages that + * we're about to go be deleted but prior to any real tear-down. + * + * Any overloading classes need to remember to pass the call up the chain. + */ + virtual void deallocating() + { + std::unique_lock<CCriticalSection> lock(*this); + m_isDeallocating = true; + } + + /** + * This is meant to be called during static initialization and so isn't + * synchronized. + */ + static short getNextClassIndex(); + + public: + AddonClass(); + virtual ~AddonClass(); + + inline const char* GetClassname() const { return typeid(*this).name(); } + inline LanguageHook* GetLanguageHook() { return languageHook; } + + /** + * This method should be called while holding a Synchronize + * on the object. It will prevent the deallocation during + * the time it's held. + */ + bool isDeallocating() { XBMC_TRACE; return m_isDeallocating; } + + static short getNumAddonClasses(); + +#ifdef XBMC_ADDON_DEBUG_MEMORY + virtual +#else + inline +#endif + void Release() const +#ifndef XBMC_ADDON_DEBUG_MEMORY + { + long ct = --refs; +#ifdef LOG_LIFECYCLE_EVENTS + CLog::Log(LOGDEBUG, "NEWADDON REFCNT decrementing to {} on {} 0x{:x}", ct, GetClassname(), + (long)(((void*)this))); +#endif + if(ct == 0) + delete this; + } +#else + ; +#endif + + +#ifdef XBMC_ADDON_DEBUG_MEMORY + virtual +#else + inline +#endif + void Acquire() const +#ifndef XBMC_ADDON_DEBUG_MEMORY + { +#ifdef LOG_LIFECYCLE_EVENTS + CLog::Log(LOGDEBUG, "NEWADDON REFCNT incrementing to {} on {} 0x{:x}", ++refs, GetClassname(), + (long)(((void*)this))); +#else + ++refs; +#endif + } +#else + ; +#endif + +#define refcheck + /** + * This class is a smart pointer for a Referenced class. + */ + template <class T> class Ref + { + T * ac; + public: + inline Ref() : ac(NULL) {} + inline Ref(const T* _ac) : ac(const_cast<T*>(_ac)) { if (ac) ac->Acquire(); refcheck; } + + // copy semantics + inline Ref(Ref<T> const & oref) : ac(const_cast<T*>(oref.get())) { if (ac) ac->Acquire(); refcheck; } + template<class O> inline Ref(Ref<O> const & oref) : ac(static_cast<T*>(oref.get())) { if (ac) ac->Acquire(); refcheck; } + + /** + * operator= should work with either another smart pointer or a pointer since it will + * be able to convert a pointer to a smart pointer using one of the above constructors. + * + * Note: There is a trick here. The temporary variable is necessary because otherwise the + * following code will fail: + * + * Ref<T> ptr = new T; + * ptr = ptr; + * + * What happens without the tmp is the dereference is called first so the object ends up + * deleted and then the reference happens on a deleted object. The order is reversed + * in the following. + * + * Note: Operator= is ambiguous if you define both an operator=(Ref<T>&) and an operator=(T*). I'm + * opting for the route the boost took here figuring it has more history behind it. + */ + inline Ref<T>& operator=(Ref<T> const & oref) + { T* tmp = ac; ac = const_cast<T*>(oref.get()); if (ac) ac->Acquire(); if (tmp) tmp->Release(); refcheck; return *this; } + + inline T* operator->() const { refcheck; return ac; } + + /** + * This operator doubles as the value in a boolean expression. + */ + inline operator T*() const { refcheck; return ac; } + inline T* get() const { refcheck; return ac; } + inline T& getRef() const { refcheck; return *ac; } + + inline ~Ref() { refcheck; if (ac) ac->Release(); } + inline bool isNull() const { refcheck; return ac == NULL; } + inline bool isNotNull() const { refcheck; return ac != NULL; } + inline bool isSet() const { refcheck; return ac != NULL; } + inline bool operator!() const { refcheck; return ac == NULL; } + inline bool operator==(const AddonClass::Ref<T>& oref) const { refcheck; return ac == oref.ac; } + inline bool operator<(const AddonClass::Ref<T>& oref) const { refcheck; return ac < oref.ac; } // std::set semantics + + // This is there only for boost compatibility + template<class O> inline void reset(Ref<O> const & oref) { refcheck; (*this) = static_cast<T*>(oref.get()); refcheck; } + template<class O> inline void reset(O * oref) { refcheck; (*this) = static_cast<T*>(oref); refcheck; } + inline void reset() { refcheck; if (ac) ac->Release(); ac = NULL; } + }; + + }; +} diff --git a/xbmc/interfaces/legacy/AddonString.h b/xbmc/interfaces/legacy/AddonString.h new file mode 100644 index 0000000..7b1ef41 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonString.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <string> + +namespace XBMCAddon +{ + typedef std::string String; + + extern String emptyString; +} + + + diff --git a/xbmc/interfaces/legacy/AddonUtils.cpp b/xbmc/interfaces/legacy/AddonUtils.cpp new file mode 100644 index 0000000..ad0f0e0 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonUtils.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonUtils.h" + +#include "LanguageHook.h" +#include "addons/Skin.h" +#include "application/Application.h" +#include "utils/XBMCTinyXML.h" +#ifdef ENABLE_XBMC_TRACE_API +#include "utils/log.h" +#endif + +namespace XBMCAddonUtils +{ + GuiLock::GuiLock(XBMCAddon::LanguageHook* languageHook, bool offScreen) + : m_languageHook(languageHook), m_offScreen(offScreen) + { + if (!m_languageHook) + m_languageHook = XBMCAddon::LanguageHook::GetLanguageHook(); + if (m_languageHook) + m_languageHook->DelayedCallOpen(); + + if (!m_offScreen) + g_application.LockFrameMoveGuard(); + } + + GuiLock::~GuiLock() + { + if (!m_offScreen) + g_application.UnlockFrameMoveGuard(); + + if (m_languageHook) + m_languageHook->DelayedCallClose(); + } + + static char defaultImage[1024]; + + const char *getDefaultImage(const char* cControlType, const char* cTextureType) + { + // create an xml block so that we can resolve our defaults + // <control type="type"> + // <description /> + // </control> + TiXmlElement control("control"); + control.SetAttribute("type", cControlType); + TiXmlElement filler("description"); + control.InsertEndChild(filler); + g_SkinInfo->ResolveIncludes(&control); + + // ok, now check for our texture type + TiXmlElement *pTexture = control.FirstChildElement(cTextureType); + if (pTexture) + { + // found our textureType + TiXmlNode *pNode = pTexture->FirstChild(); + if (pNode && pNode->Value()[0] != '-') + { + strncpy(defaultImage, pNode->Value(), sizeof(defaultImage)); + defaultImage[sizeof(defaultImage) - 1] = '\0'; + return defaultImage; + } + } + return ""; + } + +#ifdef ENABLE_XBMC_TRACE_API + static thread_local TraceGuard* tlParent; + + static char** getSpacesArray(int size) + { + char** ret = new char*[size]; + for (int i = 0; i < size; i++) + { + ret[i] = new char[i + 1]; + + int j; + for (j = 0; j < i; j++) + ret[i][j] = ' '; + ret[i][j] = 0; + } + return ret; + } + + static char** spaces = getSpacesArray(256); + + const char* TraceGuard::getSpaces() { return spaces[depth]; } + + TraceGuard::TraceGuard(const char* _function) :function(_function) + { + parent = tlParent; + depth = parent == NULL ? 0 : parent->depth + 1; + + tlParent = this; + + CLog::Log(LOGDEBUG, "{}NEWADDON Entering {}", spaces[depth], function); + } + + TraceGuard::TraceGuard() :function(NULL) + { + parent = tlParent; + depth = parent == NULL ? 0 : parent->depth + 1; + tlParent = this; + // silent + } + + TraceGuard::~TraceGuard() + { + if (function) + CLog::Log(LOGDEBUG, "{}NEWADDON Leaving {}", spaces[depth], function); + + // need to pop the stack + tlParent = this->parent; + } +#endif + + +} diff --git a/xbmc/interfaces/legacy/AddonUtils.h b/xbmc/interfaces/legacy/AddonUtils.h new file mode 100644 index 0000000..2bf7de5 --- /dev/null +++ b/xbmc/interfaces/legacy/AddonUtils.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +/* + * addon.h + * + * Created on: Aug 21, 2010 + * Author: jim + */ + +//#define ENABLE_XBMC_TRACE_API + +#include "threads/CriticalSection.h" + +#include <memory> +#include <mutex> +#include <vector> + +#ifdef TARGET_WINDOWS +#define __PRETTY_FUNCTION__ __FUNCTION__ +#endif + +/** + * This file contains the public definitions for the Addon api. It's meant to be used + * by those writing language bindings. + */ + +namespace XBMCAddon +{ +class LanguageHook; +} + +namespace XBMCAddonUtils +{ + class GuiLock + { + public: + GuiLock(XBMCAddon::LanguageHook* languageHook, bool offScreen); + ~GuiLock(); + + protected: + XBMCAddon::LanguageHook* m_languageHook = nullptr; + bool m_offScreen = false; + }; + + class InvertSingleLockGuard + { + std::unique_lock<CCriticalSection>& lock; + + public: + explicit InvertSingleLockGuard(std::unique_lock<CCriticalSection>& _lock) : lock(_lock) + { + lock.unlock(); + } + ~InvertSingleLockGuard() { lock.lock(); } + }; + + + /* + * Looks in references.xml for image name + * If none exist return default image name + */ + const char *getDefaultImage(const char* cControlType, const char* cTextureType); + +#ifdef ENABLE_XBMC_TRACE_API + class TraceGuard + { + const char* function; + public: + TraceGuard* parent; + int depth; + + const char* getSpaces(); + + explicit TraceGuard(const char* _function); + TraceGuard(); + ~TraceGuard(); + }; +#endif +} + +#ifdef ENABLE_XBMC_TRACE_API +#define XBMC_TRACE XBMCAddonUtils::TraceGuard _tg(__PRETTY_FUNCTION__) +#else +#define XBMC_TRACE +#endif + + diff --git a/xbmc/interfaces/legacy/Alternative.h b/xbmc/interfaces/legacy/Alternative.h new file mode 100644 index 0000000..a715688 --- /dev/null +++ b/xbmc/interfaces/legacy/Alternative.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Exception.h" + +namespace XBMCAddon +{ + enum WhichAlternative { none, first, second }; + + template<typename T1, typename T2> class Alternative + { + public: + private: + WhichAlternative pos = none; + T1 d1; + T2 d2; + + public: + Alternative() = default; + + inline WhichAlternative which() const { return pos; } + + inline T1& former() + { + if (pos == second)// first and none is ok + throw WrongTypeException("Access of XBMCAddon::Alternative as incorrect type"); + if (pos == none) + d1 = T1(); + pos = first; + return d1; + } + + inline const T1& former() const + { + if (pos != first) + throw WrongTypeException("Access of XBMCAddon::Alternative as incorrect type"); + return d1; + } + + inline T2& later() + { + if (pos == first) + throw WrongTypeException("Access of XBMCAddon::Alternative as incorrect type"); + if (pos == none) + d2 = T2(); + pos = second; + return d2; + } + + inline const T2& later() const + { + if (pos != second) + throw WrongTypeException("Access of XBMCAddon::Alternative as incorrect type"); + return d2; + } + + inline operator T1& () { return former(); } + inline operator const T1& () const { return former(); } + inline operator T2& () { return later(); } + inline operator const T2& () const { return later(); } + }; +} + diff --git a/xbmc/interfaces/legacy/CMakeLists.txt b/xbmc/interfaces/legacy/CMakeLists.txt new file mode 100644 index 0000000..d92fc78 --- /dev/null +++ b/xbmc/interfaces/legacy/CMakeLists.txt @@ -0,0 +1,76 @@ +set(SOURCES AddonCallback.cpp + AddonClass.cpp + Addon.cpp + AddonUtils.cpp + CallbackFunction.cpp + CallbackHandler.cpp + Control.cpp + Dialog.cpp + DrmCryptoSession.cpp + File.cpp + InfoTagGame.cpp + InfoTagMusic.cpp + InfoTagPicture.cpp + InfoTagRadioRDS.cpp + InfoTagVideo.cpp + Keyboard.cpp + LanguageHook.cpp + ListItem.cpp + ModuleXbmc.cpp + ModuleXbmcgui.cpp + ModuleXbmcplugin.cpp + ModuleXbmcvfs.cpp + Monitor.cpp + Player.cpp + PlayList.cpp + Settings.cpp + String.cpp + Window.cpp + WindowDialog.cpp + WindowDialogMixin.cpp + WindowXML.cpp) + +set(HEADERS Addon.h + AddonCallback.h + AddonClass.h + AddonString.h + AddonUtils.h + Alternative.h + aojsonrpc.h + CallbackFunction.h + CallbackHandler.h + Control.h + Dialog.h + Dictionary.h + DrmCryptoSession.h + Exception.h + File.h + InfoTagGame.h + InfoTagMusic.h + InfoTagPicture.h + InfoTagRadioRDS.h + InfoTagVideo.h + Keyboard.h + LanguageHook.h + List.h + ListItem.h + ModuleXbmc.h + ModuleXbmcgui.h + ModuleXbmcplugin.h + ModuleXbmcvfs.h + Monitor.h + Player.h + PlayList.h + RenderCapture.h + Settings.h + Stat.h + swighelper.h + Tuple.h + Window.h + WindowDialog.h + WindowDialogMixin.h + WindowException.h + WindowInterceptor.h + WindowXML.h) + +core_add_library(legacy_interface) diff --git a/xbmc/interfaces/legacy/CallbackFunction.cpp b/xbmc/interfaces/legacy/CallbackFunction.cpp new file mode 100644 index 0000000..fdd6cf8 --- /dev/null +++ b/xbmc/interfaces/legacy/CallbackFunction.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "CallbackFunction.h" + +namespace XBMCAddon +{ + Callback::~Callback() { XBMC_TRACE; deallocating(); } +} diff --git a/xbmc/interfaces/legacy/CallbackFunction.h b/xbmc/interfaces/legacy/CallbackFunction.h new file mode 100644 index 0000000..90360d5 --- /dev/null +++ b/xbmc/interfaces/legacy/CallbackFunction.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" + +namespace XBMCAddon +{ + /** + * <p>This is the parent class for the class templates that hold + * a callback. A callback is essentially a templatized + * functor (functoid?) for a call to a member function.</p> + * + * <p>This class combined with the attending CallbackHandlers should make + * sure that the AddonClass isn't in the midst of deallocating when + * the callback executes. In this way the Callback class acts as + * a weak reference.</p> + */ + class Callback : public AddonClass + { + protected: + AddonClass* addonClassObject; + explicit Callback(AddonClass* _object) : addonClassObject(_object) { XBMC_TRACE; } + + public: + virtual void executeCallback() = 0; + ~Callback() override; + + AddonClass* getObject() { XBMC_TRACE; return addonClassObject; } + }; + + struct cb_null_type {}; + + // stub type template to be partial specialized + template<typename M = cb_null_type, typename T1 = cb_null_type, + typename T2 = cb_null_type, typename T3 = cb_null_type, + typename T4 = cb_null_type, typename Extraneous = cb_null_type> + class CallbackFunction {}; + + /** + * This is the template to carry a callback to a member function + * that returns 'void' (has no return) and takes no parameters. + */ + template<class M> class CallbackFunction<M, cb_null_type, cb_null_type, cb_null_type, cb_null_type, cb_null_type> : public Callback + { + public: + typedef void (M::*MemberFunction)(); + + protected: + MemberFunction meth; + M* obj; + + public: + CallbackFunction(M* object, MemberFunction method) : + Callback(object), meth(method), obj(object) { XBMC_TRACE; } + + ~CallbackFunction() override { XBMC_TRACE; deallocating(); } + + void executeCallback() override { XBMC_TRACE; ((*obj).*(meth))(); } + }; + + /** + * This is the template to carry a callback to a member function + * that returns 'void' (has no return) and takes one parameter. + */ + template<class M, typename P1> class CallbackFunction<M,P1, cb_null_type, cb_null_type, cb_null_type, cb_null_type> : public Callback + { + public: + typedef void (M::*MemberFunction)(P1); + + protected: + MemberFunction meth; + M* obj; + P1 param; + + public: + CallbackFunction(M* object, MemberFunction method, P1 parameter) : + Callback(object), meth(method), obj(object), + param(parameter) { XBMC_TRACE; } + + ~CallbackFunction() override { XBMC_TRACE; deallocating(); } + + void executeCallback() override { XBMC_TRACE; ((*obj).*(meth))(param); } + }; + + /** + * This is the template to carry a callback to a member function + * that returns 'void' (has no return) and takes one parameter + * that can be held in an AddonClass::Ref + */ + template<class M, typename P1> class CallbackFunction<M,AddonClass::Ref<P1>, cb_null_type, cb_null_type, cb_null_type, cb_null_type> : public Callback + { + public: + typedef void (M::*MemberFunction)(P1*); + + protected: + MemberFunction meth; + M* obj; + AddonClass::Ref<P1> param; + + public: + CallbackFunction(M* object, MemberFunction method, P1* parameter) : + Callback(object), meth(method), obj(object), + param(parameter) { XBMC_TRACE; } + + ~CallbackFunction() override { XBMC_TRACE; deallocating(); } + + void executeCallback() override { XBMC_TRACE; ((*obj).*(meth))(param); } + }; + + + /** + * This is the template to carry a callback to a member function + * that returns 'void' (has no return) and takes two parameters. + */ + template<class M, typename P1, typename P2> class CallbackFunction<M,P1,P2, cb_null_type, cb_null_type, cb_null_type> : public Callback + { + public: + typedef void (M::*MemberFunction)(P1,P2); + + protected: + MemberFunction meth; + M* obj; + P1 param1; + P2 param2; + + public: + CallbackFunction(M* object, MemberFunction method, P1 parameter, P2 parameter2) : + Callback(object), meth(method), obj(object), + param1(parameter), param2(parameter2) { XBMC_TRACE; } + + ~CallbackFunction() override { XBMC_TRACE; deallocating(); } + + void executeCallback() override { XBMC_TRACE; ((*obj).*(meth))(param1,param2); } + }; + + + /** + * This is the template to carry a callback to a member function + * that returns 'void' (has no return) and takes three parameters. + */ + template<class M, typename P1, typename P2, typename P3> class CallbackFunction<M,P1,P2,P3, cb_null_type, cb_null_type> : public Callback + { + public: + typedef void (M::*MemberFunction)(P1,P2,P3); + + protected: + MemberFunction meth; + M* obj; + P1 param1; + P2 param2; + P3 param3; + + public: + CallbackFunction(M* object, MemberFunction method, P1 parameter, P2 parameter2, P3 parameter3) : + Callback(object), meth(method), obj(object), + param1(parameter), param2(parameter2), param3(parameter3) { XBMC_TRACE; } + + ~CallbackFunction() override { XBMC_TRACE; deallocating(); } + + void executeCallback() override { XBMC_TRACE; ((*obj).*(meth))(param1,param2,param3); } + }; +} + + diff --git a/xbmc/interfaces/legacy/CallbackHandler.cpp b/xbmc/interfaces/legacy/CallbackHandler.cpp new file mode 100644 index 0000000..f567168 --- /dev/null +++ b/xbmc/interfaces/legacy/CallbackHandler.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "CallbackHandler.h" + +#include "AddonUtils.h" +#include "commons/Exception.h" +#include "utils/log.h" + +#include <mutex> +#include <vector> + +namespace XBMCAddon +{ + class AsyncCallbackMessage : public AddonClass + { + public: + AddonClass::Ref<Callback> cb; + AddonClass::Ref<RetardedAsyncCallbackHandler> handler; + AsyncCallbackMessage(Callback* _cb, RetardedAsyncCallbackHandler* _handler) : + cb(_cb), handler(_handler) { XBMC_TRACE; } + }; + + //******************************************************************** + // This holds the callback messages which will be executed. It doesn't + // seem to work correctly with the Ref object so we'll go with Ref*'s + typedef std::vector<AddonClass::Ref<AsyncCallbackMessage> > CallbackQueue; + //******************************************************************** + + static CCriticalSection critSection; + static CallbackQueue g_callQueue; + + void RetardedAsyncCallbackHandler::invokeCallback(Callback* cb) + { + XBMC_TRACE; + std::unique_lock<CCriticalSection> lock(critSection); + g_callQueue.push_back(new AsyncCallbackMessage(cb,this)); + } + + RetardedAsyncCallbackHandler::~RetardedAsyncCallbackHandler() + { + XBMC_TRACE; + std::unique_lock<CCriticalSection> lock(critSection); + + // find any messages that might be there because of me ... and remove them + CallbackQueue::iterator iter = g_callQueue.begin(); + while (iter != g_callQueue.end()) + { + if ((*iter)->handler.get() == this) // then this message is because of me + { + g_callQueue.erase(iter); + iter = g_callQueue.begin(); + } + else + ++iter; + } + } + + void RetardedAsyncCallbackHandler::makePendingCalls() + { + XBMC_TRACE; + std::unique_lock<CCriticalSection> lock(critSection); + CallbackQueue::iterator iter = g_callQueue.begin(); + while (iter != g_callQueue.end()) + { + AddonClass::Ref<AsyncCallbackMessage> p(*iter); + + // only call when we are in the right thread state + if(p->handler->isStateOk(p->cb->getObject())) + { + // remove it from the queue. No matter what we're done with + // this. Even if it doesn't execute for some reason. + g_callQueue.erase(iter); + + // we need to release the critSection lock prior to grabbing the + // lock on the object. Not doing so results in deadlocks. We no + // longer are accessing the g_callQueue so it's fine to do this now + { + XBMCAddonUtils::InvertSingleLockGuard unlock(lock); + + // make sure the object is not deallocating + + // we need to grab the object lock to see if the object of the call + // is deallocating. holding this lock should prevent it from + // deallocating during the execution of this call. +#ifdef ENABLE_XBMC_TRACE_API + CLog::Log(LOGDEBUG, "{}NEWADDON executing callback 0x{:x}", _tg.getSpaces(), + (long)(p->cb.get())); +#endif + AddonClass* obj = (p->cb->getObject()); + AddonClass::Ref<AddonClass> ref(obj); + std::unique_lock<CCriticalSection> lock2(*obj); + if (!p->cb->getObject()->isDeallocating()) + { + try + { + // need to make the call + p->cb->executeCallback(); + } + catch (XbmcCommons::Exception& e) { e.LogThrowMessage(); } + catch (...) + { + CLog::Log(LOGERROR, "Unknown exception while executing callback {:#x}", + reinterpret_cast<int64_t>(p->cb.get())); + } + } + } + + // since the state of the iterator may have been corrupted by + // the changing state of the list from another thread during + // the releasing fo the lock in the immediately preceeding + // codeblock, we need to reset it before continuing the loop + iter = g_callQueue.begin(); + } + else // if we're not in the right thread for this callback... + ++iter; + } + } + + void RetardedAsyncCallbackHandler::clearPendingCalls(void* userData) + { + XBMC_TRACE; + std::unique_lock<CCriticalSection> lock(critSection); + CallbackQueue::iterator iter = g_callQueue.begin(); + while (iter != g_callQueue.end()) + { + AddonClass::Ref<AsyncCallbackMessage> p(*iter); + + if(p->handler->shouldRemoveCallback(p->cb->getObject(),userData)) + { +#ifdef ENABLE_XBMC_TRACE_API + CLog::Log(LOGDEBUG, + "{}NEWADDON removing callback 0x{:x} for PyThreadState 0x{:x} from queue", + _tg.getSpaces(), (long)(p->cb.get()), (long)userData); +#endif + iter = g_callQueue.erase(iter); + } + else + ++iter; + } + } +} + diff --git a/xbmc/interfaces/legacy/CallbackHandler.h b/xbmc/interfaces/legacy/CallbackHandler.h new file mode 100644 index 0000000..223c74a --- /dev/null +++ b/xbmc/interfaces/legacy/CallbackHandler.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "CallbackFunction.h" + +namespace XBMCAddon +{ + /** + * This is the abstraction representing different ways to handle + * the execution of callbacks. Different language bindings may + * have different requirements. + */ + class CallbackHandler : public AddonClass + { + protected: + inline CallbackHandler() = default; + + public: + virtual void invokeCallback(Callback* cb) = 0; + }; + + /** + * This class is primarily for Python support (hence the "Retarded" + * prefix). Python (et. al. Retarded languages) require that + * the context within which a callback executes is under the control + * of the language. Therefore, this handler is used to queue + * messages over to a language controlled thread for eventual + * execution. + * + * @todo Allow a cross thread synchronous execution. + * Fix the stupid means of calling the clearPendingCalls by passing + * userData which is specific to the handler/language type. + */ + class RetardedAsyncCallbackHandler : public CallbackHandler + { + protected: + inline RetardedAsyncCallbackHandler() = default; + public: + + ~RetardedAsyncCallbackHandler() override; + + void invokeCallback(Callback* cb) override; + static void makePendingCalls(); + static void clearPendingCalls(void* userData); + + virtual bool isStateOk(AddonClass* obj) = 0; + virtual bool shouldRemoveCallback(AddonClass* obj, void* userData) = 0; + }; + +} diff --git a/xbmc/interfaces/legacy/Control.cpp b/xbmc/interfaces/legacy/Control.cpp new file mode 100644 index 0000000..62dbac6 --- /dev/null +++ b/xbmc/interfaces/legacy/Control.cpp @@ -0,0 +1,1435 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Control.h" + +#include "AddonUtils.h" +#include "LanguageHook.h" +#include "ServiceBroker.h" +#include "WindowException.h" +#include "guilib/GUIButtonControl.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIControlFactory.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIFadeLabelControl.h" +#include "guilib/GUIFontManager.h" +#include "guilib/GUIImage.h" +#include "guilib/GUILabel.h" +#include "guilib/GUILabelControl.h" +#include "guilib/GUIListContainer.h" +#include "guilib/GUIProgressControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUISliderControl.h" +#include "guilib/GUITextBox.h" +#include "guilib/GUIWindowManager.h" +#include "listproviders/StaticProvider.h" +#include "utils/StringUtils.h" +#include "utils/XBMCTinyXML.h" + +using namespace KODI; + +namespace XBMCAddon +{ + namespace xbmcgui + { + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlFadeLabel::ControlFadeLabel(long x, long y, long width, long height, + const char* font, const char* _textColor, + long _alignment) : + strFont("font13"), textColor(0xffffffff), align(_alignment) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + if (font) + strFont = font; + + if (_textColor) + sscanf(_textColor, "%x", &textColor); + + pGUIControl = NULL; + } + + void ControlFadeLabel::addLabel(const String& label) + { + CGUIMessage msg(GUI_MSG_LABEL_ADD, iParentId, iControlId); + msg.SetLabel(label); + + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + void ControlFadeLabel::reset() + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, iParentId, iControlId); + + vecLabels.clear(); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + CGUIControl* ControlFadeLabel::Create() + { + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = label.focusedColor = textColor; + label.align = align; + pGUIControl = new CGUIFadeLabelControl( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight, + label, + true, + 0, + true, + false); + pGUIControl->SetVisible(m_visible); + + CGUIMessage msg(GUI_MSG_LABEL_RESET, iParentId, iControlId); + pGUIControl->OnMessage(msg); + + return pGUIControl; + } + + void ControlFadeLabel::setScrolling(bool scroll) + { + static_cast<CGUIFadeLabelControl*>(pGUIControl)->SetScrolling(scroll); + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlTextBox::ControlTextBox(long x, long y, long width, long height, + const char* font, const char* _textColor) : + strFont("font13"), textColor(0xffffffff) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + if (font) + strFont = font; + + if (_textColor) + sscanf(_textColor, "%x", &textColor); + } + + void ControlTextBox::setText(const String& text) + { + if (pGUIControl) + { + // create message + CGUIMessage msg(GUI_MSG_LABEL_SET, iParentId, iControlId); + msg.SetLabel(text); + + // send message + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + else + { + m_label = text; + } + } + + String ControlTextBox::getText() + { + if (pGUIControl == nullptr) + return m_label; + + XBMCAddonUtils::GuiLock lock(languageHook, false); + return static_cast<CGUITextBox*>(pGUIControl)->GetDescription(); + } + + void ControlTextBox::reset() + { + if (pGUIControl) + { + // create message + CGUIMessage msg(GUI_MSG_LABEL_RESET, iParentId, iControlId); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + m_label.clear(); + } + + void ControlTextBox::scroll(long position) + { + if (pGUIControl) + static_cast<CGUITextBox*>(pGUIControl)->Scroll((int)position); + } + + void ControlTextBox::autoScroll(int delay, int time, int repeat) + { + if (pGUIControl) + static_cast<CGUITextBox*>(pGUIControl)->SetAutoScrolling(delay, time, repeat); + } + + CGUIControl* ControlTextBox::Create() + { + // create textbox + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = label.focusedColor = textColor; + + pGUIControl = new CGUITextBox(iParentId, iControlId, + (float)dwPosX, (float)dwPosY, (float)dwWidth, (float)dwHeight, + label); + pGUIControl->SetVisible(m_visible); + + // set label + CGUIMessage msg(GUI_MSG_LABEL_SET, iParentId, iControlId); + msg.SetLabel(m_label); + pGUIControl->OnMessage(msg); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlButton::ControlButton(long x, + long y, + long width, + long height, + const String& label, + const char* focusTexture, + const char* noFocusTexture, + long _textOffsetX, + long _textOffsetY, + long alignment, + const char* font, + const char* _textColor, + const char* _disabledColor, + long angle, + const char* _shadowColor, + const char* _focusedColor) + : textOffsetX(_textOffsetX), + textOffsetY(_textOffsetY), + align(alignment), + strFont("font13"), + textColor(0xffffffff), + disabledColor(0x60ffffff), + iAngle(angle), + focusedColor(0xffffffff), + strText(label) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + // if texture is supplied use it, else get default ones + strTextureFocus = focusTexture ? focusTexture : + XBMCAddonUtils::getDefaultImage("button", "texturefocus"); + strTextureNoFocus = noFocusTexture ? noFocusTexture : + XBMCAddonUtils::getDefaultImage("button", "texturenofocus"); + + if (font) strFont = font; + if (_textColor) sscanf( _textColor, "%x", &textColor ); + if (_disabledColor) sscanf( _disabledColor, "%x", &disabledColor ); + if (_shadowColor) sscanf( _shadowColor, "%x", &shadowColor ); + if (_focusedColor) sscanf( _focusedColor, "%x", &focusedColor ); + } + + void ControlButton::setLabel(const String& label, + const char* font, + const char* _textColor, + const char* _disabledColor, + const char* _shadowColor, + const char* _focusedColor, + const String& label2) + { + if (!label.empty()) strText = label; + if (!label2.empty()) strText2 = label2; + if (font) strFont = font; + if (_textColor) sscanf(_textColor, "%x", &textColor); + if (_disabledColor) sscanf( _disabledColor, "%x", &disabledColor ); + if (_shadowColor) sscanf(_shadowColor, "%x", &shadowColor); + if (_focusedColor) sscanf(_focusedColor, "%x", &focusedColor); + + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + static_cast<CGUIButtonControl*>(pGUIControl)->PythonSetLabel(strFont, strText, textColor, shadowColor, focusedColor); + static_cast<CGUIButtonControl*>(pGUIControl)->SetLabel2(strText2); + static_cast<CGUIButtonControl*>(pGUIControl)->PythonSetDisabledColor(disabledColor); + } + } + + void ControlButton::setDisabledColor(const char* color) + { + if (color) sscanf(color, "%x", &disabledColor); + + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + static_cast<CGUIButtonControl*>(pGUIControl)->PythonSetDisabledColor(disabledColor); + } + } + + String ControlButton::getLabel() + { + if (pGUIControl == nullptr) + return strText; + + XBMCAddonUtils::GuiLock lock(languageHook, false); + return static_cast<CGUIButtonControl*>(pGUIControl)->GetLabel(); + } + + String ControlButton::getLabel2() + { + if (pGUIControl == nullptr) + return strText2; + + XBMCAddonUtils::GuiLock lock(languageHook, false); + return static_cast<CGUIButtonControl*>(pGUIControl)->GetLabel2(); + } + + CGUIControl* ControlButton::Create() + { + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = textColor; + label.disabledColor = disabledColor; + label.shadowColor = shadowColor; + label.focusedColor = focusedColor; + label.align = align; + label.offsetX = (float)textOffsetX; + label.offsetY = (float)textOffsetY; + label.angle = (float)-iAngle; + pGUIControl = new CGUIButtonControl( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight, + CTextureInfo(strTextureFocus), + CTextureInfo(strTextureNoFocus), + label); + pGUIControl->SetVisible(m_visible); + + CGUIButtonControl* pGuiButtonControl = + static_cast<CGUIButtonControl*>(pGUIControl); + + pGuiButtonControl->SetLabel(strText); + pGuiButtonControl->SetLabel2(strText2); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlImage::ControlImage(long x, long y, long width, long height, + const char* filename, long aRatio, + const char* _colorDiffuse): + aspectRatio(aRatio), colorDiffuse(0) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + // check if filename exists + strFileName = filename; + if (_colorDiffuse) + sscanf(_colorDiffuse, "%x", &colorDiffuse); + } + + void ControlImage::setImage(const char* imageFilename, const bool useCache) + { + strFileName = imageFilename; + + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + static_cast<CGUIImage*>(pGUIControl)->SetFileName(strFileName, false, useCache); + } + + void ControlImage::setColorDiffuse(const char* cColorDiffuse) + { + if (cColorDiffuse) sscanf(cColorDiffuse, "%x", &colorDiffuse); + else colorDiffuse = 0; + + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + static_cast<CGUIImage*>(pGUIControl)->SetColorDiffuse(GUILIB::GUIINFO::CGUIInfoColor(colorDiffuse)); + } + + CGUIControl* ControlImage::Create() + { + pGUIControl = new CGUIImage(iParentId, iControlId, + (float)dwPosX, (float)dwPosY, (float)dwWidth, (float)dwHeight, + CTextureInfo(strFileName)); + pGUIControl->SetVisible(m_visible); + + if (pGUIControl && aspectRatio <= CAspectRatio::AR_KEEP) + static_cast<CGUIImage*>(pGUIControl)->SetAspectRatio((CAspectRatio::ASPECT_RATIO)aspectRatio); + + if (pGUIControl && colorDiffuse) + static_cast<CGUIImage*>(pGUIControl)->SetColorDiffuse(GUILIB::GUIINFO::CGUIInfoColor(colorDiffuse)); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlProgress::ControlProgress(long x, long y, long width, long height, + const char* texturebg, + const char* textureleft, + const char* texturemid, + const char* textureright, + const char* textureoverlay) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + // if texture is supplied use it, else get default ones + strTextureBg = texturebg ? texturebg : + XBMCAddonUtils::getDefaultImage("progress", "texturebg"); + strTextureLeft = textureleft ? textureleft : + XBMCAddonUtils::getDefaultImage("progress", "lefttexture"); + strTextureMid = texturemid ? texturemid : + XBMCAddonUtils::getDefaultImage("progress", "midtexture"); + strTextureRight = textureright ? textureright : + XBMCAddonUtils::getDefaultImage("progress", "righttexture"); + strTextureOverlay = textureoverlay ? textureoverlay : + XBMCAddonUtils::getDefaultImage("progress", "overlaytexture"); + } + + void ControlProgress::setPercent(float pct) + { + if (pGUIControl) + static_cast<CGUIProgressControl*>(pGUIControl)->SetPercentage(pct); + } + + float ControlProgress::getPercent() + { + return pGUIControl ? static_cast<CGUIProgressControl*>(pGUIControl)->GetPercentage() : 0.0f; + } + + CGUIControl* ControlProgress::Create() + { + pGUIControl = new CGUIProgressControl(iParentId, iControlId, + (float)dwPosX, (float)dwPosY, + (float)dwWidth,(float)dwHeight, + CTextureInfo(strTextureBg), CTextureInfo(strTextureLeft), + CTextureInfo(strTextureMid), CTextureInfo(strTextureRight), + CTextureInfo(strTextureOverlay)); + pGUIControl->SetVisible(m_visible); + + if (pGUIControl && colorDiffuse) + static_cast<CGUIProgressControl*>(pGUIControl)->SetColorDiffuse(GUILIB::GUIINFO::CGUIInfoColor(colorDiffuse)); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlSlider::ControlSlider(long x, long y, long width, long height, + const char* textureback, + const char* texture, + const char* texturefocus, int orientation) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + iOrientation = orientation; + + // if texture is supplied use it, else get default ones + strTextureBack = textureback ? textureback : + XBMCAddonUtils::getDefaultImage("slider", "texturesliderbar"); + strTexture = texture ? texture : + XBMCAddonUtils::getDefaultImage("slider", "textureslidernib"); + strTextureFoc = texturefocus ? texturefocus : + XBMCAddonUtils::getDefaultImage("slider", "textureslidernibfocus"); + } + + float ControlSlider::getPercent() + { + return pGUIControl ? static_cast<CGUISliderControl*>(pGUIControl)->GetPercentage() : 0.0f; + } + + void ControlSlider::setPercent(float pct) + { + if (pGUIControl) + static_cast<CGUISliderControl*>(pGUIControl)->SetPercentage(pct); + } + + int ControlSlider::getInt() + { + return (pGUIControl) ? static_cast<CGUISliderControl*>(pGUIControl)->GetIntValue() : 0; + } + + void ControlSlider::setInt(int value, int min, int delta, int max) + { + if (pGUIControl) + { + static_cast<CGUISliderControl*>(pGUIControl)->SetType(SLIDER_CONTROL_TYPE_INT); + static_cast<CGUISliderControl*>(pGUIControl)->SetRange(min, max); + static_cast<CGUISliderControl*>(pGUIControl)->SetIntInterval(delta); + static_cast<CGUISliderControl*>(pGUIControl)->SetIntValue(value); + } + } + + float ControlSlider::getFloat() + { + return (pGUIControl) ? static_cast<CGUISliderControl*>(pGUIControl)->GetFloatValue() : 0.0f; + } + + void ControlSlider::setFloat(float value, float min, float delta, float max) + { + if (pGUIControl) + { + static_cast<CGUISliderControl*>(pGUIControl)->SetType(SLIDER_CONTROL_TYPE_FLOAT); + static_cast<CGUISliderControl*>(pGUIControl)->SetFloatRange(min, max); + static_cast<CGUISliderControl*>(pGUIControl)->SetFloatInterval(delta); + static_cast<CGUISliderControl*>(pGUIControl)->SetFloatValue(value); + } + } + + CGUIControl* ControlSlider::Create () + { + pGUIControl = new CGUISliderControl(iParentId, iControlId,(float)dwPosX, (float)dwPosY, + (float)dwWidth,(float)dwHeight, + CTextureInfo(strTextureBack),CTextureInfo(strTexture), + CTextureInfo(strTextureFoc), 0, ORIENTATION(iOrientation)); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlGroup::ControlGroup(long x, long y, long width, long height) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + } + + CGUIControl* ControlGroup::Create() + { + pGUIControl = new CGUIControlGroup(iParentId, + iControlId, + (float) dwPosX, + (float) dwPosY, + (float) dwWidth, + (float) dwHeight); + pGUIControl->SetVisible(m_visible); + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + ControlRadioButton::ControlRadioButton(long x, long y, long width, long height, const String& label, + const char* focusOnTexture, const char* noFocusOnTexture, + const char* focusOffTexture, const char* noFocusOffTexture, + const char* focusTexture, const char* noFocusTexture, + long _textOffsetX, long _textOffsetY, + long alignment, const char* font, const char* _textColor, + const char* _disabledColor, long angle, + const char* _shadowColor, const char* _focusedColor, + const char* disabledOnTexture, const char* disabledOffTexture) : + strFont("font13"), textColor(0xffffffff), disabledColor(0x60ffffff), + textOffsetX(_textOffsetX), textOffsetY(_textOffsetY), align(alignment), iAngle(angle), + shadowColor(0), focusedColor(0xffffffff) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + strText = label; + + // if texture is supplied use it, else get default ones + strTextureFocus = focusTexture ? focusTexture : + XBMCAddonUtils::getDefaultImage("button", "texturefocus"); + strTextureNoFocus = noFocusTexture ? noFocusTexture : + XBMCAddonUtils::getDefaultImage("button", "texturenofocus"); + + if (focusOnTexture && noFocusOnTexture) + { + strTextureRadioOnFocus = focusOnTexture; + strTextureRadioOnNoFocus = noFocusOnTexture; + } + else + { + strTextureRadioOnFocus = + XBMCAddonUtils::getDefaultImage("radiobutton", "textureradioonfocus"); + strTextureRadioOnNoFocus = + XBMCAddonUtils::getDefaultImage("radiobutton", "textureradioonnofocus"); + } + + if (focusOffTexture && noFocusOffTexture) + { + strTextureRadioOffFocus = focusOffTexture; + strTextureRadioOffNoFocus = noFocusOffTexture; + } + else + { + strTextureRadioOffFocus = + XBMCAddonUtils::getDefaultImage("radiobutton", "textureradioofffocus"); + strTextureRadioOffNoFocus = + XBMCAddonUtils::getDefaultImage("radiobutton", "textureradiooffnofocus"); + } + + if (font) strFont = font; + if (_textColor) sscanf( _textColor, "%x", &textColor ); + if (_disabledColor) sscanf( _disabledColor, "%x", &disabledColor ); + if (_shadowColor) sscanf( _shadowColor, "%x", &shadowColor ); + if (_focusedColor) sscanf( _focusedColor, "%x", &focusedColor ); + } + + void ControlRadioButton::setSelected(bool selected) + { + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + static_cast<CGUIRadioButtonControl*>(pGUIControl)->SetSelected(selected); + } + } + + bool ControlRadioButton::isSelected() + { + bool isSelected = false; + + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + isSelected = static_cast<CGUIRadioButtonControl*>(pGUIControl)->IsSelected(); + } + return isSelected; + } + + void ControlRadioButton::setLabel(const String& label, + const char* font, + const char* _textColor, + const char* _disabledColor, + const char* _shadowColor, + const char* _focusedColor, + const String& label2) + { + if (!label.empty()) strText = label; + if (font) strFont = font; + if (_textColor) sscanf(_textColor, "%x", &textColor); + if (_disabledColor) sscanf( _disabledColor, "%x", &disabledColor ); + if (_shadowColor) sscanf(_shadowColor, "%x", &shadowColor); + if (_focusedColor) sscanf(_focusedColor, "%x", &focusedColor); + + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + static_cast<CGUIRadioButtonControl*>(pGUIControl)->PythonSetLabel(strFont, strText, textColor, shadowColor, focusedColor); + static_cast<CGUIRadioButtonControl*>(pGUIControl)->PythonSetDisabledColor(disabledColor); + } + } + + void ControlRadioButton::setRadioDimension(long x, long y, long width, long height) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + if (pGUIControl) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + static_cast<CGUIRadioButtonControl*>(pGUIControl)->SetRadioDimensions((float)dwPosX, (float)dwPosY, (float)dwWidth, (float)dwHeight); + } + } + + CGUIControl* ControlRadioButton::Create() + { + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = textColor; + label.disabledColor = disabledColor; + label.shadowColor = shadowColor; + label.focusedColor = focusedColor; + label.align = align; + label.offsetX = (float)textOffsetX; + label.offsetY = (float)textOffsetY; + label.angle = (float)-iAngle; + pGUIControl = new CGUIRadioButtonControl( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight, + CTextureInfo(strTextureFocus), + CTextureInfo(strTextureNoFocus), + label, + CTextureInfo(strTextureRadioOnFocus), + CTextureInfo(strTextureRadioOnNoFocus), + CTextureInfo(strTextureRadioOffFocus), + CTextureInfo(strTextureRadioOffNoFocus), + CTextureInfo(strTextureRadioOnDisabled), + CTextureInfo(strTextureRadioOffDisabled)); + pGUIControl->SetVisible(m_visible); + + CGUIRadioButtonControl* pGuiButtonControl = + static_cast<CGUIRadioButtonControl*>(pGUIControl); + + pGuiButtonControl->SetLabel(strText); + + return pGUIControl; + } + + // ============================================================ + + // ============================================================ + // ============================================================ + Control::~Control() { deallocating(); } + + CGUIControl* Control::Create() + { + throw WindowException("Object is a Control, but can't be added to a window"); + } + + std::vector<int> Control::getPosition() + { + std::vector<int> ret(2); + ret[0] = dwPosX; + ret[1] = dwPosY; + return ret; + } + + void Control::setEnabled(bool enabled) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + pGUIControl->SetEnabled(enabled); + } + + void Control::setVisible(bool visible) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl != nullptr) + { + pGUIControl->SetVisible(visible); + } + else + { + m_visible = visible; + } + } + + bool Control::isVisible() + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock(languageHook, false); + if (pGUIControl) + return pGUIControl->IsVisible(); + else + return false; + } + + void Control::setVisibleCondition(const char* visible, bool allowHiddenFocus) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + + if (pGUIControl) + pGUIControl->SetVisibleCondition(visible, allowHiddenFocus ? "true" : "false"); + } + + void Control::setEnableCondition(const char* enable) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + + if (pGUIControl) + pGUIControl->SetEnableCondition(enable); + } + + void Control::setAnimations(const std::vector< Tuple<String,String> >& eventAttr) + { + CXBMCTinyXML xmlDoc; + TiXmlElement xmlRootElement("control"); + TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement); + if (!pRoot) + throw WindowException("TiXmlNode creation error"); + + std::vector<CAnimation> animations; + + for (unsigned int anim = 0; anim < eventAttr.size(); anim++) + { + const Tuple<String,String>& pTuple = eventAttr[anim]; + + if (pTuple.GetNumValuesSet() != 2) + throw WindowException("Error unpacking tuple found in list"); + + const String& cEvent = pTuple.first(); + const String& cAttr = pTuple.second(); + + TiXmlElement pNode("animation"); + std::vector<std::string> attrs = StringUtils::Split(cAttr, " "); + for (const auto& i : attrs) + { + std::vector<std::string> attrs2 = StringUtils::Split(i, "="); + if (attrs2.size() == 2) + pNode.SetAttribute(attrs2[0], attrs2[1]); + } + TiXmlText value(cEvent.c_str()); + pNode.InsertEndChild(value); + pRoot->InsertEndChild(pNode); + } + + const CRect animRect((float)dwPosX, (float)dwPosY, (float)dwPosX + dwWidth, (float)dwPosY + dwHeight); + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + { + CGUIControlFactory::GetAnimations(pRoot, animRect, iParentId, animations); + pGUIControl->SetAnimations(animations); + } + } + + void Control::setPosition(long x, long y) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + dwPosX = x; + dwPosY = y; + if (pGUIControl) + pGUIControl->SetPosition((float)dwPosX, (float)dwPosY); + } + + void Control::setWidth(long width) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + dwWidth = width; + if (pGUIControl) + pGUIControl->SetWidth((float)dwWidth); + } + + void Control::setHeight(long height) + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + dwHeight = height; + if (pGUIControl) + pGUIControl->SetHeight((float)dwHeight); + } + + void Control::setNavigation(const Control* up, const Control* down, + const Control* left, const Control* right) + { + if(iControlId == 0) + throw WindowException("Control has to be added to a window first"); + + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + { + pGUIControl->SetAction(ACTION_MOVE_UP, CGUIAction(up->iControlId)); + pGUIControl->SetAction(ACTION_MOVE_DOWN, CGUIAction(down->iControlId)); + pGUIControl->SetAction(ACTION_MOVE_LEFT, CGUIAction(left->iControlId)); + pGUIControl->SetAction(ACTION_MOVE_RIGHT, CGUIAction(right->iControlId)); + } + } + } + + void Control::controlUp(const Control* control) + { + if(iControlId == 0) + throw WindowException("Control has to be added to a window first"); + + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + pGUIControl->SetAction(ACTION_MOVE_UP, CGUIAction(control->iControlId)); + } + } + + void Control::controlDown(const Control* control) + { + if(iControlId == 0) + throw WindowException("Control has to be added to a window first"); + + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + pGUIControl->SetAction(ACTION_MOVE_DOWN, CGUIAction(control->iControlId)); + } + } + + void Control::controlLeft(const Control* control) + { + if(iControlId == 0) + throw WindowException("Control has to be added to a window first"); + + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + pGUIControl->SetAction(ACTION_MOVE_LEFT, CGUIAction(control->iControlId)); + } + } + + void Control::controlRight(const Control* control) + { + if(iControlId == 0) + throw WindowException("Control has to be added to a window first"); + + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + if (pGUIControl) + pGUIControl->SetAction(ACTION_MOVE_RIGHT, CGUIAction(control->iControlId)); + } + } + + // ============================================================ + // ControlSpin + // ============================================================ + ControlSpin::ControlSpin() + { + // default values for spin control + color = 0xffffffff; + dwPosX = 0; + dwPosY = 0; + dwWidth = 16; + dwHeight = 16; + + // get default images + strTextureUp = XBMCAddonUtils::getDefaultImage("listcontrol", "textureup"); + strTextureDown = XBMCAddonUtils::getDefaultImage("listcontrol", "texturedown"); + strTextureUpFocus = XBMCAddonUtils::getDefaultImage("listcontrol", "textureupfocus"); + strTextureDownFocus = XBMCAddonUtils::getDefaultImage("listcontrol", "texturedownfocus"); + strTextureUpDisabled = XBMCAddonUtils::getDefaultImage("listcontrol", "textureupdisabled"); + strTextureDownDisabled = XBMCAddonUtils::getDefaultImage("listcontrol", "texturedowndisabled"); + } + + void ControlSpin::setTextures(const char* up, const char* down, + const char* upFocus, + const char* downFocus, + const char* upDisabled, + const char* downDisabled) + { + strTextureUp = up; + strTextureDown = down; + strTextureUpFocus = upFocus; + strTextureDownFocus = downFocus; + strTextureUpDisabled = upDisabled; + strTextureDownDisabled = downDisabled; + /* + PyXBMCGUILock(); + if (self->pGUIControl) + { + CGUISpinControl* pControl = (CGUISpinControl*)self->pGUIControl; + pControl->se + PyXBMCGUIUnlock(); + */ + } + + ControlSpin::~ControlSpin() = default; + // ============================================================ + + // ============================================================ + // ControlLabel + // ============================================================ + ControlLabel::ControlLabel(long x, long y, long width, long height, + const String& label, + const char* font, const char* p_textColor, + const char* p_disabledColor, + long p_alignment, + bool hasPath, long angle) : + strFont("font13"), + textColor(0xffffffff), disabledColor(0x60ffffff), + align(p_alignment), bHasPath(hasPath), iAngle(angle) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + strText = label; + if (font) + strFont = font; + + if (p_textColor) + sscanf(p_textColor, "%x", &textColor); + + if (p_disabledColor) + sscanf( p_disabledColor, "%x", &disabledColor ); + } + + ControlLabel::~ControlLabel() = default; + + CGUIControl* ControlLabel::Create() + { + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = label.focusedColor = textColor; + label.disabledColor = disabledColor; + label.align = align; + label.angle = (float)-iAngle; + pGUIControl = new CGUILabelControl( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight, + label, + false, + bHasPath); + pGUIControl->SetVisible(m_visible); + static_cast<CGUILabelControl*>(pGUIControl)->SetLabel(strText); + return pGUIControl; + } + + void ControlLabel::setLabel(const String& label, const char* font, + const char* textColor, const char* disabledColor, + const char* shadowColor, const char* focusedColor, + const String& label2) + { + strText = label; + CGUIMessage msg(GUI_MSG_LABEL_SET, iParentId, iControlId); + msg.SetLabel(strText); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + String ControlLabel::getLabel() + { + return strText; + } + // ============================================================ + + // ============================================================ + // ControlEdit + // ============================================================ + ControlEdit::ControlEdit(long x, long y, long width, long height, const String& label, + const char* font, const char* _textColor, + const char* _disabledColor, + long _alignment, const char* focusTexture, + const char* noFocusTexture) : + strFont("font13"), textColor(0xffffffff), disabledColor(0x60ffffff), + align(_alignment) + + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + strTextureFocus = focusTexture ? focusTexture : + XBMCAddonUtils::getDefaultImage("edit", "texturefocus"); + + strTextureNoFocus = noFocusTexture ? noFocusTexture : + XBMCAddonUtils::getDefaultImage("edit", "texturenofocus"); + + if (!label.empty()) + { + strText = label; + } + if (font) strFont = font; + if (_textColor) sscanf( _textColor, "%x", &textColor ); + if (_disabledColor) sscanf( _disabledColor, "%x", &disabledColor ); + } + + CGUIControl* ControlEdit::Create() + { + CLabelInfo label; + label.font = g_fontManager.GetFont(strFont); + label.textColor = label.focusedColor = textColor; + label.disabledColor = disabledColor; + label.align = align; + pGUIControl = new CGUIEditControl( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight, + CTextureInfo(strTextureFocus), + CTextureInfo(strTextureNoFocus), + label, + strText); + pGUIControl->SetVisible(m_visible); + + // set label + CGUIMessage msg(GUI_MSG_LABEL_SET, iParentId, iControlId); + msg.SetLabel(strText); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + + return pGUIControl; + } + + void ControlEdit::setLabel(const String& label, const char* font, + const char* textColor, const char* disabledColor, + const char* shadowColor, const char* focusedColor, + const String& label2) + { + strText = label; + CGUIMessage msg(GUI_MSG_LABEL_SET, iParentId, iControlId); + msg.SetLabel(strText); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + String ControlEdit::getLabel() + { + return strText; + } + + void ControlEdit::setText(const String& text) + { + // create message + CGUIMessage msg(GUI_MSG_LABEL2_SET, iParentId, iControlId); + msg.SetLabel(text); + + // send message + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + String ControlEdit::getText() + { + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, iParentId, iControlId); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg, iParentId); + + return msg.GetLabel(); + } + + void ControlEdit::setType(int type, const String& heading) + { + if (pGUIControl) + { + XBMCAddonUtils::GuiLock(languageHook, false); + static_cast<CGUIEditControl*>(pGUIControl)->SetInputType(static_cast<CGUIEditControl::INPUT_TYPE>(type), CVariant{heading}); + } + } + + // ============================================================ + // ControlList + // ============================================================ + ControlList::ControlList(long x, long y, long width, long height, const char* font, + const char* ctextColor, const char* cbuttonTexture, + const char* cbuttonFocusTexture, + const char* cselectedColor, + long _imageWidth, long _imageHeight, long _itemTextXOffset, + long _itemTextYOffset, long _itemHeight, long _space, long _alignmentY) : + strFont("font13"), + textColor(0xe0f0f0f0), selectedColor(0xffffffff), + imageHeight(_imageHeight), imageWidth(_imageWidth), + itemHeight(_itemHeight), space(_space), + itemTextOffsetX(_itemTextXOffset),itemTextOffsetY(_itemTextYOffset), + alignmentY(_alignmentY) + { + dwPosX = x; + dwPosY = y; + dwWidth = width; + dwHeight = height; + + // create a python spin control + pControlSpin = new ControlSpin(); + + // initialize default values + if (font) + strFont = font; + + if (ctextColor) + sscanf( ctextColor, "%x", &textColor ); + + if (cselectedColor) + sscanf( cselectedColor, "%x", &selectedColor ); + + strTextureButton = cbuttonTexture ? cbuttonTexture : + XBMCAddonUtils::getDefaultImage("listcontrol", "texturenofocus"); + + strTextureButtonFocus = cbuttonFocusTexture ? cbuttonFocusTexture : + XBMCAddonUtils::getDefaultImage("listcontrol", "texturefocus"); + + // default values for spin control + pControlSpin->dwPosX = dwWidth - 35; + pControlSpin->dwPosY = dwHeight - 15; + } + + ControlList::~ControlList() = default; + + CGUIControl* ControlList::Create() + { + CLabelInfo label; + label.align = alignmentY; + label.font = g_fontManager.GetFont(strFont); + label.textColor = label.focusedColor = textColor; + //label.shadowColor = shadowColor; + label.selectedColor = selectedColor; + label.offsetX = (float)itemTextOffsetX; + label.offsetY = (float)itemTextOffsetY; + // Second label should have the same font, alignment, and colours as the first, but + // the offsets should be 0. + CLabelInfo label2 = label; + label2.offsetX = label2.offsetY = 0; + label2.align |= XBFONT_RIGHT; + + pGUIControl = new CGUIListContainer( + iParentId, + iControlId, + (float)dwPosX, + (float)dwPosY, + (float)dwWidth, + (float)dwHeight - pControlSpin->dwHeight - 5, + label, label2, + CTextureInfo(strTextureButton), + CTextureInfo(strTextureButtonFocus), + (float)itemHeight, + (float)imageWidth, (float)imageHeight, + (float)space); + pGUIControl->SetVisible(m_visible); + return pGUIControl; + } + + void ControlList::addItem(const Alternative<String, const XBMCAddon::xbmcgui::ListItem* > & item, bool sendMessage) + { + XBMC_TRACE; + + if (item.which() == first) + internAddListItem(ListItem::fromString(item.former()),sendMessage); + else + internAddListItem(item.later(),sendMessage); + } + + void ControlList::addItems(const std::vector<Alternative<String, const XBMCAddon::xbmcgui::ListItem* > > & items) + { + XBMC_TRACE; + + for (const auto& iter : items) + addItem(iter, false); + sendLabelBind(vecItems.size()); + } + + void ControlList::internAddListItem(const AddonClass::Ref<ListItem>& pListItem, + bool sendMessage) + { + if (pListItem.isNull()) + throw WindowException("NULL ListItem passed to ControlList::addListItem"); + + // add item to objects vector + vecItems.push_back(pListItem); + + // send all of the items ... this is what it did before. + if (sendMessage) + sendLabelBind(vecItems.size()); + } + + void ControlList::sendLabelBind(int tail) + { + // construct a CFileItemList to pass 'em on to the list + CGUIListItemPtr items(new CFileItemList()); + for (unsigned int i = vecItems.size() - tail; i < vecItems.size(); i++) + static_cast<CFileItemList*>(items.get())->Add(vecItems[i]->item); + + CGUIMessage msg(GUI_MSG_LABEL_BIND, iParentId, iControlId, 0, 0, items); + msg.SetPointer(items.get()); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + void ControlList::selectItem(long item) + { + // create message + CGUIMessage msg(GUI_MSG_ITEM_SELECT, iParentId, iControlId, item); + + // send message + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + } + + void ControlList::removeItem(int index) + { + if (index < 0 || index >= (int)vecItems.size()) + throw WindowException("Index out of range"); + + vecItems.erase(vecItems.begin() + index); + + sendLabelBind(vecItems.size()); + } + + void ControlList::reset() + { + CGUIMessage msg(GUI_MSG_LABEL_RESET, iParentId, iControlId); + + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iParentId); + + // delete all items from vector + // delete all ListItem from vector + vecItems.clear(); // this should delete all of the objects + } + + Control* ControlList::getSpinControl() + { + return pControlSpin; + } + + long ControlList::getSelectedPosition() + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + + // create message + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, iParentId, iControlId); + long pos = -1; + + // send message + if (!vecItems.empty() && pGUIControl) + { + pGUIControl->OnMessage(msg); + pos = msg.GetParam1(); + } + + return pos; + } + + XBMCAddon::xbmcgui::ListItem* ControlList::getSelectedItem() + { + DelayedCallGuard dcguard(languageHook); + XBMCAddonUtils::GuiLock lock(languageHook, false); + + // create message + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, iParentId, iControlId); + AddonClass::Ref<ListItem> pListItem = NULL; + + // send message + if (!vecItems.empty() && pGUIControl) + { + pGUIControl->OnMessage(msg); + if (msg.GetParam1() >= 0 && (size_t)msg.GetParam1() < vecItems.size()) + pListItem = vecItems[msg.GetParam1()]; + } + + return pListItem.get(); + } + + void ControlList::setImageDimensions(long imageWidth,long imageHeight) + { + CLog::Log(LOGWARNING,"ControlList::setImageDimensions was called but ... it currently isn't defined to do anything."); + /* + PyXBMCGUILock(); + if (self->pGUIControl) + { + CGUIListControl* pListControl = (CGUIListControl*) self->pGUIControl; + pListControl->SetImageDimensions((float)self->dwImageWidth, (float)self->dwImageHeight ); + } + PyXBMCGUIUnlock(); + */ + } + + void ControlList::setItemHeight(long height) + { + CLog::Log(LOGWARNING,"ControlList::setItemHeight was called but ... it currently isn't defined to do anything."); + /* + PyXBMCGUILock(); + if (self->pGUIControl) + { + CGUIListControl* pListControl = (CGUIListControl*) self->pGUIControl; + pListControl->SetItemHeight((float)self->dwItemHeight); + } + PyXBMCGUIUnlock(); + */ + } + + void ControlList::setSpace(int space) + { + CLog::Log(LOGWARNING,"ControlList::setSpace was called but ... it currently isn't defined to do anything."); + /* + PyXBMCGUILock(); + if (self->pGUIControl) + { + CGUIListControl* pListControl = (CGUIListControl*) self->pGUIControl; + pListControl->SetSpaceBetweenItems((float)self->dwSpace); + } + PyXBMCGUIUnlock(); + */ + } + + void ControlList::setPageControlVisible(bool visible) + { + CLog::Log(LOGWARNING,"ControlList::setPageControlVisible was called but ... it currently isn't defined to do anything."); + + // char isOn = true; + + /* + PyXBMCGUILock(); + if (self->pGUIControl) + { + ((CGUIListControl*)self->pGUIControl)->SetPageControlVisible((bool)isOn ); + } + PyXBMCGUIUnlock(); + */ + } + + long ControlList::size() + { + return (long)vecItems.size(); + } + + long ControlList::getItemHeight() + { + return (long)itemHeight; + } + + long ControlList::getSpace() + { + return (long)space; + } + + XBMCAddon::xbmcgui::ListItem* ControlList::getListItem(int index) + { + if (index < 0 || index >= (int)vecItems.size()) + throw WindowException("Index out of range"); + + AddonClass::Ref<ListItem> pListItem = vecItems[index]; + return pListItem.get(); + } + + void ControlList::setStaticContent(const ListItemList* pitems) + { + const ListItemList& vecItems = *pitems; + + std::vector<CGUIStaticItemPtr> items; + + for (unsigned int item = 0; item < vecItems.size(); item++) + { + ListItem* pItem = vecItems[item]; + + // NOTE: This code has likely not worked fully correctly for some time + // In particular, the click behaviour won't be working. + CGUIStaticItemPtr newItem(new CGUIStaticItem(*pItem->item)); + items.push_back(newItem); + } + + // set static list + std::unique_ptr<IListProvider> provider = std::make_unique<CStaticListProvider>(items); + static_cast<CGUIBaseContainer*>(pGUIControl)->SetListProvider(std::move(provider)); + } + + // ============================================================ + + } +} diff --git a/xbmc/interfaces/legacy/Control.h b/xbmc/interfaces/legacy/Control.h new file mode 100644 index 0000000..7441310 --- /dev/null +++ b/xbmc/interfaces/legacy/Control.h @@ -0,0 +1,2960 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Alternative.h" +#include "ListItem.h" +#include "Tuple.h" +#include "guilib/GUIControl.h" +#include "guilib/GUIFont.h" +#include "input/Key.h" +#include "swighelper.h" +#include "utils/ColorUtils.h" + +#include <vector> + + +// hardcoded offsets for button controls (and controls that use button controls) +// ideally they should be dynamically read in as with all the other properties. +#define CONTROL_TEXT_OFFSET_X 10 +#define CONTROL_TEXT_OFFSET_Y 2 + +namespace XBMCAddon +{ + namespace xbmcgui + { + + /// \defgroup python_xbmcgui_control Control + /// \ingroup python_xbmcgui + /// @{ + /// @brief **Code based skin access.** + /// + /// Offers classes and functions that manipulate the add-on gui controls. + /// + ///------------------------------------------------------------------------- + /// + /// \python_class{ Control() } + /// + /// **Code based skin access.** + /// + /// Kodi is noted as having a very flexible and robust framework for its + /// GUI, making theme-skinning and personal customization very accessible. + /// Users can create their own skin (or modify an existing skin) and share + /// it with others. + /// + /// Kodi includes a new GUI library written from scratch. This library + /// allows you to skin/change everything you see in Kodi, from the images, + /// the sizes and positions of all controls, colours, fonts, and text, + /// through to altering navigation and even adding new functionality. The + /// skin system is quite complex, and this portion of the manual is dedicated + /// to providing in depth information on how it all works, along with tips + /// to make the experience a little more pleasant. + /// + ///------------------------------------------------------------------------- + // + class Control : public AddonClass + { + protected: + Control() = default; + + public: + ~Control() override; + +#ifndef SWIG + virtual CGUIControl* Create(); +#endif + + // currently we only accept messages from a button or controllist with a select action + virtual bool canAcceptMessages(int actionId) { return false; } + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getId() } + /// Returns the control's current id as an integer. + /// + /// @return int - Current id + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// id = self.button.getId() + /// ... + /// ~~~~~~~~~~~~~ + /// + getId() +#else + virtual int getId() { return iControlId; } +#endif + + inline bool operator==(const Control& other) const { return iControlId == other.iControlId; } + inline bool operator>(const Control& other) const { return iControlId > other.iControlId; } + inline bool operator<(const Control& other) const { return iControlId < other.iControlId; } + + // hack this because it returns a tuple +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getPosition() } + /// Returns the control's current position as a x,y integer tuple. + /// + /// @return Current position as a x,y integer tuple + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// pos = self.button.getPosition() + /// ... + /// ~~~~~~~~~~~~~ + /// + getPosition(); +#else + virtual std::vector<int> getPosition(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getX() } + /// Returns the control's current X position. + /// + /// @return int - Current X position + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// posX = self.button.getX() + /// ... + /// ~~~~~~~~~~~~~ + /// + getX(); +#else + int getX() { return dwPosX; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getY() } + /// Returns the control's current Y position. + /// + /// @return int - Current Y position + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// posY = self.button.getY() + /// ... + /// ~~~~~~~~~~~~~ + /// + getY(); +#else + int getY() { return dwPosY; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getHeight() } + /// Returns the control's current height as an integer. + /// + /// @return int - Current height + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// height = self.button.getHeight() + /// ... + /// ~~~~~~~~~~~~~ + /// + getHeight(); +#else + virtual int getHeight() { return dwHeight; } +#endif + + // getWidth() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ getWidth() } + /// Returns the control's current width as an integer. + /// + /// @return int - Current width + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// width = self.button.getWidth() + /// ... + /// ~~~~~~~~~~~~~ + /// + getWidth(); +#else + virtual int getWidth() { return dwWidth; } +#endif + + // setEnabled() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setEnabled(enabled) } + /// Sets the control's enabled/disabled state. + /// + /// @param enabled bool - True=enabled / False=disabled. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.setEnabled(False) + /// ... + /// ~~~~~~~~~~~~~ + /// + setEnabled(...); +#else + virtual void setEnabled(bool enabled); +#endif + + // setVisible() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setVisible(visible) } + /// Sets the control's visible/hidden state. + /// \anchor python_xbmcgui_control_setVisible + /// + /// @param visible bool - True=visible / False=hidden. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 You can now define the visible state of a control before it being + /// added to a window. This value will be taken into account when the control is later + /// added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.setVisible(False) + /// ... + /// ~~~~~~~~~~~~~ + /// + setVisible(...); +#else + virtual void setVisible(bool visible); +#endif + + // isVisible() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ isVisible() } + /// Get the control's visible/hidden state with respect to the container/window + /// + /// @note If a given control is set visible (c.f. \ref python_xbmcgui_control_setVisible "setVisible()" + /// but was not yet added to a window, this method will return `False` (the control is not visible yet since + /// it was not added to the window). + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// if self.button.isVisible(): + /// ... + /// ~~~~~~~~~~~~~ + /// + isVisible(...); +#else + virtual bool isVisible(); +#endif + + // setVisibleCondition() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setVisibleCondition(visible[,allowHiddenFocus]) } + /// Sets the control's visible condition. + /// + /// Allows Kodi to control the visible status of the control. + /// + /// [List of Conditions](http://kodi.wiki/view/List_of_Boolean_Conditions) + /// + /// @param visible string - Visible condition + /// @param allowHiddenFocus [opt] bool - True=gains focus even if + /// hidden + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setVisibleCondition(visible[,allowHiddenFocus]) + /// self.button.setVisibleCondition('[Control.IsVisible(41) + !Control.IsVisible(12)]', True) + /// ... + /// ~~~~~~~~~~~~~ + /// + setVisibleCondition(...); +#else + virtual void setVisibleCondition(const char* visible, bool allowHiddenFocus = false); +#endif + + // setEnableCondition() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setEnableCondition(enable) } + /// Sets the control's enabled condition. + /// + /// Allows Kodi to control the enabled status of the control. + /// + /// [List of Conditions](http://kodi.wiki/view/List_of_Boolean_Conditions) + /// + /// @param enable string - Enable condition. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setEnableCondition(enable) + /// self.button.setEnableCondition('System.InternetState') + /// ... + /// ~~~~~~~~~~~~~ + /// + setEnableCondition(...); +#else + virtual void setEnableCondition(const char* enable); +#endif + + // setAnimations() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setAnimations([(event, attr,)*]) } + /// Sets the control's animations. + /// + /// <b>[(event,attr,)*]</b>: list - A list of tuples consisting of event + /// and attributes pairs. + /// + /// [Animating your skin](http://kodi.wiki/view/Animating_Your_Skin) + /// + /// @param event string - The event to animate. + /// @param attr string - The whole attribute string + /// separated by spaces. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setAnimations([(event, attr,)*]) + /// self.button.setAnimations([('focus', 'effect=zoom end=90,247,220,56 time=0',)]) + /// ... + /// ~~~~~~~~~~~~~ + /// + setAnimations(...); +#else + virtual void setAnimations(const std::vector< Tuple<String,String> >& eventAttr); +#endif + + // setPosition() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setPosition(x, y) } + /// Sets the controls position. + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// + /// @note You may use negative integers. (e.g sliding a control into view) + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.setPosition(100, 250) + /// ... + /// ~~~~~~~~~~~~~ + /// + setPosition(...); +#else + virtual void setPosition(long x, long y); +#endif + + // setWidth() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setWidth(width) } + /// Sets the controls width. + /// + /// @param width integer - width of control. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.image.setWidth(100) + /// ... + /// ~~~~~~~~~~~~~ + /// + setWidth(...); +#else + virtual void setWidth(long width); +#endif + + // setHeight() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setHeight(height) } + /// Sets the controls height. + /// + /// @param height integer - height of control. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.image.setHeight(100) + /// ... + /// ~~~~~~~~~~~~~ + /// + setHeight(...); +#else + virtual void setHeight(long height); +#endif + + // setNavigation() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ setNavigation(up, down, left, right) } + /// Sets the controls navigation. + /// + /// @param up control object - control to navigate to on up. + /// @param down control object - control to navigate to on down. + /// @param left control object - control to navigate to on left. + /// @param right control object - control to navigate to on right. + /// @throw TypeError if one of the supplied arguments is not a + /// control type. + /// @throw ReferenceError if one of the controls is not added to a + /// window. + /// + /// @note Same as controlUp(), controlDown(), controlLeft(), controlRight(). + /// Set to self to disable navigation for that direction. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.setNavigation(self.button1, self.button2, self.button3, self.button4) + /// ... + /// ~~~~~~~~~~~~~ + // + setNavigation(...); +#else + virtual void setNavigation(const Control* up, const Control* down, + const Control* left, const Control* right); +#endif + + // controlUp() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ controlUp(control) } + /// Sets the controls up navigation. + /// + /// @param control control object - control to navigate to on up. + /// @throw TypeError if one of the supplied arguments is not a + /// control type. + /// @throw ReferenceError if one of the controls is not added to a + /// window. + /// + /// + /// @note You can also use setNavigation(). Set to self to disable navigation. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.controlUp(self.button1) + /// ... + /// ~~~~~~~~~~~~~ + /// + controlUp(...); +#else + virtual void controlUp(const Control* up); +#endif + + // controlDown() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ controlDown(control) } + /// Sets the controls down navigation. + /// + /// @param control control object - control to navigate to on down. + /// @throw TypeError if one of the supplied arguments is not a + /// control type. + /// @throw ReferenceError if one of the controls is not added to a + /// window. + /// + /// + /// @note You can also use setNavigation(). Set to self to disable navigation. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.controlDown(self.button1) + /// ... + /// ~~~~~~~~~~~~~ + /// + controlDown(...); +#else + virtual void controlDown(const Control* control); +#endif + + // controlLeft() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ controlLeft(control) } + /// Sets the controls left navigation. + /// + /// @param control control object - control to navigate to on left. + /// @throw TypeError if one of the supplied arguments is not a + /// control type. + /// @throw ReferenceError if one of the controls is not added to a + /// window. + /// + /// + /// @note You can also use setNavigation(). Set to self to disable navigation. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.controlLeft(self.button1) + /// ... + /// ~~~~~~~~~~~~~ + /// + controlLeft(...); +#else + virtual void controlLeft(const Control* control); +#endif + + // controlRight() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control + /// @brief \python_func{ controlRight(control) } + /// Sets the controls right navigation. + /// + /// @param control control object - control to navigate to on right. + /// @throw TypeError if one of the supplied arguments is not a + /// control type. + /// @throw ReferenceError if one of the controls is not added to a + /// window. + /// + /// + /// @note You can also use setNavigation(). Set to self to disable navigation. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.button.controlRight(self.button1) + /// ... + /// ~~~~~~~~~~~~~ + /// + controlRight(...); +#else + virtual void controlRight(const Control* control); +#endif + +#ifndef SWIG + int iControlId = 0; + int iParentId = 0; + int dwPosX = 0; + int dwPosY = 0; + int dwWidth = 0; + int dwHeight = 0; + int iControlUp = 0; + int iControlDown = 0; + int iControlLeft = 0; + int iControlRight = 0; + std::string m_label{}; + bool m_visible{true}; + CGUIControl* pGUIControl = nullptr; +#endif + + }; + /// @} + + /// \defgroup python_xbmcgui_control_spin Subclass - ControlSpin + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used for cycling up/down controls.** + /// + /// Offers classes and functions that manipulate the add-on gui controls. + /// + ///------------------------------------------------------------------------- + /// + /// \python_class{ ControlSpin() } + /// + /// **Code based skin access.** + /// + /// The spin control is used for when a list of options can be chosen (such + /// as a page up/down control). You can choose the position, size, and look + /// of the spin control. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @warning **Not working yet**. + /// You can't create this object, it is returned by objects like ControlTextBox and ControlList. + /// + /// + ///------------------------------------------------------------------------- + /// + /// + class ControlSpin : public Control + { + public: + ~ControlSpin() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_spin + /// @brief \python_func{ setTextures(up, down, upFocus, downFocus) } + /// Sets textures for this control. + /// + /// Texture are image files that are used for example in the skin + /// + /// @warning **Not working yet**. + /// + /// @param up label - for the up arrow + /// when it doesn't have focus. + /// @param down label - for the down button + /// when it is not focused. + /// @param upFocus label - for the up button + /// when it has focus. + /// @param downFocus label - for the down button + /// when it has focus. + /// @param upDisabled label - for the up arrow + /// when the button is disabled. + /// @param downDisabled label - for the up arrow + /// when the button is disabled. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setTextures(up, down, upFocus, downFocus, upDisabled, downDisabled) + /// + /// ... + /// ~~~~~~~~~~~~~ + /// + setTextures(...); +#else + virtual void setTextures(const char* up, const char* down, + const char* upFocus, + const char* downFocus, + const char* upDisabled, const char* downDisabled); +#endif + +#ifndef SWIG + UTILS::COLOR::Color color; + std::string strTextureUp; + std::string strTextureDown; + std::string strTextureUpFocus; + std::string strTextureDownFocus; + std::string strTextureUpDisabled; + std::string strTextureDownDisabled; +#endif + + private: + ControlSpin(); + + friend class Window; + friend class ControlList; + + }; + /// @} + + /// \defgroup python_xbmcgui_control_label Subclass - ControlLabel + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to show some lines of text.** + /// + /// \python_class{ ControlLabel(x, y, width, height, label[, font, textColor, + /// disabledColor, alignment, hasPath, angle]) } + /// + /// The label control is used for displaying text in Kodi. You can choose + /// the font, size, colour, location and contents of the text to be + /// displayed. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param label string or unicode - text string. + /// @param font [opt] string - font used for label + /// text. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled + /// label's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// label's label. (e.g. '0xFFFF3300') + /// @param alignment [opt] integer - alignment of label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// @param hasPath [opt] bool - True=stores a + /// path / False=no path + /// @param angle [opt] integer - angle of control. + /// (<b>+</b> rotates CCW, <b>-</b> rotates C) + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # ControlLabel(x, y, width, height, label[, font, textColor, + /// # disabledColor, alignment, hasPath, angle]) + /// self.label = xbmcgui.ControlLabel(100, 250, 125, 75, 'Status', angle=45) + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlLabel : public Control + { + public: + ControlLabel(long x, long y, long width, long height, const String& label, + const char* font = NULL, const char* textColor = NULL, + const char* disabledColor = NULL, + long alignment = XBFONT_LEFT, + bool hasPath = false, long angle = 0); + + ~ControlLabel() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_label + /// @brief \python_func{ getLabel() } + /// Returns the text value for this label. + /// + /// @return This label + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// label = self.label.getLabel() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel(); +#else + virtual String getLabel(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_label + /// @brief \python_func{ setLabel(label[, font, textColor, disabledColor, shadowColor, focusedColor, label2]) } + /// Sets text for this label. + /// + /// @param label string or unicode - text string. + /// @param font [opt] string - font used for label text. + /// (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled + /// label's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// label's label. (e.g. '0xFFFF3300') + /// @param shadowColor [opt] hexstring - color of button's + /// label's shadow. (e.g. '0xFF000000') + /// @param focusedColor [opt] hexstring - color of focused + /// button's label. (e.g. '0xFF00FFFF') + /// @param label2 [opt] string or unicode - text string. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.label.setLabel('Status') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel(...); +#else + virtual void setLabel(const String& label = emptyString, + const char* font = NULL, + const char* textColor = NULL, + const char* disabledColor = NULL, + const char* shadowColor = NULL, + const char* focusedColor = NULL, + const String& label2 = emptyString); +#endif + +#ifndef SWIG + ControlLabel() = default; + + std::string strFont; + std::string strText; + UTILS::COLOR::Color textColor; + UTILS::COLOR::Color disabledColor; + uint32_t align; + bool bHasPath = false; + int iAngle = 0; + + CGUIControl* Create() override; + +#endif + }; + /// @} + + // ControlEdit class + /// \defgroup python_xbmcgui_control_edit Subclass - ControlEdit + /// \ingroup python_xbmcgui_control + /// @{ + /// **Used as an input control for the osd keyboard and other input fields.** + /// + /// \python_class{ ControlEdit(x, y, width, height, label[, font, textColor, + /// disabledColor, alignment, focusTexture, noFocusTexture]) } + /// + /// The edit control allows a user to input text in Kodi. You can choose the + /// font, size, colour, location and header of the text to be displayed. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param label string or unicode - text string. + /// @param font [opt] string - font used for label text. + /// (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled + /// label's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// label's label. (e.g. '0xFFFF3300') + /// @param alignment [opt] integer - alignment of label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// @param focusTexture [opt] string - filename for focus texture. + /// @param noFocusTexture [opt] string - filename for no focus texture. + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword.\n + /// After you create the control, you need to add it to the window with + /// addControl().\n + /// + /// + /// + ///------------------------------------------------------------------------- + /// @python_v18 Deprecated **isPassword** + /// @python_v19 Removed **isPassword** + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.edit = xbmcgui.ControlEdit(100, 250, 125, 75, 'Status') + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlEdit : public Control + { + public: + ControlEdit(long x, long y, long width, long height, const String& label, + const char* font = NULL, const char* textColor = NULL, + const char* disabledColor = NULL, + long _alignment = XBFONT_LEFT, const char* focusTexture = NULL, + const char* noFocusTexture = NULL); + + + // setLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_edit + /// @brief \python_func{ setLabel(label[, font, textColor, disabledColor, shadowColor, focusedColor, label2]) } + /// Sets text heading for this edit control. + /// + /// @param label string or unicode - text string. + /// @param font [opt] string - font used for label text. + /// (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled + /// label's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// label's label. (e.g. '0xFFFF3300') + /// @param shadowColor [opt] hexstring - color of button's + /// label's shadow. (e.g. '0xFF000000') + /// @param focusedColor [opt] hexstring - color of focused + /// button's label. (e.g. '0xFF00FFFF') + /// @param label2 [opt] string or unicode - text string. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.edit.setLabel('Status') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel(...); +#else + virtual void setLabel(const String& label = emptyString, + const char* font = NULL, + const char* textColor = NULL, + const char* disabledColor = NULL, + const char* shadowColor = NULL, + const char* focusedColor = NULL, + const String& label2 = emptyString); +#endif + + // getLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_edit + /// @brief \python_func{ getLabel() } + /// Returns the text heading for this edit control. + /// + /// @return Heading text + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// label = self.edit.getLabel() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel(); +#else + virtual String getLabel(); +#endif + + // setText() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_edit + /// @brief \python_func{ setText(value) } + /// Sets text value for this edit control. + /// + /// @param value string or unicode - text string. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.edit.setText('online') + /// ... + /// ~~~~~~~~~~~~~ + /// + setText(...); +#else + virtual void setText(const String& text); +#endif + + // getText() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_edit + /// @brief \python_func{ getText() } + /// Returns the text value for this edit control. + /// + /// @return Text value of control + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// value = self.edit.getText() + /// ... + /// ~~~~~~~~~~~~~ + /// + getText(); +#else + virtual String getText(); +#endif + +#ifndef SWIG + ControlEdit() = default; + + std::string strFont; + std::string strText; + std::string strTextureFocus; + std::string strTextureNoFocus; + UTILS::COLOR::Color textColor; + UTILS::COLOR::Color disabledColor; + uint32_t align; + + CGUIControl* Create() override; +#endif + + // setType() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_edit + /// @brief \python_func{ setType(type, heading) } + /// Sets the type of this edit control. + /// + /// @param type integer - type of the edit control. + /// | Param | Definition | + /// |-----------------------------------------------|:--------------------------------------------| + /// | xbmcgui.INPUT_TYPE_TEXT | (standard keyboard) + /// | xbmcgui.INPUT_TYPE_NUMBER | (format: #) + /// | xbmcgui.INPUT_TYPE_DATE | (format: DD/MM/YYYY) + /// | xbmcgui.INPUT_TYPE_TIME | (format: HH:MM) + /// | xbmcgui.INPUT_TYPE_IPADDRESS | (format: #.#.#.#) + /// | xbmcgui.INPUT_TYPE_PASSWORD | (input is masked) + /// | xbmcgui.INPUT_TYPE_PASSWORD_MD5 | (input is masked, return md5 hash of input) + /// | xbmcgui.INPUT_TYPE_SECONDS | (format: SS or MM:SS or HH:MM:SS or MM min) + /// | xbmcgui.INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW | (numeric input is masked) + /// @param heading string or unicode - heading that will be used for to numeric or + /// keyboard dialog when the edit control is clicked. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// @python_v19 New option added to mask numeric input. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.edit.setType(xbmcgui.INPUT_TYPE_TIME, 'Please enter the time') + /// ... + /// ~~~~~~~~~~~~~ + /// + setType(...); +#else + virtual void setType(int type, const String& heading); +#endif + }; + /// @} + + // ControlList class + /// \defgroup python_xbmcgui_control_list Subclass - ControlList + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used for a scrolling lists of items. Replaces the list control.** + /// + /// \python_class{ ControlList(x, y, width, height[, font, textColor, buttonTexture, buttonFocusTexture, + /// selectedColor, imageWidth, imageHeight, itemTextXOffset, itemTextYOffset, + /// itemHeight, space, alignmentY, shadowColor]) } + /// + /// The list container is one of several containers used to display items + /// from file lists in various ways. The list container is very + /// flexible - it's only restriction is that it is a list - i.e. a single + /// column or row of items. The layout of the items is very flexible and + /// is up to the skinner. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param font [opt] string - font used for items label. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of items label. (e.g. '0xFFFFFFFF') + /// @param buttonTexture [opt] string - filename for focus texture. + /// @param buttonFocusTexture [opt] string - filename for no focus texture. + /// @param selectedColor [opt] integer - x offset of label. + /// @param imageWidth [opt] integer - width of items icon or thumbnail. + /// @param imageHeight [opt] integer - height of items icon or thumbnail. + /// @param itemTextXOffset [opt] integer - x offset of items label. + /// @param itemTextYOffset [opt] integer - y offset of items label. + /// @param itemHeight [opt] integer - height of items. + /// @param space [opt] integer - space between items. + /// @param alignmentY [opt] integer - Y-axis alignment of items label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// @param shadowColor [opt] hexstring - color of items + /// label's shadow. (e.g. '0xFF000000') + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window + /// with addControl(). + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.cList = xbmcgui.ControlList(100, 250, 200, 250, 'font14', space=5) + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlList : public Control + { + void internAddListItem(const AddonClass::Ref<ListItem>& listitem, bool sendMessage); + + public: + ControlList(long x, long y, long width, long height, const char* font = NULL, + const char* textColor = NULL, const char* buttonTexture = NULL, + const char* buttonFocusTexture = NULL, + const char* selectedColor = NULL, + long _imageWidth=10, long _imageHeight=10, long _itemTextXOffset = CONTROL_TEXT_OFFSET_X, + long _itemTextYOffset = CONTROL_TEXT_OFFSET_Y, long _itemHeight = 27, long _space = 2, + long _alignmentY = XBFONT_CENTER_Y); + + ~ControlList() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ addItem(item) } + /// Add a new item to this list control. + /// + /// @param item string, unicode or ListItem - item to add. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.addItem('Reboot Kodi') + /// ... + /// ~~~~~~~~~~~~~ + /// + addItem(...); +#else + virtual void addItem(const Alternative<String, const XBMCAddon::xbmcgui::ListItem* > & item, bool sendMessage = true); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ addItems(items) } + /// Adds a list of listitems or strings to this list control. + /// + /// @param items List - list of strings, unicode objects or ListItems to add. + /// + /// @note You can use the above as keywords for arguments. + /// + /// Large lists benefit considerably, than using the standard addItem() + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.addItems(items=listitems) + /// ... + /// ~~~~~~~~~~~~~ + /// + addItems(...); +#else + virtual void addItems(const std::vector<Alternative<String, const XBMCAddon::xbmcgui::ListItem* > > & items); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ selectItem(item) } + /// Select an item by index number. + /// + /// @param item integer - index number of the item to select. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.selectItem(12) + /// ... + /// ~~~~~~~~~~~~~ + /// + selectItem(...); +#else + virtual void selectItem(long item); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ removeItem(index) } + /// Remove an item by index number. + /// + /// @param index integer - index number of the item to remove. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.removeItem(12) + /// ... + /// ~~~~~~~~~~~~~ + /// + removeItem(...); +#else + virtual void removeItem(int index); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ reset() } + /// Clear all ListItems in this control list. + /// + /// @warning Calling `reset()` will destroy any `ListItem` objects in the + /// `ControlList` if not hold by any other class. Make sure you + /// you don't call `addItems()` with the previous `ListItem` references + /// after calling `reset()`. If you need to preserve the `ListItem` objects after + /// `reset()` make sure you store them as members of your `WindowXML` class (see examples). + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Examples:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.reset() + /// ... + /// ~~~~~~~~~~~~~ + /// + /// The below example shows you how you can reset the `ControlList` but this time avoiding `ListItem` object + /// destruction. The example assumes `self` as a `WindowXMLDialog` instance containing a `ControlList` + /// with id = 800. The class preserves the `ListItem` objects in a class member variable. + /// + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # Get all the ListItem objects in the control + /// self.list_control = self.getControl(800) # ControlList object + /// self.listitems = [self.list_control.getListItem(item) for item in range(0, self.list_control.size())] + /// # Reset the ControlList control + /// self.list_control.reset() + /// # + /// # do something with your ListItem objects here (e.g. sorting.) + /// # ... + /// # + /// # Add them again to the ControlList + /// self.list_control.addItems(self.listitems) + /// ... + /// ~~~~~~~~~~~~~ + /// + reset(); +#else + virtual void reset(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getSpinControl() } + /// Returns the associated ControlSpin object. + /// + /// @warning Not working completely yet\n + /// After adding this control list to a window it is not possible to change + /// the settings of this spin control. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// ctl = cList.getSpinControl() + /// ... + /// ~~~~~~~~~~~~~ + /// + getSpinControl(); +#else + virtual Control* getSpinControl(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getSelectedPosition() } + /// Returns the position of the selected item as an integer. + /// + /// @note Returns -1 for empty lists. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// pos = cList.getSelectedPosition() + /// ... + /// ~~~~~~~~~~~~~ + /// + getSelectedPosition(); +#else + virtual long getSelectedPosition(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getSelectedItem() } + /// Returns the selected item as a ListItem object. + /// + /// @return The selected item + /// + /// + /// @note Same as getSelectedPosition(), but instead of an integer a ListItem object + /// is returned. Returns None for empty lists.\n + /// See windowexample.py on how to use this. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// item = cList.getSelectedItem() + /// ... + /// ~~~~~~~~~~~~~ + /// + getSelectedItem(); +#else + virtual XBMCAddon::xbmcgui::ListItem* getSelectedItem(); +#endif + + // setImageDimensions() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ setImageDimensions(imageWidth, imageHeight) } + /// Sets the width/height of items icon or thumbnail. + /// + /// @param imageWidth [opt] integer - width of items icon or thumbnail. + /// @param imageHeight [opt] integer - height of items icon or thumbnail. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.setImageDimensions(18, 18) + /// ... + /// ~~~~~~~~~~~~~ + /// + setImageDimensions(...); +#else + virtual void setImageDimensions(long imageWidth,long imageHeight); +#endif + + // setItemHeight() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @brief \python_func{ setItemHeight(itemHeight) } + /// Sets the height of items. + /// + /// @param itemHeight integer - height of items. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.setItemHeight(25) + /// ... + /// ~~~~~~~~~~~~~ + /// + setItemHeight(...); +#else + virtual void setItemHeight(long height); +#endif + + // setSpace() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ setSpace(space) } + /// Sets the space between items. + /// + /// @param space [opt] integer - space between items. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.setSpace(5) + /// ... + /// ~~~~~~~~~~~~~ + /// + setSpace(...); +#else + virtual void setSpace(int space); +#endif + + // setPageControlVisible() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ setPageControlVisible(visible) } + /// Sets the spin control's visible/hidden state. + /// + /// @param visible boolean - True=visible / False=hidden. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.setPageControlVisible(True) + /// ... + /// ~~~~~~~~~~~~~ + /// + setPageControlVisible(...); +#else + virtual void setPageControlVisible(bool visible); +#endif + + // size() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ size() } + /// Returns the total number of items in this list control as an integer. + /// + /// @return Total number of items + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cnt = cList.size() + /// ... + /// ~~~~~~~~~~~~~ + /// + size(); +#else + virtual long size(); +#endif + + // getItemHeight() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getItemHeight() } + /// Returns the control's current item height as an integer. + /// + /// @return Current item heigh + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// item_height = self.cList.getItemHeight() + /// ... + /// ~~~~~~~~~~~~~ + /// + getItemHeight(); +#else + virtual long getItemHeight(); +#endif + + // getSpace() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getSpace() } + /// Returns the control's space between items as an integer. + /// + /// @return Space between items + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// gap = self.cList.getSpace() + /// ... + /// ~~~~~~~~~~~~~ + /// + getSpace(); +#else + virtual long getSpace(); +#endif + + // getListItem() method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ getListItem(index) } + /// Returns a given ListItem in this List. + /// + /// @param index integer - index number of item to return. + /// @return List item + /// @throw ValueError if index is out of range. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem = cList.getListItem(6) + /// ... + /// ~~~~~~~~~~~~~ + /// + getListItem(...); +#else + virtual XBMCAddon::xbmcgui::ListItem* getListItem(int index); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_list + /// @brief \python_func{ setStaticContent(items) } + /// Fills a static list with a list of listitems. + /// + /// @param items List - list of listitems to add. + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// cList.setStaticContent(items=listitems) + /// ... + /// ~~~~~~~~~~~~~ + /// + setStaticContent(...); +#else + virtual void setStaticContent(const ListItemList* items); +#endif + +#ifndef SWIG + void sendLabelBind(int tail); + + bool canAcceptMessages(int actionId) override + { return ((actionId == ACTION_SELECT_ITEM) | (actionId == ACTION_MOUSE_LEFT_CLICK)); } + + // This is called from AddonWindow.cpp but shouldn't be available + // to the scripting languages. + ControlList() = default; + + std::vector<AddonClass::Ref<ListItem> > vecItems; + std::string strFont; + AddonClass::Ref<ControlSpin> pControlSpin; + + UTILS::COLOR::Color textColor; + UTILS::COLOR::Color selectedColor; + std::string strTextureButton; + std::string strTextureButtonFocus; + + int imageHeight = 0; + int imageWidth = 0; + int itemHeight = 0; + int space = 0; + + int itemTextOffsetX = 0; + int itemTextOffsetY = 0; + uint32_t alignmentY; + + CGUIControl* Create() override; +#endif + }; + /// @} + + // ControlFadeLabel class + /// + /// \defgroup python_xbmcgui_control_fadelabel Subclass - ControlFadeLabel + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to show multiple pieces of text in the same position, by + /// fading from one to the other.** + /// + /// \python_class{ ControlFadeLabel(x, y, width, height[, font, textColor, alignment]) } + /// + /// The fade label control is used for displaying multiple pieces of text + /// in the same space in Kodi. You can choose the font, size, colour, + /// location and contents of the text to be displayed. The first piece of + /// information to display fades in over 50 frames, then scrolls off to + /// the left. Once it is finished scrolling off screen, the second piece + /// of information fades in and the process repeats. A fade label control + /// is not supported in a list container. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param font [opt] string - font used for label text. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of fadelabel's labels. (e.g. '0xFFFFFFFF') + /// @param alignment [opt] integer - alignment of label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window + /// with addControl(). + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.fadelabel = xbmcgui.ControlFadeLabel(100, 250, 200, 50, textColor='0xFFFFFFFF') + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlFadeLabel : public Control + { + public: + ControlFadeLabel(long x, long y, long width, long height, + const char* font = NULL, + const char* textColor = NULL, + long _alignment = XBFONT_LEFT); + + // addLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcgui_control_fadelabel + /// @brief \python_func{ addLabel(label) } + /// Add a label to this control for scrolling. + /// + /// @param label string or unicode - text string to add. + /// + /// @note To remove added text use `reset()` for them. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.fadelabel.addLabel('This is a line of text that can scroll.') + /// ... + /// ~~~~~~~~~~~~~ + /// + addLabel(...); +#else + virtual void addLabel(const String& label); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_fadelabel + /// @brief \python_func{ setScrolling(scroll) } + /// Set scrolling. If set to false, the labels won't scroll. + /// Defaults to true. + /// + /// @param scroll boolean - True = enabled / False = disabled + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.fadelabel.setScrolling(False) + /// ... + /// ~~~~~~~~~~~~~ + /// + setScrolling(...); +#else + virtual void setScrolling(bool scroll); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_label + /// @brief \python_func{ reset() } + /// Clear this fade label. + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.fadelabel.reset() + /// ... + /// ~~~~~~~~~~~~~ + /// + reset(); +#else + virtual void reset(); +#endif + +#ifndef SWIG + std::string strFont; + UTILS::COLOR::Color textColor; + std::vector<std::string> vecLabels; + uint32_t align; + + CGUIControl* Create() override; + + ControlFadeLabel() = default; +#endif + }; + /// @} + + // ControlTextBox class + /// + /// \defgroup python_xbmcgui_control_textbox Subclass - ControlTextBox + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to show a multi-page piece of text.** + /// + /// \python_class{ ControlTextBox(x, y, width, height[, font, textColor]) } + /// + /// The text box is used for showing a large multipage piece of text in Kodi. + /// You can choose the position, size, and look of the text. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param font [opt] string - font used for text. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of textbox's text. (e.g. '0xFFFFFFFF') + /// + /// @note You can use the above as keywords for arguments and skip certain optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword.\n + /// After you create the control, you need to add it to the window with addControl(). + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # ControlTextBox(x, y, width, height[, font, textColor]) + /// self.textbox = xbmcgui.ControlTextBox(100, 250, 300, 300, textColor='0xFFFFFFFF') + /// ... + /// ~~~~~~~~~~~~~ + /// + /// As stated above, the GUI control is only created once added to a window. The example + /// below shows how a ControlTextBox can be created, added to the current window and + /// have some of its properties changed. + /// + /// **Extended example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// textbox = xbmcgui.ControlTextBox(100, 250, 300, 300, textColor='0xFFFFFFFF') + /// window = xbmcgui.Window(xbmcgui.getCurrentWindowId()) + /// window.addControl(textbox) + /// textbox.setText("My Text Box") + /// textbox.scroll() + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlTextBox : public Control + { + public: + ControlTextBox(long x, long y, long width, long height, + const char* font = NULL, + const char* textColor = NULL); + + // SetText() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_textbox + /// @brief \python_func{ setText(text) } + /// Sets the text for this textbox. + /// \anchor python_xbmcgui_control_textbox_settext + /// + /// @param text string - text string. + /// + ///----------------------------------------------------------------------- + /// + /// @python_v19 setText can now be used before adding the control to the window (the defined + /// value is taken into consideration when the control is created) + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setText(text) + /// self.textbox.setText('This is a line of text that can wrap.') + /// ... + /// ~~~~~~~~~~~~~ + /// + setText(...); +#else + virtual void setText(const String& text); +#endif + + // getText() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_textbox + /// @brief \python_func{ getText() } + /// Returns the text value for this textbox. + /// + /// @return To get text from box + /// + ///----------------------------------------------------------------------- + /// + /// @python_v19 getText() can now be used before adding the control to the window + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getText() + /// text = self.text.getText() + /// ... + /// ~~~~~~~~~~~~~ + /// + getText(); +#else + virtual String getText(); +#endif + + // reset() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_textbox + /// @brief \python_func{ reset() } + /// Clear's this textbox. + /// + ///----------------------------------------------------------------------- + /// @python_v19 reset() will reset any text defined for this control even before you add the control to the window + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # reset() + /// self.textbox.reset() + /// ... + /// ~~~~~~~~~~~~~ + /// + reset(); +#else + virtual void reset(); +#endif + + // scroll() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_textbox + /// @brief \python_func{ scroll(id) } + /// Scrolls to the given position. + /// + /// @param id integer - position to scroll to. + /// + /// @note scroll() only works after the control is added to a window. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # scroll(position) + /// self.textbox.scroll(10) + /// ... + /// ~~~~~~~~~~~~~ + /// + scroll(...); +#else + virtual void scroll(long id); +#endif + + // autoScroll() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_textbox + /// @brief \python_func{ autoScroll(delay, time, repeat) } + /// Set autoscrolling times. + /// + /// @param delay integer - Scroll delay (in ms) + /// @param time integer - Scroll time (in ms) + /// @param repeat integer - Repeat time + /// + /// @note autoScroll only works after you add the control to a window. + /// + ///----------------------------------------------------------------------- + /// + /// @python_v15 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.textbox.autoScroll(1, 2, 1) + /// ... + /// ~~~~~~~~~~~~~ + /// + autoScroll(...); +#else + virtual void autoScroll(int delay, int time, int repeat); +#endif + +#ifndef SWIG + std::string strFont; + UTILS::COLOR::Color textColor; + + CGUIControl* Create() override; + + ControlTextBox() = default; +#endif + }; + /// @} + + // ControlImage class + /// + /// \defgroup python_xbmcgui_control_image Subclass - ControlImage + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to show an image.** + /// + /// \python_class{ ControlImage(x, y, width, height, filename[, aspectRatio, colorDiffuse]) } + /// + /// The image control is used for displaying images in Kodi. You can choose + /// the position, size, transparency and contents of the image to be + /// displayed. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param filename string - image filename. + /// @param aspectRatio [opt] integer - (values 0 = stretch + /// (default), 1 = scale up (crops), + /// 2 = scale down (black bar) + /// @param colorDiffuse hexString - (example, '0xC0FF0000' (red tint)) + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window with + /// addControl(). + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # ControlImage(x, y, width, height, filename[, aspectRatio, colorDiffuse]) + /// self.image = xbmcgui.ControlImage(100, 250, 125, 75, aspectRatio=2) + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlImage : public Control + { + public: + ControlImage(long x, long y, long width, long height, + const char* filename, long aspectRatio = 0, + const char* colorDiffuse = NULL); + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_image + /// @brief \python_func{ setImage(filename[, useCache]) } + /// Changes the image. + /// + /// @param filename string - image filename. + /// @param useCache [opt] bool - True=use cache (default) / + /// False=don't use cache. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 Added new option **useCache**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setImage(filename[, useCache]) + /// self.image.setImage('special://home/scripts/test.png') + /// self.image.setImage('special://home/scripts/test.png', False) + /// ... + /// ~~~~~~~~~~~~~ + /// + setImage(...); +#else + virtual void setImage(const char* imageFilename, const bool useCache = true); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_image + /// @brief \python_func{ setColorDiffuse(colorDiffuse) } + /// Changes the images color. + /// + /// @param colorDiffuse hexString - (example, '0xC0FF0000' + /// (red tint)) + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setColorDiffuse(colorDiffuse) + /// self.image.setColorDiffuse('0xC0FF0000') + /// ... + /// ~~~~~~~~~~~~~ + /// + setColorDiffuse(...); +#else + virtual void setColorDiffuse(const char* hexString); +#endif + +#ifndef SWIG + ControlImage() = default; + + std::string strFileName; + int aspectRatio = 0; + UTILS::COLOR::Color colorDiffuse; + + CGUIControl* Create() override; +#endif + }; + /// @} + + // ControlImage class + /// + /// \defgroup python_xbmcgui_control_progress Subclass - ControlProgress + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to show the progress of a particular operation.** + /// + /// \python_class{ ControlProgress(x, y, width, height, filename[, texturebg, textureleft, texturemid, textureright, textureoverlay]) } + /// + /// The progress control is used to show the progress of an item that may + /// take a long time, or to show how far through a movie you are. You can + /// choose the position, size, and look of the progress control. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param filename string - image filename. + /// @param texturebg [opt] string - specifies the image file + /// whichshould be displayed in the + /// background of the progress control. + /// @param textureleft [opt] string - specifies the image file + /// whichshould be displayed for the left + /// side of the progress bar. This is + /// rendered on the left side of the bar. + /// @param texturemid [opt] string - specifies the image file + /// which should be displayed for the middl + /// portion of the progress bar. This is + /// the `fill` texture used to fill up the + /// bar. It's positioned on the right of + /// the `<lefttexture>` texture, and fills + /// the gap between the `<lefttexture>` and + /// `<righttexture>` textures, depending on + /// how far progressed the item is. + /// @param textureright [opt] string - specifies the image file + /// which should be displayed for the right + /// side of the progress bar. This is + /// rendered on the right side of the bar. + /// @param textureoverlay [opt] string - specifies the image file + /// which should be displayed over the top of + /// all other images in the progress bar. It + /// is centered vertically and horizontally + /// within the space taken up by the + /// background image. + /// + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window + /// with addControl(). + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # ControlProgress(x, y, width, height, filename[, texturebg, textureleft, texturemid, textureright, textureoverlay]) + /// self.image = xbmcgui.ControlProgress(100, 250, 250, 30, 'special://home/scripts/test.png') + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlProgress : public Control + { + public: + ControlProgress(long x, long y, long width, long height, + const char* texturebg = NULL, + const char* textureleft = NULL, + const char* texturemid = NULL, + const char* textureright = NULL, + const char* textureoverlay = NULL); + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_progress + /// @brief \python_func{ setPercent(percent) } + /// Sets the percentage of the progressbar to show. + /// + /// @param percent float - percentage of the bar to show. + /// + /// + /// @note valid range for percent is 0-100 + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setPercent(percent) + /// self.progress.setPercent(60) + /// ... + /// ~~~~~~~~~~~~~ + /// + setPercent(...); +#else + virtual void setPercent(float pct); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_progress + /// @brief \python_func{ getPercent() } + /// Returns a float of the percent of the progress. + /// + /// @return Percent position + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getPercent() + /// print(self.progress.getPercent()) + /// ... + /// ~~~~~~~~~~~~~ + /// + getPercent(); +#else + virtual float getPercent(); +#endif + +#ifndef SWIG + std::string strTextureLeft; + std::string strTextureMid; + std::string strTextureRight; + std::string strTextureBg; + std::string strTextureOverlay; + int aspectRatio = 0; + UTILS::COLOR::Color colorDiffuse; + + CGUIControl* Create() override; + ControlProgress() = default; +#endif + }; + /// @} + + // ControlButton class + /// + /// \defgroup python_xbmcgui_control_button Subclass - ControlButton + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief <b>A standard push button control.</b> + /// + /// \python_class{ ControlButton(x, y, width, height, label[, focusTexture, noFocusTexture, textOffsetX, textOffsetY, + /// alignment, font, textColor, disabledColor, angle, shadowColor, focusedColor]) } + /// + /// The button control is used for creating push buttons in Kodi. You can + /// choose the position, size, and look of the button, as well as choosing + /// what action(s) should be performed when pushed. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param label string or unicode - text string. + /// @param focusTexture [opt] string - filename for focus + /// texture. + /// @param noFocusTexture [opt] string - filename for no focus + /// texture. + /// @param textOffsetX [opt] integer - x offset of label. + /// @param textOffsetY [opt] integer - y offset of label. + /// @param alignment [opt] integer - alignment of label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// @param font [opt] string - font used for label text. + /// (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled + /// button's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// button's label. (e.g. '0xFFFF3300') + /// @param angle [opt] integer - angle of control. + /// (+ rotates CCW, - rotates CW) + /// @param shadowColor [opt] hexstring - color of button's + /// label's shadow. (e.g. '0xFF000000') + /// @param focusedColor [opt] hexstring - color of focused + /// button's label. (e.g. '0xFF00FFFF') + /// + /// @note You can use the above as keywords for arguments and skip + /// certain optional arguments.\n + /// Once you use a keyword, all following arguments require + /// the keyword.\n + /// After you create the control, you need to add it to the + /// window with addControl(). + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # ControlButton(x, y, width, height, label[, focusTexture, noFocusTexture, textOffsetX, textOffsetY, + /// # alignment, font, textColor, disabledColor, angle, shadowColor, focusedColor]) + /// self.button = xbmcgui.ControlButton(100, 250, 200, 50, 'Status', font='font14') + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlButton : public Control + { + public: + ControlButton(long x, long y, long width, long height, const String& label, + const char* focusTexture = NULL, const char* noFocusTexture = NULL, + long textOffsetX = CONTROL_TEXT_OFFSET_X, + long textOffsetY = CONTROL_TEXT_OFFSET_Y, + long alignment = (XBFONT_LEFT | XBFONT_CENTER_Y), + const char* font = NULL, const char* textColor = NULL, + const char* disabledColor = NULL, long angle = 0, + const char* shadowColor = NULL, const char* focusedColor = NULL); + + // setLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_button + /// @brief \python_func{ setLabel([label, font, textColor, disabledColor, shadowColor, focusedColor, label2]) } + /// Sets this buttons text attributes. + /// + /// @param label [opt] string or unicode - text string. + /// @param font [opt] string - font used for label text. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled button's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled button's label. (e.g. '0xFFFF3300') + /// @param shadowColor [opt] hexstring - color of button's label's shadow. (e.g. '0xFF000000') + /// @param focusedColor [opt] hexstring - color of focused button's label. (e.g. '0xFFFFFF00') + /// @param label2 [opt] string or unicode - text string. + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setLabel([label, font, textColor, disabledColor, shadowColor, focusedColor]) + /// self.button.setLabel('Status', 'font14', '0xFFFFFFFF', '0xFFFF3300', '0xFF000000') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel(...); +#else + virtual void setLabel(const String& label = emptyString, + const char* font = NULL, + const char* textColor = NULL, + const char* disabledColor = NULL, + const char* shadowColor = NULL, + const char* focusedColor = NULL, + const String& label2 = emptyString); +#endif + + // setDisabledColor() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_button + /// @brief \python_func{ setDisabledColor(disabledColor) } + /// Sets this buttons disabled color. + /// + /// @param disabledColor hexstring - color of disabled button's label. (e.g. '0xFFFF3300') + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setDisabledColor(disabledColor) + /// self.button.setDisabledColor('0xFFFF3300') + /// ... + /// ~~~~~~~~~~~~~ + /// + setDisabledColor(...); +#else + virtual void setDisabledColor(const char* color); +#endif + + // getLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_button + /// @brief \python_func{ getLabel() } + /// Returns the buttons label as a unicode string. + /// + /// @return Unicode string + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getLabel() + /// label = self.button.getLabel() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel(); +#else + virtual String getLabel(); +#endif + + // getLabel2() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_button + /// @brief \python_func{ getLabel2() } + /// Returns the buttons label2 as a string. + /// + /// @return string of label 2 + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getLabel2() + /// label = self.button.getLabel2() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel2(); +#else + virtual String getLabel2(); +#endif + +#ifndef SWIG + bool canAcceptMessages(int actionId) override { return true; } + + int textOffsetX = 0; + int textOffsetY = 0; + UTILS::COLOR::Color align; + std::string strFont; + UTILS::COLOR::Color textColor; + UTILS::COLOR::Color disabledColor; + int iAngle = 0; + int shadowColor = 0; + int focusedColor = 0; + std::string strText; + std::string strText2; + std::string strTextureFocus; + std::string strTextureNoFocus; + + CGUIControl* Create() override; + + ControlButton() = default; +#endif + }; + /// @} + + // ControlGroup class + /// + /// \defgroup python_xbmcgui_control_group Subclass - ControlGroup + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used to group controls together..** + /// + /// \python_class{ ControlGroup(x, y, width, height) } + /// + /// The group control is one of the most important controls. It allows you + /// to group controls together, applying attributes to all of them at once. + /// It also remembers the last navigated button in the group, so you can set + /// the <b>`<onup>`</b> of a control to a group of controls to have it always + /// go back to the one you were at before. It also allows you to position + /// controls more accurately relative to each other, as any controls within + /// a group take their coordinates from the group's top left corner (or from + /// elsewhere if you use the <b>"r"</b> attribute). You can have as many + /// groups as you like within the skin, and groups within groups are handled + /// with no issues. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.group = xbmcgui.ControlGroup(100, 250, 125, 75) + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlGroup : public Control + { + public: + ControlGroup(long x, long y, long width, long height); + +#ifndef SWIG + CGUIControl* Create() override; + + inline ControlGroup() = default; +#endif + }; + /// @} + + // ControlRadioButton class + /// + /// \defgroup python_xbmcgui_control_radiobutton Subclass - ControlRadioButton + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **A radio button control (as used for on/off settings).** + /// + /// \python_class{ ControlRadioButton(x, y, width, height, label[, focusOnTexture, noFocusOnTexture, + /// focusOffTexture, noFocusOffTexture, focusTexture, noFocusTexture, + /// textOffsetX, textOffsetY, alignment, font, textColor, disabledColor]) } + /// + /// The radio button control is used for creating push button on/off + /// settings in Kodi. You can choose the position, size, and look of the + /// button, as well as the focused and unfocused radio textures. Used + /// for settings controls. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control. + /// @param y integer - y coordinate of control. + /// @param width integer - width of control. + /// @param height integer - height of control. + /// @param label string or unicode - text string. + /// @param focusOnTexture [opt] string - filename for radio ON + /// focused texture. + /// @param noFocusOnTexture [opt] string - filename for radio ON not + /// focused texture. + /// @param focusOfTexture [opt] string - filename for radio OFF + /// focused texture. + /// @param noFocusOffTexture [opt] string - filename for radio OFF + /// not focused texture. + /// @param focusTexture [opt] string - filename for focused button + /// texture. + /// @param noFocusTexture [opt] string - filename for not focused button + /// texture. + /// @param textOffsetX [opt] integer - horizontal text offset + /// @param textOffsetY [opt] integer - vertical text offset + /// @param alignment [opt] integer - alignment of label + /// - \ref kodi_gui_font_alignment "Flags for alignment" used as bits to have several together: + /// | Definition name | Bitflag | Description | + /// |-------------------|:----------:|:------------------------------------| + /// | XBFONT_LEFT | 0x00000000 | Align X left + /// | XBFONT_RIGHT | 0x00000001 | Align X right + /// | XBFONT_CENTER_X | 0x00000002 | Align X center + /// | XBFONT_CENTER_Y | 0x00000004 | Align Y center + /// | XBFONT_TRUNCATED | 0x00000008 | Truncated text + /// | XBFONT_JUSTIFIED | 0x00000010 | Justify text + /// @param font [opt] string - font used for label text. + /// (e.g. 'font13') + /// @param textColor [opt] hexstring - color of label when control + /// is enabled. + /// radiobutton's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of label when control + /// is disabled. (e.g. '0xFFFF3300') + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window with + /// addControl(). + /// + /// + ///-------------------------------------------------------------------------- + /// @python_v13 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.radiobutton = xbmcgui.ControlRadioButton(100, 250, 200, 50, 'Enable', font='font14') + /// ... + /// ~~~~~~~~~~~~~ + /// + class ControlRadioButton : public Control + { + public: + ControlRadioButton(long x, long y, long width, long height, const String& label, + const char* focusOnTexture = NULL, const char* noFocusOnTexture = NULL, + const char* focusOffTexture = NULL, const char* noFocusOffTexture = NULL, + const char* focusTexture = NULL, const char* noFocusTexture = NULL, + long textOffsetX = CONTROL_TEXT_OFFSET_X, + long textOffsetY = CONTROL_TEXT_OFFSET_Y, + long _alignment = (XBFONT_LEFT | XBFONT_CENTER_Y), + const char* font = NULL, const char* textColor = NULL, + const char* disabledColor = NULL, long angle = 0, + const char* shadowColor = NULL, const char* focusedColor = NULL, + const char* disabledOnTexture = NULL, const char* disabledOffTexture = NULL); + + // setSelected() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_radiobutton + /// @brief \python_func{ setSelected(selected) } + /// **Sets the radio buttons's selected status.** + /// + /// @param selected bool - True=selected (on) / False=not + /// selected (off) + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.radiobutton.setSelected(True) + /// ... + /// ~~~~~~~~~~~~~ + /// + setSelected(...); +#else + virtual void setSelected(bool selected); +#endif + + // isSelected() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_radiobutton + /// @brief \python_func{ isSelected() } + /// Returns the radio buttons's selected status. + /// + /// @return True if selected on + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// is = self.radiobutton.isSelected() + /// ... + /// ~~~~~~~~~~~~~ + /// + isSelected(); +#else + virtual bool isSelected(); +#endif + + // setLabel() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_radiobutton + /// @brief \python_func{ setLabel(label[, font, textColor, disabledColor, shadowColor, focusedColor]) } + /// Sets the radio buttons text attributes. + /// + /// @param label string or unicode - text string. + /// @param font [opt] string - font used for label + /// text. (e.g. 'font13') + /// @param textColor [opt] hexstring - color of enabled radio + /// button's label. (e.g. '0xFFFFFFFF') + /// @param disabledColor [opt] hexstring - color of disabled + /// radio button's label. (e.g. '0xFFFF3300') + /// @param shadowColor [opt] hexstring - color of radio + /// button's label's shadow. + /// (e.g. '0xFF000000') + /// @param focusedColor [opt] hexstring - color of focused radio + /// button's label. (e.g. '0xFFFFFF00') + /// + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setLabel(label[, font, textColor, disabledColor, shadowColor, focusedColor]) + /// self.radiobutton.setLabel('Status', 'font14', '0xFFFFFFFF', '0xFFFF3300', '0xFF000000') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel(...); +#else + virtual void setLabel(const String& label = emptyString, + const char* font = NULL, + const char* textColor = NULL, + const char* disabledColor = NULL, + const char* shadowColor = NULL, + const char* focusedColor = NULL, + const String& label2 = emptyString); +#endif + + // setRadioDimension() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_radiobutton + /// @brief \python_func{ setRadioDimension(x, y, width, height) } + /// Sets the radio buttons's radio texture's position and size. + /// + /// @param x integer - x coordinate of radio texture. + /// @param y integer - y coordinate of radio texture. + /// @param width integer - width of radio texture. + /// @param height integer - height of radio texture. + /// + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.radiobutton.setRadioDimension(x=100, y=5, width=20, height=20) + /// ... + /// ~~~~~~~~~~~~~ + /// + setRadioDimension(...); +#else + virtual void setRadioDimension(long x, long y, long width, long height); +#endif + +#ifndef SWIG + bool canAcceptMessages(int actionId) override { return true; } + + std::string strFont; + std::string strText; + std::string strTextureFocus; + std::string strTextureNoFocus; + std::string strTextureRadioOnFocus; + std::string strTextureRadioOnNoFocus; + std::string strTextureRadioOffFocus; + std::string strTextureRadioOffNoFocus; + std::string strTextureRadioOnDisabled; + std::string strTextureRadioOffDisabled; + UTILS::COLOR::Color textColor; + UTILS::COLOR::Color disabledColor; + int textOffsetX = 0; + int textOffsetY = 0; + uint32_t align; + int iAngle = 0; + UTILS::COLOR::Color shadowColor; + UTILS::COLOR::Color focusedColor; + + CGUIControl* Create() override; + + ControlRadioButton() = default; +#endif + }; + /// @} + + /// \defgroup python_xbmcgui_control_slider Subclass - ControlSlider + /// \ingroup python_xbmcgui_control + /// @{ + /// @brief **Used for a volume slider.** + /// + /// \python_class{ ControlSlider(x, y, width, height[, textureback, texture, texturefocus, orientation]) } + /// + /// The slider control is used for things where a sliding bar best represents + /// the operation at hand (such as a volume control or seek control). You can + /// choose the position, size, and look of the slider control. + /// + /// @note This class include also all calls from \ref python_xbmcgui_control "Control" + /// + /// @param x integer - x coordinate of control + /// @param y integer - y coordinate of control + /// @param width integer - width of control + /// @param height integer - height of control + /// @param textureback [opt] string - image filename + /// @param texture [opt] string - image filename + /// @param texturefocus [opt] string - image filename + /// @param orientation [opt] integer - orientation of slider (xbmcgui.HORIZONTAL / xbmcgui.VERTICAL (default)) + /// + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword.\n + /// After you create the control, you need to add it to the window + /// with addControl(). + /// + /// + ///-------------------------------------------------------------------------- + /// @python_v17 **orientation** option added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.slider = xbmcgui.ControlSlider(100, 250, 350, 40) + /// ... + /// ~~~~~~~~~~~~~ + class ControlSlider : public Control + { + public: + ControlSlider(long x, long y, long width, long height, + const char* textureback = NULL, + const char* texture = NULL, + const char* texturefocus = NULL, int orientation = 1); + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ getPercent() } + /// Returns a float of the percent of the slider. + /// + /// @return float - Percent of slider + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// print(self.slider.getPercent()) + /// ... + /// ~~~~~~~~~~~~~ + /// + getPercent(); +#else + virtual float getPercent(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ setPercent(pct) } + /// Sets the percent of the slider. + /// + /// @param pct float - Percent value of slider + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.slider.setPercent(50) + /// ... + /// ~~~~~~~~~~~~~ + /// + setPercent(...); +#else + virtual void setPercent(float pct); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ getInt() } + /// Returns the value of the slider. + /// + /// @return int - value of slider + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// print(self.slider.getInt()) + /// ... + /// ~~~~~~~~~~~~~ + /// + getInt(); +#else + virtual int getInt(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ setInt(value, min, delta, max) } + /// Sets the range, value and step size of the slider. + /// + /// @param value int - value of slider + /// @param min int - min of slider + /// @param delta int - step size of slider + /// @param max int - max of slider + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.slider.setInt(450, 200, 10, 900) + /// ... + /// ~~~~~~~~~~~~~ + /// + setInt(...); +#else + virtual void setInt(int value, int min, int delta, int max); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ getFloat() } + /// Returns the value of the slider. + /// + /// @return float - value of slider + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// print(self.slider.getFloat()) + /// ... + /// ~~~~~~~~~~~~~ + /// + getFloat(); +#else + virtual float getFloat(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_control_slider + /// @brief \python_func{ setFloat(value, min, delta, max) } + /// Sets the range, value and step size of the slider. + /// + /// @param value float - value of slider + /// @param min float - min of slider + /// @param delta float - step size of slider + /// @param max float - max of slider + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// self.slider.setFloat(15.0, 10.0, 1.0, 20.0) + /// ... + /// ~~~~~~~~~~~~~ + /// + setFloat(...); +#else + virtual void setFloat(float value, float min, float delta, float max); +#endif + +#ifndef SWIG + std::string strTextureBack; + std::string strTexture; + std::string strTextureFoc; + int iOrientation; + + CGUIControl* Create() override; + + inline ControlSlider() = default; +#endif + }; + /// @} + } +} diff --git a/xbmc/interfaces/legacy/Dialog.cpp b/xbmc/interfaces/legacy/Dialog.cpp new file mode 100644 index 0000000..6dd00f1 --- /dev/null +++ b/xbmc/interfaces/legacy/Dialog.cpp @@ -0,0 +1,622 @@ + /* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ +#include "Dialog.h" + +#include "LanguageHook.h" +#include "ListItem.h" +#include "ModuleXbmcgui.h" +#include "ServiceBroker.h" +#include "WindowException.h" +#include "dialogs/GUIDialogColorPicker.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIWindowManager.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "music/dialogs/GUIDialogMusicInfo.h" +#include "settings/MediaSourceSettings.h" +#include "storage/MediaManager.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" +#include "utils/XTimeUtils.h" +#include "utils/log.h" +#include "video/dialogs/GUIDialogVideoInfo.h" + + using namespace KODI::MESSAGING; + +#define ACTIVE_WINDOW CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() + +namespace XBMCAddon +{ + namespace xbmcgui + { + Dialog::~Dialog() = default; + + bool Dialog::yesno(const String& heading, + const String& message, + const String& nolabel, + const String& yeslabel, + int autoclose, + int defaultbutton) + { + return yesNoCustomInternal(heading, message, nolabel, yeslabel, emptyString, autoclose, + defaultbutton) == 1; + } + + int Dialog::yesnocustom(const String& heading, + const String& message, + const String& customlabel, + const String& nolabel, + const String& yeslabel, + int autoclose, + int defaultbutton) + { + return yesNoCustomInternal(heading, message, nolabel, yeslabel, customlabel, autoclose, + defaultbutton); + } + + int Dialog::yesNoCustomInternal(const String& heading, + const String& message, + const String& nolabel, + const String& yeslabel, + const String& customlabel, + int autoclose, + int defaultbutton) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogYesNo* pDialog = + CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>( + WINDOW_DIALOG_YES_NO); + if (pDialog == nullptr) + throw WindowException("Error: Window is null"); + + return pDialog->ShowAndGetInput(CVariant{heading}, CVariant{message}, CVariant{nolabel}, + CVariant{yeslabel}, CVariant{customlabel}, autoclose, + defaultbutton); + } + + bool Dialog::info(const ListItem* item) + { + DelayedCallGuard dcguard(languageHook); + const AddonClass::Ref<xbmcgui::ListItem> listitem(item); + if (listitem->item->HasVideoInfoTag()) + { + CGUIDialogVideoInfo::ShowFor(*listitem->item); + return true; + } + else if (listitem->item->HasMusicInfoTag()) + { + CGUIDialogMusicInfo::ShowFor(listitem->item.get()); + return true; + } + return false; + } + + int Dialog::contextmenu(const std::vector<String>& list) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogContextMenu* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogContextMenu>(WINDOW_DIALOG_CONTEXT_MENU); + if (pDialog == NULL) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + + CContextButtons choices; + for(unsigned int i = 0; i < list.size(); i++) + { + choices.Add(i, list[i]); + } + return pDialog->Show(choices); + } + + + int Dialog::select(const String& heading, const std::vector<Alternative<String, const ListItem* > > & list, int autoclose, int preselect, bool useDetails) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (pDialog == NULL) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + + pDialog->Reset(); + if (!heading.empty()) + pDialog->SetHeading(CVariant{heading}); + for (const auto& item : list) + { + AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later()); + CFileItemPtr& fileItem = ritem->item; + pDialog->Add(*fileItem); + } + if (preselect > -1) + pDialog->SetSelected(preselect); + if (autoclose > 0) + pDialog->SetAutoClose(autoclose); + pDialog->SetUseDetails(useDetails); + pDialog->Open(); + + return pDialog->GetSelectedItem(); + } + + + std::unique_ptr<std::vector<int>> Dialog::multiselect(const String& heading, + const std::vector<Alternative<String, const ListItem* > > & options, int autoclose, const std::vector<int>& preselect, bool useDetails) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (pDialog == nullptr) + throw WindowException("Error: Window is NULL"); + + pDialog->Reset(); + pDialog->SetMultiSelection(true); + pDialog->SetHeading(CVariant{heading}); + + for (const auto& item : options) + { + AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later()); + CFileItemPtr& fileItem = ritem->item; + pDialog->Add(*fileItem); + } + if (autoclose > 0) + pDialog->SetAutoClose(autoclose); + pDialog->SetUseDetails(useDetails); + pDialog->SetSelected(preselect); + pDialog->Open(); + + if (pDialog->IsConfirmed()) + return std::unique_ptr<std::vector<int>>(new std::vector<int>(pDialog->GetSelectedItems())); + else + return std::unique_ptr<std::vector<int>>(); + } + + bool Dialog::ok(const String& heading, const String& message) + { + DelayedCallGuard dcguard(languageHook); + return HELPERS::ShowOKDialogText(CVariant{heading}, CVariant{message}); + } + + void Dialog::textviewer(const String& heading, const String& text, bool usemono) + { + DelayedCallGuard dcguard(languageHook); + + CGUIDialogTextViewer* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogTextViewer>(WINDOW_DIALOG_TEXT_VIEWER); + if (pDialog == NULL) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + if (!heading.empty()) + pDialog->SetHeading(heading); + if (!text.empty()) + pDialog->SetText(text); + pDialog->UseMonoFont(usemono); + pDialog->Open(); + } + + + Alternative<String, std::vector<String> > Dialog::browse(int type, const String& heading, + const String& s_shares, const String& maskparam, bool useThumbs, + bool useFileDirectories, const String& defaultt, + bool enableMultiple) + { + Alternative<String, std::vector<String> > ret; + if (enableMultiple) + ret.later() = browseMultiple(type,heading,s_shares,maskparam,useThumbs,useFileDirectories,defaultt); + else + ret.former() = browseSingle(type,heading,s_shares,maskparam,useThumbs,useFileDirectories,defaultt); + return ret; + } + + String Dialog::browseSingle(int type, const String& heading, const String& s_shares, + const String& maskparam, bool useThumbs, + bool useFileDirectories, + const String& defaultt ) + { + DelayedCallGuard dcguard(languageHook); + std::string value; + std::string mask = maskparam; + VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(s_shares); + + VECSOURCES localShares; + if (!shares) + { + CServiceBroker::GetMediaManager().GetLocalDrives(localShares); + if (StringUtils::CompareNoCase(s_shares, "local") != 0) + CServiceBroker::GetMediaManager().GetNetworkLocations(localShares); + } + else // always append local drives + { + localShares = *shares; + CServiceBroker::GetMediaManager().GetLocalDrives(localShares); + } + + if (useFileDirectories && !maskparam.empty()) + mask += "|.rar|.zip"; + + value = defaultt; + if (type == 1) + CGUIDialogFileBrowser::ShowAndGetFile(localShares, mask, heading, value, useThumbs, useFileDirectories); + else if (type == 2) + CGUIDialogFileBrowser::ShowAndGetImage(localShares, heading, value); + else + CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, heading, value, type != 0); + return value; + } + + std::vector<String> Dialog::browseMultiple(int type, const String& heading, const String& s_shares, + const String& mask, bool useThumbs, + bool useFileDirectories, const String& defaultt ) + { + DelayedCallGuard dcguard(languageHook); + VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(s_shares); + std::vector<String> valuelist; + String lmask = mask; + + VECSOURCES localShares; + if (!shares) + { + CServiceBroker::GetMediaManager().GetLocalDrives(localShares); + if (StringUtils::CompareNoCase(s_shares, "local") != 0) + CServiceBroker::GetMediaManager().GetNetworkLocations(localShares); + } + else // always append local drives + { + localShares = *shares; + CServiceBroker::GetMediaManager().GetLocalDrives(localShares); + } + + if (useFileDirectories && !lmask.empty()) + lmask += "|.rar|.zip"; + + if (type == 1) + CGUIDialogFileBrowser::ShowAndGetFileList(localShares, lmask, heading, valuelist, useThumbs, useFileDirectories); + else if (type == 2) + CGUIDialogFileBrowser::ShowAndGetImageList(localShares, heading, valuelist); + else + throw WindowException("Error: Cannot retrieve multiple directories using browse %s is NULL.",s_shares.c_str()); + + return valuelist; + } + + String Dialog::numeric(int inputtype, const String& heading, const String& defaultt, bool bHiddenInput) + { + DelayedCallGuard dcguard(languageHook); + std::string value; + KODI::TIME::SystemTime timedate; + KODI::TIME::GetLocalTime(&timedate); + + if (!heading.empty()) + { + if (inputtype == 1) + { + if (!defaultt.empty() && defaultt.size() == 10) + { + const std::string& sDefault = defaultt; + timedate.day = atoi(sDefault.substr(0, 2).c_str()); + timedate.month = atoi(sDefault.substr(3, 4).c_str()); + timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) + value = + StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, timedate.year); + else + return emptyString; + } + else if (inputtype == 2) + { + if (!defaultt.empty() && defaultt.size() == 5) + { + const std::string& sDefault = defaultt; + timedate.hour = atoi(sDefault.substr(0, 2).c_str()); + timedate.minute = atoi(sDefault.substr(3, 2).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) + value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); + else + return emptyString; + } + else if (inputtype == 3) + { + value = defaultt; + if (!CGUIDialogNumeric::ShowAndGetIPAddress(value, heading)) + return emptyString; + } + else if (inputtype == 4) + { + value = defaultt; + if (!CGUIDialogNumeric::ShowAndVerifyNewPassword(value)) + return emptyString; + } + else + { + value = defaultt; + if (!CGUIDialogNumeric::ShowAndGetNumber(value, heading, 0, bHiddenInput)) + return emptyString; + } + } + return value; + } + + void Dialog::notification(const String& heading, const String& message, const String& icon, int time, bool sound) + { + DelayedCallGuard dcguard(languageHook); + + std::string strIcon = getNOTIFICATION_INFO(); + int iTime = TOAST_DISPLAY_TIME; + + if (time > 0) + iTime = time; + if (!icon.empty()) + strIcon = icon; + + if (strIcon == getNOTIFICATION_INFO()) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, heading, message, iTime, sound); + else if (strIcon == getNOTIFICATION_WARNING()) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, heading, message, iTime, sound); + else if (strIcon == getNOTIFICATION_ERROR()) + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, heading, message, iTime, sound); + else + CGUIDialogKaiToast::QueueNotification(strIcon, heading, message, iTime, sound); + } + + String Dialog::input(const String& heading, const String& defaultt, int type, int option, int autoclose) + { + DelayedCallGuard dcguard(languageHook); + std::string value(defaultt); + KODI::TIME::SystemTime timedate; + KODI::TIME::GetLocalTime(&timedate); + + switch (type) + { + case INPUT_ALPHANUM: + { + bool bHiddenInput = (option & ALPHANUM_HIDE_INPUT) == ALPHANUM_HIDE_INPUT; + if (!CGUIKeyboardFactory::ShowAndGetInput(value, CVariant{heading}, true, bHiddenInput, autoclose)) + value = emptyString; + } + break; + case INPUT_NUMERIC: + { + if (!CGUIDialogNumeric::ShowAndGetNumber(value, heading, autoclose)) + value = emptyString; + } + break; + case INPUT_DATE: + { + if (!defaultt.empty() && defaultt.size() == 10) + { + const std::string& sDefault = defaultt; + timedate.day = atoi(sDefault.substr(0, 2).c_str()); + timedate.month = atoi(sDefault.substr(3, 4).c_str()); + timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) + value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, + timedate.year); + else + value = emptyString; + } + break; + case INPUT_TIME: + { + if (!defaultt.empty() && defaultt.size() == 5) + { + const std::string& sDefault = defaultt; + timedate.hour = atoi(sDefault.substr(0, 2).c_str()); + timedate.minute = atoi(sDefault.substr(3, 2).c_str()); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) + value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); + else + value = emptyString; + } + break; + case INPUT_IPADDRESS: + { + if (!CGUIDialogNumeric::ShowAndGetIPAddress(value, heading)) + value = emptyString; + } + break; + case INPUT_PASSWORD: + { + bool bResult = false; + + if (option & PASSWORD_VERIFY) + bResult = CGUIKeyboardFactory::ShowAndVerifyPassword(value, heading, 0, autoclose) == 0 ? true : false; + else + bResult = CGUIKeyboardFactory::ShowAndVerifyNewPassword(value, heading, true, autoclose); + + if (!bResult) + value = emptyString; + } + break; + default: + value = emptyString; + break; + } + + return value; + } + + String Dialog::colorpicker(const String& heading, + const String& selectedcolor, + const String& colorfile, + const std::vector<const ListItem*>& colorlist) + { + DelayedCallGuard dcguard(languageHook); + std::string value = emptyString; + CGUIDialogColorPicker* pDialog = + CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogColorPicker>( + WINDOW_DIALOG_COLOR_PICKER); + if (pDialog == nullptr) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + + pDialog->Reset(); + if (!heading.empty()) + pDialog->SetHeading(CVariant{heading}); + + if (!colorlist.empty()) + { + CFileItemList items; + for (const auto& coloritem : colorlist) + { + items.Add(coloritem->item); + } + pDialog->SetItems(items); + } + else if (!colorfile.empty()) + pDialog->LoadColors(colorfile); + else + pDialog->LoadColors(); + + if (!selectedcolor.empty()) + pDialog->SetSelectedColor(selectedcolor); + + pDialog->Open(); + + if (pDialog->IsConfirmed()) + value = pDialog->GetSelectedColor(); + return value; + } + + DialogProgress::~DialogProgress() { XBMC_TRACE; deallocating(); } + + void DialogProgress::deallocating() + { + XBMC_TRACE; + + if (dlg && open) + { + DelayedCallGuard dg; + dlg->Close(); + } + } + + void DialogProgress::create(const String& heading, const String& message) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogProgress* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); + + if (pDialog == NULL) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + + dlg = pDialog; + open = true; + + pDialog->SetHeading(CVariant{heading}); + + if (!message.empty()) + pDialog->SetText(CVariant{message}); + + pDialog->Open(); + } + + void DialogProgress::update(int percent, const String& message) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogProgress* pDialog = dlg; + + if (pDialog == NULL) + throw WindowException("Dialog not created."); + + if (percent >= 0 && percent <= 100) + { + pDialog->SetPercentage(percent); + pDialog->ShowProgressBar(true); + } + else + { + pDialog->ShowProgressBar(false); + } + + if (!message.empty()) + pDialog->SetText(CVariant{message}); + } + + void DialogProgress::close() + { + DelayedCallGuard dcguard(languageHook); + if (dlg == NULL) + throw WindowException("Dialog not created."); + dlg->Close(); + open = false; + } + + bool DialogProgress::iscanceled() + { + if (dlg == NULL) + throw WindowException("Dialog not created."); + return dlg->IsCanceled(); + } + + DialogProgressBG::~DialogProgressBG() { XBMC_TRACE; deallocating(); } + + void DialogProgressBG::deallocating() + { + XBMC_TRACE; + + if (dlg && open) + { + DelayedCallGuard dg; + dlg->Close(); + } + } + + void DialogProgressBG::create(const String& heading, const String& message) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogExtendedProgressBar* pDialog = + CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogExtendedProgressBar>(WINDOW_DIALOG_EXT_PROGRESS); + + if (pDialog == NULL) + throw WindowException("Error: Window is NULL, this is not possible :-)"); + + CGUIDialogProgressBarHandle* pHandle = pDialog->GetHandle(heading); + + dlg = pDialog; + handle = pHandle; + open = true; + + pHandle->SetTitle(heading); + if (!message.empty()) + pHandle->SetText(message); + } + + void DialogProgressBG::update(int percent, const String& heading, const String& message) + { + DelayedCallGuard dcguard(languageHook); + CGUIDialogProgressBarHandle* pHandle = handle; + + if (pHandle == NULL) + throw WindowException("Dialog not created."); + + if (percent >= 0 && percent <= 100) + pHandle->SetPercentage((float)percent); + if (!heading.empty()) + pHandle->SetTitle(heading); + if (!message.empty()) + pHandle->SetText(message); + } + + void DialogProgressBG::close() + { + DelayedCallGuard dcguard(languageHook); + if (handle == NULL) + throw WindowException("Dialog not created."); + handle->MarkFinished(); + open = false; + } + + bool DialogProgressBG::isFinished() + { + if (handle == NULL) + throw WindowException("Dialog not created."); + return handle->IsFinished(); + } + + } +} diff --git a/xbmc/interfaces/legacy/Dialog.h b/xbmc/interfaces/legacy/Dialog.h new file mode 100644 index 0000000..9faa733 --- /dev/null +++ b/xbmc/interfaces/legacy/Dialog.h @@ -0,0 +1,965 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "AddonString.h" +#include "Alternative.h" +#include "ListItem.h" +#include "dialogs/GUIDialogBoxBase.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogProgress.h" +#include "swighelper.h" + +#include <string> +#include <vector> + +namespace XBMCAddon +{ +namespace xbmcgui +{ +constexpr int INPUT_ALPHANUM{0}; +constexpr int INPUT_NUMERIC{1}; +constexpr int INPUT_DATE{2}; +constexpr int INPUT_TIME{3}; +constexpr int INPUT_IPADDRESS{4}; +constexpr int INPUT_PASSWORD{5}; + +constexpr int PASSWORD_VERIFY{1}; +constexpr int ALPHANUM_HIDE_INPUT{2}; + + /// + /// \defgroup python_Dialog Dialog + /// \ingroup python_xbmcgui + /// @{ + /// @brief **Kodi's dialog class** + /// + /// The graphical control element dialog box (also called dialogue box or + /// just dialog) is a small window that communicates information to the user + /// and prompts them for a response. + /// + class Dialog : public AddonClass + { + public: + + inline Dialog() = default; + ~Dialog() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().yesno(heading, message, [nolabel, yeslabel, autoclose]) } + /// **Yes / no dialog** + /// + /// The Yes / No dialog can be used to inform the user about questions and + /// get the answer. + /// + /// @param heading string or unicode - dialog heading. + /// @param message string or unicode - message text. + /// @param nolabel [opt] label to put on the no button. + /// @param yeslabel [opt] label to put on the yes button. + /// @param autoclose [opt] integer - milliseconds to autoclose dialog. (default=do not autoclose) + /// @param defaultbutton [opt] integer - specifies the default focused button. + /// <em>(default=DLG_YESNO_NO_BTN)</em> + /// | Value: | Description: | + /// |------------------------------:|---------------------------------------------------| + /// | xbmcgui.DLG_YESNO_NO_BTN | Set the "No" button as default. + /// | xbmcgui.DLG_YESNO_YES_BTN | Set the "Yes" button as default. + /// | xbmcgui.DLG_YESNO_CUSTOM_BTN | Set the "Custom" button as default. + /// @return Returns True if 'Yes' was pressed, else False. + /// + /// + /// + ///------------------------------------------------------------------------ + /// @python_v13 Added new option **autoclose**. + /// @python_v19 Renamed option **line1** to **message**. + /// @python_v19 Removed option **line2**. + /// @python_v19 Removed option **line3**. + /// @python_v20 Added new option **defaultbutton**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.yesno('Kodi', 'Do you want to exit this script?') + /// .. + /// ~~~~~~~~~~~~~ + /// + yesno(...); +#else + bool yesno(const String& heading, + const String& message, + const String& nolabel = emptyString, + const String& yeslabel = emptyString, + int autoclose = 0, + int defaultbutton = CONTROL_NO_BUTTON); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().yesnocustom(heading, message, customlabel, [nolabel, yeslabel, autoclose]) } + /// **Yes / no / custom dialog** + /// + /// The YesNoCustom dialog can be used to inform the user about questions and + /// get the answer. The dialog provides a third button appart from yes and no. + /// Button labels are fully customizable. + /// + /// @param heading string or unicode - dialog heading. + /// @param message string or unicode - message text. + /// @param customlabel string or unicode - label to put on the custom button. + /// @param nolabel [opt] label to put on the no button. + /// @param yeslabel [opt] label to put on the yes button. + /// @param autoclose [opt] integer - milliseconds to autoclose dialog. (default=do not autoclose) + /// @param defaultbutton [opt] integer - specifies the default focused button. + /// <em>(default=DLG_YESNO_NO_BTN)</em> + /// | Value: | Description: | + /// |------------------------------:|---------------------------------------------------| + /// | xbmcgui.DLG_YESNO_NO_BTN | Set the "No" button as default. + /// | xbmcgui.DLG_YESNO_YES_BTN | Set the "Yes" button as default. + /// | xbmcgui.DLG_YESNO_CUSTOM_BTN | Set the "Custom" button as default. + /// @return Returns the integer value for the selected button (-1:cancelled, 0:no, 1:yes, 2:custom) + /// + /// + /// + ///------------------------------------------------------------------------ + /// @python_v19 New function added. + /// @python_v20 Added new option **defaultbutton**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.yesnocustom('Kodi', 'Question?', 'Maybe') + /// .. + /// ~~~~~~~~~~~~~ + /// + yesnocustom(...); +#else + int yesnocustom(const String& heading, + const String& message, + const String& customlabel, + const String& nolabel = emptyString, + const String& yeslabel = emptyString, + int autoclose = 0, + int defaultbutton = CONTROL_NO_BUTTON); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().info(listitem) } + /// **Info dialog** + /// + /// Show the corresponding info dialog for a given listitem + /// + /// @param listitem xbmcgui.ListItem - ListItem to show info for. + /// @return Returns whether the dialog opened successfully. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v17 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.info(listitem) + /// .. + /// ~~~~~~~~~~~~~ + /// + info(...); +#else + bool info(const ListItem* item); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().select(heading, list[, autoclose, preselect, useDetails]) } + /// **Select dialog** + /// + /// Show of a dialog to select of an entry as a key + /// + /// @param heading string or unicode - dialog heading. + /// @param list list of strings / xbmcgui.ListItems - list of items shown in dialog. + /// @param autoclose [opt] integer - milliseconds to autoclose dialog. (default=do not autoclose) + /// @param preselect [opt] integer - index of preselected item. (default=no preselected item) + /// @param useDetails [opt] bool - use detailed list instead of a compact list. (default=false) + /// @return Returns the position of the highlighted item as an integer. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v17 **preselect** option added. + /// @python_v17 Added new option **useDetails**. + /// @python_v17 Allow listitems for parameter **list** + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.select('Choose a playlist', ['Playlist #1', 'Playlist #2, 'Playlist #3']) + /// .. + /// ~~~~~~~~~~~~~ + /// + select(...); +#else + int select(const String& heading, const std::vector<Alternative<String, const ListItem* > > & list, int autoclose=0, int preselect=-1, bool useDetails=false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().contextmenu(list) } + /// Show a context menu. + /// + /// @param list string list - list of items. + /// @return the position of the highlighted item as an integer + /// (-1 if cancelled). + /// + /// + ///-------------------------------------------------------------------------- + /// @python_v17 New function added + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.contextmenu(['Option #1', 'Option #2', 'Option #3']) + /// .. + /// ~~~~~~~~~~~~~ + /// + contextmenu(...); +#else + int contextmenu(const std::vector<String>& list); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().multiselect(heading, options[, autoclose, preselect, useDetails]) } + /// Show a multi-select dialog. + /// + /// @param heading string or unicode - dialog heading. + /// @param options list of strings / xbmcgui.ListItems - options to choose from. + /// @param autoclose [opt] integer - milliseconds to autoclose dialog. + /// (default=do not autoclose) + /// @param preselect [opt] list of int - indexes of items to preselect + /// in list (default: do not preselect any item) + /// @param useDetails [opt] bool - use detailed list instead of a compact list. (default=false) + /// @return Returns the selected items as a list of indices, + /// or None if cancelled. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v16 New function added. + /// @python_v17 Added new option **preselect**. + /// @python_v17 Added new option **useDetails**. + /// @python_v17 Allow listitems for parameter **options** + /// + /// **Example:** + /// @code{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ret = dialog.multiselect("Choose something", ["Foo", "Bar", "Baz"], preselect=[1,2]) + /// .. + /// @endcode + /// + multiselect(...); +#else + std::unique_ptr<std::vector<int> > multiselect(const String& heading, const std::vector<Alternative<String, const ListItem* > > & options, int autoclose=0, const std::vector<int>& preselect = std::vector<int>(), bool useDetails=false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().ok(heading, message) } + /// **OK dialog** + /// + /// The functions permit the call of a dialog of information, a + /// confirmation of the user by press from OK required. + /// + /// @param heading string or unicode - dialog heading. + /// @param message string or unicode - message text. + /// @return Returns True if 'Ok' was pressed, else False. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v19 Renamed option **line1** to **message**. + /// @python_v19 Removed option **line2**. + /// @python_v19 Removed option **line3**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// ok = dialog.ok('Kodi', 'There was an error.') + /// .. + /// ~~~~~~~~~~~~~ + /// + ok(...); +#else + bool ok(const String& heading, const String& message); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().textviewer(heading, text, usemono) } + /// **TextViewer dialog** + /// + /// The text viewer dialog can be used to display descriptions, help texts + /// or other larger texts. + /// + /// @param heading string or unicode - dialog heading. + /// @param text string or unicode - text. + /// @param usemono [opt] bool - use monospace font + /// + /// + ///------------------------------------------------------------------------ + /// @python_v16 New function added. + /// @python_v18 New optional param added **usemono**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// dialog.textviewer('Plot', 'Some movie plot.') + /// .. + /// ~~~~~~~~~~~~~ + /// + textviewer(...); +#else + void textviewer(const String& heading, const String& text, bool usemono = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().browse(type, heading, shares[, mask, useThumbs, treatAsFolder, defaultt, enableMultiple]) } + /// **Browser dialog** + /// + /// The function offer the possibility to select a file by the user of + /// the add-on. + /// + /// It allows all the options that are possible in Kodi itself and offers + /// all support file types. + /// + /// @param type integer - the type of browse dialog. + /// | Param | Name | + /// |:-----:|:--------------------------------| + /// | 0 | ShowAndGetDirectory | + /// | 1 | ShowAndGetFile | + /// | 2 | ShowAndGetImage | + /// | 3 | ShowAndGetWriteableDirectory | + /// @param heading string or unicode - dialog heading. + /// @param shares string or unicode - from [sources.xml](http://kodi.wiki/view/Sources.xml) + /// | Param | Name | + /// |:--------------:|:---------------------------------------------| + /// | "programs" | list program addons + /// | "video" | list video sources + /// | "music" | list music sources + /// | "pictures" | list picture sources + /// | "files" | list file sources (added through filemanager) + /// | "games" | list game sources + /// | "local" | list local drives + /// | "" | list local drives and network shares + /// @param mask [opt] string or unicode - '|' separated file mask. (i.e. '.jpg|.png') + /// @param useThumbs [opt] boolean - if True autoswitch to Thumb view if files exist. + /// @param treatAsFolder [opt] boolean - if True playlists and archives act as folders. + /// @param defaultt [opt] string - default path or file. + /// @param enableMultiple [opt] boolean - if True multiple file selection is enabled. + /// + /// @return If enableMultiple is False (default): returns filename and/or path as a string + /// to the location of the highlighted item, if user pressed 'Ok' or a masked item + /// was selected. Returns the default value if dialog was canceled. + /// If enableMultiple is True: returns tuple of marked filenames as a string + /// if user pressed 'Ok' or a masked item was selected. Returns empty tuple if dialog was canceled.\n\n + /// If type is 0 or 3 the enableMultiple parameter is ignore + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New option added to browse network and/or local drives. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// fn = dialog.browse(3, 'Kodi', 'files', '', False, False, False, 'special://masterprofile/script_data/Kodi Lyrics') + /// .. + /// ~~~~~~~~~~~~~ + /// + browse(...); +#else + Alternative<String, std::vector<String> > browse(int type, const String& heading, const String& shares, + const String& mask = emptyString, bool useThumbs = false, + bool treatAsFolder = false, const String& defaultt = emptyString, + bool enableMultiple = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().browseSingle(type, heading, shares[, mask, useThumbs, treatAsFolder, defaultt]) } + /// **Browse single dialog** + /// + /// The function offer the possibility to select a file by the user of + /// the add-on. + /// + /// It allows all the options that are possible in Kodi itself and offers + /// all support file types. + /// + /// @param type integer - the type of browse dialog. + /// | Param | Name | + /// |:-----:|:--------------------------------| + /// | 0 | ShowAndGetDirectory + /// | 1 | ShowAndGetFile + /// | 2 | ShowAndGetImage + /// | 3 | ShowAndGetWriteableDirectory + /// @param heading string or unicode - dialog heading. + /// @param shares string or unicode - from [sources.xml](http://kodi.wiki/view/Sources.xml) + /// | Param | Name | + /// |:--------------:|:---------------------------------------------| + /// | "programs" | list program addons + /// | "video" | list video sources + /// | "music" | list music sources + /// | "pictures" | list picture sources + /// | "files" | list file sources (added through filemanager) + /// | "games" | list game sources + /// | "local" | list local drives + /// | "" | list local drives and network shares + /// @param mask [opt] string or unicode - '|' separated file mask. (i.e. '.jpg|.png') + /// @param useThumbs [opt] boolean - if True autoswitch to Thumb view if files exist (default=false). + /// @param treatAsFolder [opt] boolean - if True playlists and archives act as folders (default=false). + /// @param defaultt [opt] string - default path or file. + /// + /// @return Returns filename and/or path as a string to the location of the highlighted item, + /// if user pressed 'Ok' or a masked item was selected. + /// Returns the default value if dialog was canceled. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New option added to browse network and/or local drives. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// fn = dialog.browseSingle(3, 'Kodi', 'files', '', False, False, 'special://masterprofile/script_data/Kodi Lyrics') + /// .. + /// ~~~~~~~~~~~~~ + /// + browseSingle(...); +#else + String browseSingle(int type, const String& heading, const String& shares, + const String& mask = emptyString, bool useThumbs = false, + bool treatAsFolder = false, + const String& defaultt = emptyString ); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().browseMultiple(type, heading, shares[, mask, useThumbs, treatAsFolder, defaultt]) } + /// **Browser dialog** + /// + /// The function offer the possibility to select multiple files by the + /// user of the add-on. + /// + /// It allows all the options that are possible in Kodi itself and offers + /// all support file types. + /// + /// @param type integer - the type of browse dialog. + /// | Param | Name | + /// |:-----:|:--------------------------------| + /// | 1 | ShowAndGetFile + /// | 2 | ShowAndGetImage + /// @param heading string or unicode - dialog heading. + /// @param shares string or unicode - from [sources.xml](http://kodi.wiki/view/Sources.xml) + /// | Param | Name | + /// |:--------------:|:---------------------------------------------| + /// | "programs" | list program addons + /// | "video" | list video sources + /// | "music" | list music sources + /// | "pictures" | list picture sources + /// | "files" | list file sources (added through filemanager) + /// | "games" | list game sources + /// | "local" | list local drives + /// | "" | list local drives and network shares + /// @param mask [opt] string or unicode - '|' separated file mask. (i.e. '.jpg|.png') + /// @param useThumbs [opt] boolean - if True autoswitch to Thumb view if files exist (default=false). + /// @param treatAsFolder [opt] boolean - if True playlists and archives act as folders (default=false). + /// @param defaultt [opt] string - default path or file. + /// @return Returns tuple of marked filenames as a string," + /// if user pressed 'Ok' or a masked item was selected. Returns empty tuple if dialog was canceled. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New option added to browse network and/or local drives. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// fn = dialog.browseMultiple(2, 'Kodi', 'files', '', False, False, 'special://masterprofile/script_data/Kodi Lyrics') + /// .. + /// ~~~~~~~~~~~~~ + /// + browseMultiple(...); +#else + std::vector<String> browseMultiple(int type, const String& heading, const String& shares, + const String& mask = emptyString, bool useThumbs = false, + bool treatAsFolder = false, + const String& defaultt = emptyString ); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().numeric(type, heading[, defaultt, bHiddenInput]) } + /// **Numeric dialog** + /// + /// The function have to be permitted by the user for the representation + /// of a numeric keyboard around an input. + /// + /// @param type integer - the type of numeric dialog. + /// | Param | Name | Format | + /// |:-----:|:-------------------------|:-----------------------------| + /// | 0 | ShowAndGetNumber | (default format: #) + /// | 1 | ShowAndGetDate | (default format: DD/MM/YYYY) + /// | 2 | ShowAndGetTime | (default format: HH:MM) + /// | 3 | ShowAndGetIPAddress | (default format: #.#.#.#) + /// | 4 | ShowAndVerifyNewPassword | (default format: *) + /// @param heading string or unicode - dialog heading (will be ignored for type 4). + /// @param defaultt [opt] string - default value. + /// @param bHiddenInput [opt] bool - mask input (available for type 0). + /// @return Returns the entered data as a string. + /// Returns the default value if dialog was canceled. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v19 New option added ShowAndVerifyNewPassword. + /// @python_v19 Added new option **bHiddenInput**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// d = dialog.numeric(1, 'Enter date of birth') + /// .. + /// ~~~~~~~~~~~~~ + /// + numeric(...); +#else + String numeric(int type, const String& heading, const String& defaultt = emptyString, bool bHiddenInput = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().notification(heading, message[, icon, time, sound]) } + /// Show a Notification alert. + /// + /// @param heading string - dialog heading. + /// @param message string - dialog message. + /// @param icon [opt] string - icon to use. (default xbmcgui.NOTIFICATION_INFO) + /// @param time [opt] integer - time in milliseconds (default 5000) + /// @param sound [opt] bool - play notification sound (default True) + /// + /// Builtin Icons: + /// - xbmcgui.NOTIFICATION_INFO + /// - xbmcgui.NOTIFICATION_WARNING + /// - xbmcgui.NOTIFICATION_ERROR + /// + /// + ///------------------------------------------------------------------------ + /// @python_v13 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// dialog.notification('Movie Trailers', 'Finding Nemo download finished.', xbmcgui.NOTIFICATION_INFO, 5000) + /// .. + /// ~~~~~~~~~~~~~ + /// + notification(...); +#else + void notification(const String& heading, const String& message, const String& icon = emptyString, int time = 0, bool sound = true); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().input(heading[, defaultt, type, option, autoclose]) } + /// Show an Input dialog. + /// + /// @param heading string - dialog heading. + /// @param defaultt [opt] string - default value. (default=empty string) + /// @param type [opt] integer - the type of keyboard dialog. (default=xbmcgui.INPUT_ALPHANUM) + /// | Parameter | Format | + /// |---------------------------------:|:--------------------------------| + /// | <tt>xbmcgui.INPUT_ALPHANUM</tt> | (standard keyboard) + /// | <tt>xbmcgui.INPUT_NUMERIC</tt> | (format: #) + /// | <tt>xbmcgui.INPUT_DATE</tt> | (format: DD/MM/YYYY) + /// | <tt>xbmcgui.INPUT_TIME</tt> | (format: HH:MM) + /// | <tt>xbmcgui.INPUT_IPADDRESS</tt> | (format: #.#.#.#) + /// | <tt>xbmcgui.INPUT_PASSWORD</tt> | (return md5 hash of input, input is masked) + /// @param option [opt] integer - option for the dialog. (see Options below) + /// - Password dialog: + /// - <tt>xbmcgui.PASSWORD_VERIFY</tt> (verifies an existing (default) md5 hashed password) + /// - Alphanum dialog: + /// - <tt>xbmcgui.ALPHANUM_HIDE_INPUT</tt> (masks input) + /// @param autoclose [opt] integer - milliseconds to autoclose dialog. (default=do not autoclose) + /// + /// @return Returns the entered data as a string. + /// Returns an empty string if dialog was canceled. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v13 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.Dialog() + /// d = dialog.input('Enter secret code', type=xbmcgui.INPUT_ALPHANUM, option=xbmcgui.ALPHANUM_HIDE_INPUT) + /// .. + /// ~~~~~~~~~~~~~ + /// + input(...); +#else + String input(const String& heading, + const String& defaultt = emptyString, + int type = INPUT_ALPHANUM, + int option = 0, + int autoclose = 0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Dialog + /// \python_func{ xbmcgui.Dialog().colorpicker(heading[, colorfile, colorlist, selectedcolor]) } + /// Show a color selection dialog. + /// + /// @param heading string - dialog heading. + /// @param selectedcolor [opt] string - hex value of the preselected color. + /// @param colorfile [opt] string - xml file containing color definitions.\n + /// **XML content style:** + /// ~~~~~~xml + /// <colors> + /// <color name="white">ffffffff</color> + /// <color name="grey">7fffffff</color> + /// <color name="green">ff00ff7f</color> + /// </colors> + /// ~~~~~~ + /// @param colorlist [opt] xbmcgui.ListItems - where label defines the color name and label2 is set to the hex value. + /// + /// @return Returns the hex value of the selected color as a string. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # colorfile example + /// dialog = xbmcgui.Dialog() + /// value = dialog.colorpicker('Select color', 'ff00ff00', 'os.path.join(xbmcaddon.Addon().getAddonInfo("path"), "colors.xml")') + /// .. + /// # colorlist example + /// listitems = [] + /// l1 = xbmcgui.ListItem("red", "FFFF0000") + /// l2 = xbmcgui.ListItem("green", "FF00FF00") + /// l3 = xbmcgui.ListItem("blue", "FF0000FF") + /// listitems.append(l1) + /// listitems.append(l2) + /// listitems.append(l3) + /// dialog = xbmcgui.Dialog() + /// value = dialog.colorpicker("Select color", "FF0000FF", colorlist=listitems) + /// .. + /// ~~~~~~~~~~~~~ + /// + colorpicker(...); +#else + String colorpicker( + const String& heading, + const String& selectedcolor = emptyString, + const String& colorfile = emptyString, + const std::vector<const ListItem*>& colorlist = std::vector<const ListItem*>()); +#endif + + private: +#ifndef DOXYGEN_SHOULD_SKIP_THIS + // used by both yesno() and yesnocustom() + int yesNoCustomInternal(const String& heading, + const String& message, + const String& nolabel, + const String& yeslabel, + const String& customlabel, + int autoclose, + int defaultbutton); +#endif + }; + //@} + + /// + /// \defgroup python_DialogProgress DialogProgress + /// \ingroup python_xbmcgui + /// @{ + /// @brief <b>Kodi's progress dialog class (Duh!)</b> + /// + /// + class DialogProgress : public AddonClass + { + CGUIDialogProgress* dlg = nullptr; + bool open = false; + + protected: + void deallocating() override; + + public: + + DialogProgress() = default; + ~DialogProgress() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgress + /// \python_func{ xbmcgui.DialogProgress().create(heading[, message]) } + /// Create and show a progress dialog. + /// + /// @param heading string or unicode - dialog heading. + /// @param message [opt] string or unicode - message text. + /// + /// @note Use update() to update lines and progressbar. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v19 Renamed option **line1** to **message**. + /// @python_v19 Removed option **line2**. + /// @python_v19 Removed option **line3**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog = xbmcgui.DialogProgress() + /// pDialog.create('Kodi', 'Initializing script...') + /// .. + /// ~~~~~~~~~~~~~ + /// + create(...); +#else + void create(const String& heading, const String& message = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgress + /// \python_func{ xbmcgui.DialogProgress().update(percent[, message]) } + /// Updates the progress dialog. + /// + /// @param percent integer - percent complete. (0:100) + /// @param message [opt] string or unicode - message text. + /// + /// + /// + ///------------------------------------------------------------------------ + /// @python_v19 Renamed option **line1** to **message**. + /// @python_v19 Removed option **line2**. + /// @python_v19 Removed option **line3**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog.update(25, 'Importing modules...') + /// .. + /// ~~~~~~~~~~~~~ + /// + update(...); +#else + void update(int percent, const String& message = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgress + /// \python_func{ xbmcgui.DialogProgress().close() } + /// Close the progress dialog. + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + close(...); +#else + void close(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgress + /// \python_func{ xbmcgui.DialogProgress().iscanceled() } + /// Checks progress is canceled. + /// + /// @return True if the user pressed cancel. + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// if (pDialog.iscanceled()): return + /// .. + /// ~~~~~~~~~~~~~ + /// + iscanceled(...); +#else + bool iscanceled(); +#endif + }; + + //@} + + /// + /// \defgroup python_DialogProgressBG DialogProgressBG + /// \ingroup python_xbmcgui + /// @{ + /// @brief <b>Kodi's background progress dialog class</b> + /// + /// + class DialogProgressBG : public AddonClass + { + CGUIDialogExtendedProgressBar* dlg = nullptr; + CGUIDialogProgressBarHandle* handle = nullptr; + bool open = false; + + protected: + void deallocating() override; + + public: + + DialogProgressBG() = default; + ~DialogProgressBG() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgressBG + /// \python_func{ xbmcgui.DialogProgressBG().create(heading[, message]) } + /// Create and show a background progress dialog. + /// + /// @param heading string or unicode - dialog heading. + /// @param message [opt] string or unicode - message text. + /// + /// @note 'heading' is used for the dialog's id. Use a unique heading. + /// Use update() to update heading, message and progressbar. + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog = xbmcgui.DialogProgressBG() + /// pDialog.create('Movie Trailers', 'Downloading Monsters Inc... .') + /// .. + /// ~~~~~~~~~~~~~ + /// + create(...); +#else + void create(const String& heading, const String& message = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgressBG + /// \python_func{ xbmcgui.DialogProgressBG().update([percent, heading, message]) } + /// Updates the background progress dialog. + /// + /// @param percent [opt] integer - percent complete. (0:100) + /// @param heading [opt] string or unicode - dialog heading. + /// @param message [opt] string or unicode - message text. + /// + /// @note To clear heading or message, you must pass a blank character. + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog.update(25, message='Downloading Finding Nemo ...') + /// .. + /// ~~~~~~~~~~~~~ + /// + update(...); +#else + void update(int percent = 0, const String& heading = emptyString, const String& message = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgressBG + /// \python_func{ xbmcgui.DialogProgressBG().close() } + /// Close the background progress dialog + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pDialog.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + close(...); +#else + void close(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_DialogProgressBG + /// \python_func{ xbmcgui.DialogProgressBG().isFinished() } + /// Checks progress is finished + /// + /// @return True if the background dialog is active. + /// + /// + ///------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// if (pDialog.isFinished()): return + /// .. + /// ~~~~~~~~~~~~~ + /// + isFinished(...); +#else + bool isFinished(); +#endif + }; + //@} +#ifndef DOXYGEN_SHOULD_SKIP_THIS + SWIG_CONSTANT2(int, DLG_YESNO_NO_BTN, CONTROL_NO_BUTTON); + SWIG_CONSTANT2(int, DLG_YESNO_YES_BTN, CONTROL_YES_BUTTON); + SWIG_CONSTANT2(int, DLG_YESNO_CUSTOM_BTN, CONTROL_CUSTOM_BUTTON); +#endif +} // namespace xbmcgui +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/Dictionary.h b/xbmc/interfaces/legacy/Dictionary.h new file mode 100644 index 0000000..65a4db8 --- /dev/null +++ b/xbmc/interfaces/legacy/Dictionary.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonString.h" + +#include <map> + +namespace XBMCAddon +{ + // This is a hack in order to handle int's as strings. The correct fix for + // this is to get rid of Alternative all together and make the codegenerator + // finally handle overloading correctly. + typedef String StringOrInt; + + /** + * This is a bit of a hack for dynamically typed languages. In some + * cases python addon api calls handle dictionaries with variable + * value types. In this case we coerce all of these types into + * strings and then convert them back in the api. Yes, this is messy + * and maybe we should use the CVariant here. But for now the + * native api handles these calls by converting the string to the + * appropriate types. + */ + template<class T> class Dictionary : public std::map<String,T> {}; + + typedef Dictionary<StringOrInt> Properties; +} diff --git a/xbmc/interfaces/legacy/DrmCryptoSession.cpp b/xbmc/interfaces/legacy/DrmCryptoSession.cpp new file mode 100644 index 0000000..26f69be --- /dev/null +++ b/xbmc/interfaces/legacy/DrmCryptoSession.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "DrmCryptoSession.h" + +#include "media/drm/CryptoSession.h" + +using namespace XbmcCommons; + +namespace XBMCAddon +{ + namespace xbmcdrm + { + CryptoSession::CryptoSession(const String& UUID, + const String& cipherAlgorithm, + const String& macAlgorithm) + : m_cryptoSession(DRM::CCryptoSession::GetCryptoSession(UUID, cipherAlgorithm, macAlgorithm)) + { + } + + CryptoSession::~CryptoSession() + { + delete m_cryptoSession; + } + + Buffer CryptoSession::GetKeyRequest(const Buffer &init, const String &mimeType, bool offlineKey, const std::map<String, String> ¶meters) + { + if (m_cryptoSession) + return m_cryptoSession->GetKeyRequest(init, mimeType, offlineKey, parameters); + + return Buffer(); + } + + String CryptoSession::GetPropertyString(const String &name) + { + if (m_cryptoSession) + return m_cryptoSession->GetPropertyString(name); + + return ""; + } + + String CryptoSession::ProvideKeyResponse(const Buffer &response) + { + if (m_cryptoSession) + return m_cryptoSession->ProvideKeyResponse(response); + + return ""; + } + + void CryptoSession::RemoveKeys() + { + if (m_cryptoSession) + m_cryptoSession->RemoveKeys(); + } + + void CryptoSession::RestoreKeys(const String& keySetId) + { + if (m_cryptoSession) + m_cryptoSession->RestoreKeys(keySetId); + } + + void CryptoSession::SetPropertyString(const String &name, const String &value) + { + if (m_cryptoSession) + return m_cryptoSession->SetPropertyString(name, value); + } + + /*******************Crypto section *****************/ + + Buffer CryptoSession::Decrypt(const Buffer &cipherKeyId, const Buffer &input, const Buffer &iv) + { + if (m_cryptoSession) + return m_cryptoSession->Decrypt(cipherKeyId, input, iv); + + return Buffer(); + } + + Buffer CryptoSession::Encrypt(const Buffer &cipherKeyId, const Buffer &input, const Buffer &iv) + { + if (m_cryptoSession) + return m_cryptoSession->Encrypt(cipherKeyId, input, iv); + + return Buffer(); + } + + Buffer CryptoSession::Sign(const Buffer &macKeyId, const Buffer &message) + { + if (m_cryptoSession) + return m_cryptoSession->Sign(macKeyId, message); + + return Buffer(); + } + + bool CryptoSession::Verify(const Buffer &macKeyId, const Buffer &message, const Buffer &signature) + { + if (m_cryptoSession) + return m_cryptoSession->Verify(macKeyId, message, signature); + + return false; + } + + } //namespace xbmcdrm +} //namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/DrmCryptoSession.h b/xbmc/interfaces/legacy/DrmCryptoSession.h new file mode 100644 index 0000000..816bec8 --- /dev/null +++ b/xbmc/interfaces/legacy/DrmCryptoSession.h @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "Exception.h" +#include "commons/Buffer.h" + +#include <map> +#include <vector> + +namespace DRM +{ + class CCryptoSession; +} + +namespace XBMCAddon +{ + + typedef std::vector<char> charVec; + + namespace xbmcdrm + { + + XBMCCOMMONS_STANDARD_EXCEPTION(DRMException); + + // + /// \defgroup python_xbmcdrm Library - xbmcdrm + ///@{ + /// @brief **Kodi's %DRM class.** + /// + /// Offers classes and functions that allow a developer to work with + /// DRM-protected contents like Widevine. + /// + /// This type of functionality is closely related to the type of %DRM + /// used and the service to be implemented. + /// + /// Using the \ref xbmcdrm_CryptoSession "CryptoSession" constructor allow you + /// to have access to a %DRM session. + /// With a %DRM session you can read and write the %DRM properties + /// \ref xbmcdrm_GetPropertyString "GetPropertyString", + /// \ref xbmcdrm_SetPropertyString "SetPropertyString" + /// and establish session keys with + /// \ref xbmcdrm_GetKeyRequest "GetKeyRequest" and + /// \ref xbmcdrm_ProvideKeyResponse "ProvideKeyResponse", + /// or resume previous session keys with + /// \ref xbmcdrm_RestoreKeys "RestoreKeys". + /// + /// When the session keys are established you can use these methods + /// to perform various operations: + /// \ref xbmcdrm_Encrypt "Encrypt" / + /// \ref xbmcdrm_Decrypt "Decrypt" for data encryption / decryption, + /// \ref xbmcdrm_Sign "Sign" / + /// \ref xbmcdrm_Verify "Verify" for make or verify data-signature. + /// Useful for example to implement encrypted communication between + /// a client and the server. + /// + /// An example where such functionality is useful is the Message + /// Security Layer (MSL) transmission protocol used in some VOD applications. + /// This protocol (or rather framework) is used to increase the level of security + /// in the exchange of messages (such as licences, manifests or other data), + /// which defines a security extension / layer on top of the HTTP protocol. + /// + ///-------------------------------------------------------------------------- + /// Constructor for %DRM crypto session + /// + /// \anchor xbmcdrm_CryptoSession + /// \python_class{ xbmcdrm.CryptoSession(UUID, cipherAlgorithm, macAlgorithm) } + /// + /// @param UUID string - 16 byte UUID of the %DRM system to use + /// @param cipherAlgorithm string - Algorithm used for encryption / decryption ciphers + /// @param macAlgorithm string - Algorithm used for sign / verify + /// + /// @throws RuntimeException If the session can not be established + /// + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New class added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// uuid_widevine = 'edef8ba9-79d6-4ace-a3c8-27dcd51d21ed' + /// crypto_session = xbmcdrm.CryptoSession(uuid_widevine, 'AES/CBC/NoPadding', 'HmacSHA256') + /// .. + /// ~~~~~~~~~~~~~ + /// + class CryptoSession : public AddonClass + { + DRM::CCryptoSession* m_cryptoSession; + public: + CryptoSession(const String& UUID, const String& cipherAlgorithm, const String& macAlgorithm); + ~CryptoSession() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ GetKeyRequest(init, mimeType, offlineKey, optionalParameters) } + /// Generate a key request + /// + /// Generate a key request, used for request/response exchange between the app + /// and a license server to obtain or release keys used to decrypt encrypted content. + /// After the app has received the key request response from the license server, + /// it should deliver to the response to the %DRM instance using + /// the method \ref xbmcdrm_ProvideKeyResponse "ProvideKeyResponse", to activate the keys. + // \anchor xbmcdrm_GetKeyRequest + /// + /// @param init byte - Initialization bytes container-specific data, + /// its meaning is interpreted based on the mime type provided + /// in the mimeType parameter. It could contain, for example, + /// the content ID, key ID or other data required in generating + /// the key request. + /// @param mimeType string - Type of media which is exchanged + /// (e.g. "application/xml", "video/mp4") + /// @param offlineKey bool - Specifies the type of the request. + /// The request may be to acquire keys for Streaming or Offline content + /// @param optionalParameters [opt] map - Will be included in the key request message + /// to allow a client application to provide additional + /// message parameters to the server + /// + /// @return byte - The opaque key request data (challenge) which is send to key server + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 the init param must be a bytearray instead of byte. + /// + GetKeyRequest(...); +#else + XbmcCommons::Buffer GetKeyRequest(const XbmcCommons::Buffer &init, const String &mimeType, bool offlineKey, const std::map<String, String> &optionalParameters); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ GetPropertyString(name) } + /// Request a system specific property value of the %DRM system. + /// + ///\anchor xbmcdrm_GetPropertyString + /// @param Name string - Name of the property to query + /// + /// @return Value of the requested property + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + GetPropertyString(...); +#else + String GetPropertyString(const String &name); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ ProvideKeyResponse(response) } + /// Provide a key response + /// + /// \anchor xbmcdrm_ProvideKeyResponse + /// When a key response is received from the license server, + /// must be sent to the %DRM instance by using provideKeyResponse. + /// See also \ref xbmcdrm_GetKeyRequest "GetKeyRequest". + /// + /// @param response byte - Key data returned from the license server + /// + /// @return A keySetId if the response is for an offline key requests which + /// can be used later with restoreKeys, + /// else return empty for streaming key requests. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 the response argument must be a bytearray instead of byte. + /// + ProvideKeyResponse(...); +#else + String ProvideKeyResponse(const XbmcCommons::Buffer &response); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ RemoveKeys() } + /// Removes all keys currently loaded in a session. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + RemoveKeys(...); +#else + void RemoveKeys(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ RestoreKeys(keySetId) } + /// Restores session keys stored during previous + /// \ref xbmcdrm_ProvideKeyResponse "ProvideKeyResponse" call. + /// \anchor xbmcdrm_RestoreKeys + /// + /// @param keySetId string - Identifies the saved key set to restore. + /// This value must never be null. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + RestoreKeys(...); +#else + void RestoreKeys(const String& keySetId); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ SetPropertyString(name, value) } + /// Set a system specific property value in the %DRM system. + /// + /// \anchor xbmcdrm_SetPropertyString + /// + /// @param name string - Name of the property. This value must never be null. + /// @param value string - Value of the property to set. This value must never be null. + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + SetPropertyString(...); +#else + void SetPropertyString(const String &name, const String &value); +#endif + +/*******************Crypto section *****************/ + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ Decrypt(cipherKeyId, input, iv) } + /// Decrypt an encrypted data by using session keys. + /// + /// \anchor xbmcdrm_Decrypt + /// + /// @param cipherKeyId byte - Encryption key id (provided from a service handshake) + /// @param input byte - Cipher text to decrypt + /// @param iv byte - Initialization vector of cipher text + /// + /// @return Decrypted input data + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 all arguments need to be of type bytearray instead of byte. + /// + Decrypt(...); +#else + XbmcCommons::Buffer Decrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ Encrypt(cipherKeyId, input, iv) } + /// Encrypt data by using session keys. + /// + /// \anchor xbmcdrm_Encrypt + /// + /// @param cipherKeyId byte - Encryption key id (provided from a service handshake) + /// @param input byte - Encrypted text + /// @param iv byte - Initialization vector of encrypted text + /// + /// @return byte - Encrypted input data + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 all arguments need to be of type bytearray instead of byte. + /// + Encrypt(...); +#else + XbmcCommons::Buffer Encrypt(const XbmcCommons::Buffer &cipherKeyId, const XbmcCommons::Buffer &input, const XbmcCommons::Buffer &iv); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ Sign(macKeyId, message) } + /// Generate a %DRM encrypted signature for a text message. + /// + /// \anchor xbmcdrm_Sign + /// + /// @param macKeyId byte - HMAC key id (provided from a service handshake) + /// @param message byte - Message text on which to base the signature + /// + /// @return byte - Signature + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 all arguments need to be of type bytearray instead of byte. + /// + Sign(...); +#else + XbmcCommons::Buffer Sign(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcdrm + /// @brief \python_func{ Verify(macKeyId, message, signature) } + /// Verify the validity of a %DRM signature of a text message. + /// + /// \anchor xbmcdrm_Verify + /// + /// @param macKeyId byte - HMAC key id (provided from a service handshake) + /// @param message byte - Message text on which the signature is based + /// @param signature byte - The signature to verify + /// + /// @return true when the signature is valid + /// + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// @python_v19 With python 3 for all arguments is needed to pass bytearray instead of byte. + /// + Verify(...); +#else + bool Verify(const XbmcCommons::Buffer &macKeyId, const XbmcCommons::Buffer &message, const XbmcCommons::Buffer &signature); +#endif + + }; + ///@} + } +} diff --git a/xbmc/interfaces/legacy/Exception.h b/xbmc/interfaces/legacy/Exception.h new file mode 100644 index 0000000..61a32be --- /dev/null +++ b/xbmc/interfaces/legacy/Exception.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "commons/Exception.h" +#include "utils/log.h" + +#ifndef SWIG +namespace XBMCAddon +{ + XBMCCOMMONS_STANDARD_EXCEPTION(WrongTypeException); + + /** + * UnimplementedException Can be used in places like the + * Control hierarchy where the + * requirements of dynamic language usage force us to add + * unimplemented methods to a class hierarchy. See the + * detailed explanation on the class Control for more. + */ + class UnimplementedException : public XbmcCommons::Exception + { + public: + inline UnimplementedException(const UnimplementedException& other) = default; + inline UnimplementedException(const char* classname, const char* methodname) : + Exception("UnimplementedException") + { SetMessage("Unimplemented method: %s::%s(...)", classname, methodname); } + }; + + /** + * This is what callback exceptions from the scripting language + * are translated to. + */ + class UnhandledException : public XbmcCommons::Exception + { + public: + inline UnhandledException(const UnhandledException& other) = default; + inline UnhandledException(const char* _message,...) : Exception("UnhandledException") { XBMCCOMMONS_COPYVARARGS(_message); } + }; +} +#endif + +/** + * These macros allow the easy declaration (and definition) of parent + * class virtual methods that are not implemented until the child class. + * This is to support the idosyncracies of dynamically typed scripting + * languages. See the comment in AddonControl.h for more details. + */ +#define THROW_UNIMP(classname) throw UnimplementedException(classname, __FUNCTION__) + diff --git a/xbmc/interfaces/legacy/File.cpp b/xbmc/interfaces/legacy/File.cpp new file mode 100644 index 0000000..4c0ca68 --- /dev/null +++ b/xbmc/interfaces/legacy/File.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "File.h" + +namespace XBMCAddon +{ + namespace xbmcvfs + { + XbmcCommons::Buffer File::readBytes(unsigned long numBytes) + { + DelayedCallGuard dg(languageHook); + int64_t size = file->GetLength(); + if ((!numBytes || (((int64_t)numBytes) > size)) && (size >= 0)) + numBytes = (unsigned long) size; + + + XbmcCommons::Buffer ret(numBytes); + + if (numBytes == 0) + return ret; + + while(ret.remaining() > 0) + { + ssize_t bytesRead = file->Read(ret.curPosition(), ret.remaining()); + if (bytesRead <= 0) // we consider this a failure or a EOF, can't tell which, + { // return whatever we have already. + ret.flip(); + return ret; + } + ret.forward(bytesRead); + } + ret.flip(); + return ret; + } + + bool File::write(XbmcCommons::Buffer& buffer) + { + DelayedCallGuard dg(languageHook); + while (buffer.remaining() > 0) + { + ssize_t bytesWritten = file->Write( buffer.curPosition(), buffer.remaining()); + if (bytesWritten == 0) // this could be a failure (see HDFile, and XFileUtils) or + // it could mean something else when a negative number means an error + // (see CCurlFile). There is no consistency so we can only assume we're + // done when we get a 0. + return false; + else if (bytesWritten < 0) // But, if we get something less than zero, we KNOW it's an error. + return false; + buffer.forward(bytesWritten);// Otherwise, we advance the buffer by the amount written. + } + return true; + } + + } +} diff --git a/xbmc/interfaces/legacy/File.h b/xbmc/interfaces/legacy/File.h new file mode 100644 index 0000000..01a757a --- /dev/null +++ b/xbmc/interfaces/legacy/File.h @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "AddonString.h" +#include "LanguageHook.h" +#include "commons/Buffer.h" +#include "filesystem/File.h" + +#include <algorithm> + +namespace XBMCAddon +{ + + namespace xbmcvfs + { + + // + /// \defgroup python_file File + /// \ingroup python_xbmcvfs + /// @{ + /// @brief <b>Kodi's file class.</b> + /// + /// \python_class{ xbmcvfs.File(filepath, [mode]) } + /// + /// @param filepath string Selected file path + /// @param mode [opt] string Additional mode options (if no mode is supplied, the default is Open for Read). + /// | Mode | Description | + /// |:------:|:--------------------------------| + /// | w | Open for write | + /// + /// + ///-------------------------------------------------------------------------- + /// @python_v19 Added context manager support + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file, 'w') + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file, 'w') as f: + /// .. + /// .. + /// ~~~~~~~~~~~~~ + // + class File : public AddonClass + { + XFILE::CFile* file; + public: + inline File(const String& filepath, const char* mode = NULL) : file(new XFILE::CFile()) + { + DelayedCallGuard dg(languageHook); + if (mode && strncmp(mode, "w", 1) == 0) + file->OpenForWrite(filepath,true); + else + file->Open(filepath, XFILE::READ_NO_CACHE); + } + + inline ~File() override { delete file; } + +#if !defined(DOXYGEN_SHOULD_USE_THIS) + inline File* __enter__() { return this; } + inline void __exit__() { close(); } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ read([bytes]) } + /// Read file parts as string. + /// + /// @param bytes [opt] How many bytes to read - if not + /// set it will read the whole file + /// @return string + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// b = f.read() + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as file: + /// b = f.read() + /// .. + /// ~~~~~~~~~~~~~ + /// + read(...); +#else + inline String read(unsigned long numBytes = 0) + { + XbmcCommons::Buffer b = readBytes(numBytes); + return b.getString(numBytes == 0 ? b.remaining() : std::min((unsigned long)b.remaining(),numBytes)); + } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ readBytes(numbytes) } + /// Read bytes from file. + /// + /// @param numbytes How many bytes to read [opt]- if not set + /// it will read the whole file + /// @return bytearray + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// b = f.readBytes() + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as f: + /// b = f.readBytes() + /// .. + /// ~~~~~~~~~~~~~ + /// + readBytes(...); +#else + XbmcCommons::Buffer readBytes(unsigned long numBytes = 0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ write(buffer) } + /// To write given data in file. + /// + /// @param buffer Buffer to write to file + /// @return True on success. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file, 'w') + /// result = f.write(buffer) + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file, 'w') as f: + /// result = f.write(buffer) + /// .. + /// ~~~~~~~~~~~~~ + /// + write(...); +#else + bool write(XbmcCommons::Buffer& buffer); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ size() } + /// Get the file size. + /// + /// @return The file size + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// s = f.size() + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as f: + /// s = f.size() + /// .. + /// ~~~~~~~~~~~~~ + /// + size(); +#else + inline long long size() { DelayedCallGuard dg(languageHook); return file->GetLength(); } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ seek(seekBytes, iWhence) } + /// Seek to position in file. + /// + /// @param seekBytes position in the file + /// @param iWhence [opt] where in a file to seek from[0 beginning, + /// 1 current , 2 end position] + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 Function changed. param **iWhence** is now optional. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// result = f.seek(8129, 0) + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as f: + /// result = f.seek(8129, 0) + /// .. + /// ~~~~~~~~~~~~~ + /// + seek(...); +#else + inline long long seek(long long seekBytes, int iWhence = SEEK_SET) { DelayedCallGuard dg(languageHook); return file->Seek(seekBytes,iWhence); } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ tell() } + /// Get the current position in the file. + /// + /// @return The file position + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// s = f.tell() + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as f: + /// s = f.tell() + /// .. + /// ~~~~~~~~~~~~~ + /// + tell(); +#else + inline long long tell() { DelayedCallGuard dg(languageHook); return file->GetPosition(); } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_file + /// @brief \python_func{ close() } + /// Close opened file. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// f = xbmcvfs.File(file) + /// f.close() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// **Example (v19 and up):** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// with xbmcvfs.File(file) as f: + /// .. + /// .. + /// ~~~~~~~~~~~~~ + /// + close(); +#else + inline void close() { DelayedCallGuard dg(languageHook); file->Close(); } +#endif + +#ifndef SWIG + inline const XFILE::CFile* getFile() const { return file; } +#endif + + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/InfoTagGame.cpp b/xbmc/interfaces/legacy/InfoTagGame.cpp new file mode 100644 index 0000000..697247b --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagGame.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "InfoTagGame.h" + +#include "AddonUtils.h" +#include "games/tags/GameInfoTag.h" + +using namespace KODI::GAME; +using namespace XBMCAddonUtils; + +namespace XBMCAddon +{ +namespace xbmc +{ + +InfoTagGame::InfoTagGame(bool offscreen /* = false */) + : infoTag(new CGameInfoTag), offscreen(offscreen), owned(true) +{ +} + +InfoTagGame::InfoTagGame(const CGameInfoTag* tag) + : infoTag(new CGameInfoTag(*tag)), offscreen(true), owned(true) +{ +} + +InfoTagGame::InfoTagGame(CGameInfoTag* tag, bool offscreen /* = false */) + : infoTag(tag), offscreen(offscreen), owned(false) +{ +} + +InfoTagGame::~InfoTagGame() +{ + if (owned) + delete infoTag; +} + +String InfoTagGame::getTitle() +{ + return infoTag->GetTitle(); +} + +String InfoTagGame::getPlatform() +{ + return infoTag->GetPlatform(); +} + +std::vector<String> InfoTagGame::getGenres() +{ + return infoTag->GetGenres(); +} + +String InfoTagGame::getPublisher() +{ + return infoTag->GetPublisher(); +} + +String InfoTagGame::getDeveloper() +{ + return infoTag->GetDeveloper(); +} + +String InfoTagGame::getOverview() +{ + return infoTag->GetOverview(); +} + +unsigned int InfoTagGame::getYear() +{ + return infoTag->GetYear(); +} + +String InfoTagGame::getGameClient() +{ + return infoTag->GetGameClient(); +} + +void InfoTagGame::setTitle(const String& title) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTitleRaw(infoTag, title); +} + +void InfoTagGame::setPlatform(const String& platform) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPlatformRaw(infoTag, platform); +} + +void InfoTagGame::setGenres(const std::vector<String>& genres) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setGenresRaw(infoTag, genres); +} + +void InfoTagGame::setPublisher(const String& publisher) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPublisherRaw(infoTag, publisher); +} + +void InfoTagGame::setDeveloper(const String& developer) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDeveloperRaw(infoTag, developer); +} + +void InfoTagGame::setOverview(const String& overview) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setOverviewRaw(infoTag, overview); +} + +void InfoTagGame::setYear(unsigned int year) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setYearRaw(infoTag, year); +} + +void InfoTagGame::setGameClient(const String& gameClient) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setGameClientRaw(infoTag, gameClient); +} + +void InfoTagGame::setTitleRaw(KODI::GAME::CGameInfoTag* infoTag, const String& title) +{ + infoTag->SetTitle(title); +} + +void InfoTagGame::setPlatformRaw(KODI::GAME::CGameInfoTag* infoTag, const String& platform) +{ + infoTag->SetPlatform(platform); +} + +void InfoTagGame::setGenresRaw(KODI::GAME::CGameInfoTag* infoTag, const std::vector<String>& genres) +{ + infoTag->SetGenres(genres); +} + +void InfoTagGame::setPublisherRaw(KODI::GAME::CGameInfoTag* infoTag, const String& publisher) +{ + infoTag->SetPublisher(publisher); +} + +void InfoTagGame::setDeveloperRaw(KODI::GAME::CGameInfoTag* infoTag, const String& developer) +{ + infoTag->SetDeveloper(developer); +} + +void InfoTagGame::setOverviewRaw(KODI::GAME::CGameInfoTag* infoTag, const String& overview) +{ + infoTag->SetOverview(overview); +} + +void InfoTagGame::setYearRaw(KODI::GAME::CGameInfoTag* infoTag, unsigned int year) +{ + infoTag->SetYear(year); +} + +void InfoTagGame::setGameClientRaw(KODI::GAME::CGameInfoTag* infoTag, const String& gameClient) +{ + infoTag->SetGameClient(gameClient); +} + +} // namespace xbmc +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/InfoTagGame.h b/xbmc/interfaces/legacy/InfoTagGame.h new file mode 100644 index 0000000..a6161b4 --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagGame.h @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" + +namespace KODI +{ +namespace GAME +{ +class CGameInfoTag; +} +} // namespace KODI + +namespace XBMCAddon +{ +namespace xbmc +{ +/// +/// \defgroup python_InfoTagGame InfoTagGame +/// \ingroup python_xbmc +/// @{ +/// @brief **Kodi's game info tag class.** +/// +/// \python_class{ InfoTagGame() } +/// +/// Access and / or modify the game metadata of a ListItem. +/// +///------------------------------------------------------------------------- +/// @python_v20 New class added. +/// +/// **Example:** +/// ~~~~~~~~~~~~~{.py} +/// ... +/// tag = item.getGameInfoTag() +/// +/// title = tag.getTitle() +/// tag.setDeveloper('John Doe') +/// ... +/// ~~~~~~~~~~~~~ +/// +class InfoTagGame : public AddonClass +{ +private: + KODI::GAME::CGameInfoTag* infoTag; + bool offscreen; + bool owned; + +public: +#ifndef SWIG + explicit InfoTagGame(const KODI::GAME::CGameInfoTag* tag); + explicit InfoTagGame(KODI::GAME::CGameInfoTag* tag, bool offscreen = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ xbmc.InfoTagGame([offscreen]) } + /// Create a game info tag. + /// + /// @param offscreen [opt] bool (default `False`) - if GUI based locks should be + /// avoided. Most of the times listitems are created + /// offscreen and added later to a container + /// for display (e.g. plugins) or they are not + /// even displayed (e.g. python scrapers). + /// In such cases, there is no need to lock the + /// GUI when creating the items (increasing your addon + /// performance). + /// Note however, that if you are creating listitems + /// and managing the container itself (e.g using + /// WindowXML or WindowXMLDialog classes) subsquent + /// modifications to the item will require locking. + /// Thus, in such cases, use the default value (`False`). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// gameinfo = xbmc.InfoTagGame(offscreen=False) + /// ... + /// ~~~~~~~~~~~~~ + /// + InfoTagGame(...); +#else + explicit InfoTagGame(bool offscreen = false); +#endif + ~InfoTagGame() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getTitle() } + /// Gets the title of the game. + /// + /// @return [string] title + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getTitle(); +#else + String getTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getPlatform() } + /// Gets the platform on which the game is run. + /// + /// @return [string] platform + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getPlatform(); +#else + String getPlatform(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getGenres() } + /// Gets the genres of the game. + /// + /// @return [list] genres + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getGenres(); +#else + std::vector<String> getGenres(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getPublisher() } + /// Gets the publisher of the game. + /// + /// @return [string] publisher + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getPublisher(); +#else + String getPublisher(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getDeveloper() } + /// Gets the developer of the game. + /// + /// @return [string] developer + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getDeveloper(); +#else + String getDeveloper(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getOverview() } + /// Gets the overview of the game. + /// + /// @return [string] overview + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getOverview(); +#else + String getOverview(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getYear() } + /// Gets the year in which the game was published. + /// + /// @return [integer] year + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getYear(); +#else + unsigned int getYear(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ getGameClient() } + /// Gets the add-on ID of the game client executing the game. + /// + /// @return [string] game client + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getGameClient(); +#else + String getGameClient(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setTitle(title) } + /// Sets the title of the game. + /// + /// @param title string - title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTitle(...); +#else + void setTitle(const String& title); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setPlatform(platform) } + /// Sets the platform on which the game is run. + /// + /// @param platform string - platform. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPlatform(...); +#else + void setPlatform(const String& platform); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setGenres(genres) } + /// Sets the genres of the game. + /// + /// @param genres list - genres. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setGenres(...); +#else + void setGenres(const std::vector<String>& genres); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setPublisher(publisher) } + /// Sets the publisher of the game. + /// + /// @param publisher string - publisher. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPublisher(...); +#else + void setPublisher(const String& publisher); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setDeveloper(developer) } + /// Sets the developer of the game. + /// + /// @param developer string - title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDeveloper(...); +#else + void setDeveloper(const String& developer); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setOverview(overview) } + /// Sets the overview of the game. + /// + /// @param overview string - overview. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setOverview(...); +#else + void setOverview(const String& overview); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setYear(year) } + /// Sets the year in which the game was published. + /// + /// @param year integer - year. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setYear(...); +#else + void setYear(unsigned int year); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagGame + /// @brief \python_func{ setGameClient(gameClient) } + /// Sets the add-on ID of the game client executing the game. + /// + /// @param gameClient string - game client. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setGameClient(...); +#else + void setGameClient(const String& gameClient); +#endif + +#ifndef SWIG + static void setTitleRaw(KODI::GAME::CGameInfoTag* infoTag, const String& title); + static void setPlatformRaw(KODI::GAME::CGameInfoTag* infoTag, const String& platform); + static void setGenresRaw(KODI::GAME::CGameInfoTag* infoTag, const std::vector<String>& genres); + static void setPublisherRaw(KODI::GAME::CGameInfoTag* infoTag, const String& publisher); + static void setDeveloperRaw(KODI::GAME::CGameInfoTag* infoTag, const String& developer); + static void setOverviewRaw(KODI::GAME::CGameInfoTag* infoTag, const String& overview); + static void setYearRaw(KODI::GAME::CGameInfoTag* infoTag, unsigned int year); + static void setGameClientRaw(KODI::GAME::CGameInfoTag* infoTag, const String& gameClient); +#endif +}; + +} // namespace xbmc +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/InfoTagMusic.cpp b/xbmc/interfaces/legacy/InfoTagMusic.cpp new file mode 100644 index 0000000..3baf7d5 --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagMusic.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "InfoTagMusic.h" + +#include "AddonUtils.h" +#include "ServiceBroker.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +namespace XBMCAddon +{ + namespace xbmc + { + InfoTagMusic::InfoTagMusic(bool offscreen /* = false */) + : infoTag(new MUSIC_INFO::CMusicInfoTag()), offscreen(offscreen), owned(true) + { + } + + InfoTagMusic::InfoTagMusic(const MUSIC_INFO::CMusicInfoTag* tag) : InfoTagMusic(true) + { + *infoTag = *tag; + } + + InfoTagMusic::InfoTagMusic(MUSIC_INFO::CMusicInfoTag* tag, bool offscreen /* = false */) + : infoTag(tag), offscreen(offscreen), owned(false) + { + } + + InfoTagMusic::~InfoTagMusic() + { + if (owned) + delete infoTag; + } + + int InfoTagMusic::getDbId() + { + return infoTag->GetDatabaseId(); + } + + String InfoTagMusic::getURL() + { + return infoTag->GetURL(); + } + + String InfoTagMusic::getTitle() + { + return infoTag->GetTitle(); + } + + String InfoTagMusic::getMediaType() + { + return infoTag->GetType(); + } + + String InfoTagMusic::getArtist() + { + return infoTag->GetArtistString(); + } + + String InfoTagMusic::getAlbumArtist() + { + return infoTag->GetAlbumArtistString(); + } + + String InfoTagMusic::getAlbum() + { + return infoTag->GetAlbum(); + } + + String InfoTagMusic::getGenre() + { + return StringUtils::Join(infoTag->GetGenre(), CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator); + } + + std::vector<String> InfoTagMusic::getGenres() + { + return infoTag->GetGenre(); + } + + int InfoTagMusic::getDuration() + { + return infoTag->GetDuration(); + } + + int InfoTagMusic::getYear() + { + return infoTag->GetYear(); + } + + int InfoTagMusic::getRating() + { + return infoTag->GetRating(); + } + + int InfoTagMusic::getUserRating() + { + return infoTag->GetUserrating(); + } + + int InfoTagMusic::getTrack() + { + return infoTag->GetTrackNumber(); + } + + int InfoTagMusic::getDisc() + { + return infoTag->GetDiscNumber(); + } + + String InfoTagMusic::getReleaseDate() + { + return infoTag->GetReleaseDate(); + } + + int InfoTagMusic::getListeners() + { + return infoTag->GetListeners(); + } + + int InfoTagMusic::getPlayCount() + { + return infoTag->GetPlayCount(); + } + + String InfoTagMusic::getLastPlayed() + { + CLog::Log(LOGWARNING, "InfoTagMusic.getLastPlayed() is deprecated and might be removed in " + "future Kodi versions. Please use InfoTagMusic.getLastPlayedAsW3C()."); + + return infoTag->GetLastPlayed().GetAsLocalizedDate(); + } + + String InfoTagMusic::getLastPlayedAsW3C() + { + return infoTag->GetLastPlayed().GetAsW3CDateTime(); + } + + String InfoTagMusic::getComment() + { + return infoTag->GetComment(); + } + + String InfoTagMusic::getLyrics() + { + return infoTag->GetLyrics(); + } + + String InfoTagMusic::getMusicBrainzTrackID() + { + return infoTag->GetMusicBrainzTrackID(); + } + + std::vector<String> InfoTagMusic::getMusicBrainzArtistID() + { + return infoTag->GetMusicBrainzArtistID(); + } + + String InfoTagMusic::getMusicBrainzAlbumID() + { + return infoTag->GetMusicBrainzAlbumID(); + } + + String InfoTagMusic::getMusicBrainzReleaseGroupID() + { + return infoTag->GetMusicBrainzReleaseGroupID(); + } + + std::vector<String> InfoTagMusic::getMusicBrainzAlbumArtistID() + { + return infoTag->GetMusicBrainzAlbumArtistID(); + } + + void InfoTagMusic::setDbId(int dbId, const String& type) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDbIdRaw(infoTag, dbId, type); + } + + void InfoTagMusic::setURL(const String& url) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setURLRaw(infoTag, url); + } + + void InfoTagMusic::setMediaType(const String& mediaType) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMediaTypeRaw(infoTag, mediaType); + } + + void InfoTagMusic::setTrack(int track) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTrackRaw(infoTag, track); + } + + void InfoTagMusic::setDisc(int disc) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDiscRaw(infoTag, disc); + } + + void InfoTagMusic::setDuration(int duration) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDurationRaw(infoTag, duration); + } + + void InfoTagMusic::setYear(int year) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setYearRaw(infoTag, year); + } + + void InfoTagMusic::setReleaseDate(const String& releaseDate) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setReleaseDateRaw(infoTag, releaseDate); + } + + void InfoTagMusic::setListeners(int listeners) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setListenersRaw(infoTag, listeners); + } + + void InfoTagMusic::setPlayCount(int playcount) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPlayCountRaw(infoTag, playcount); + } + + void InfoTagMusic::setGenres(const std::vector<String>& genres) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setGenresRaw(infoTag, genres); + } + + void InfoTagMusic::setAlbum(const String& album) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setAlbumRaw(infoTag, album); + } + + void InfoTagMusic::setArtist(const String& artist) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setArtistRaw(infoTag, artist); + } + + void InfoTagMusic::setAlbumArtist(const String& albumArtist) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setAlbumArtistRaw(infoTag, albumArtist); + } + + void InfoTagMusic::setTitle(const String& title) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTitleRaw(infoTag, title); + } + + void InfoTagMusic::setRating(float rating) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setRatingRaw(infoTag, rating); + } + + void InfoTagMusic::setUserRating(int userrating) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setUserRatingRaw(infoTag, userrating); + } + + void InfoTagMusic::setLyrics(const String& lyrics) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setLyricsRaw(infoTag, lyrics); + } + + void InfoTagMusic::setLastPlayed(const String& lastPlayed) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setLastPlayedRaw(infoTag, lastPlayed); + } + + void InfoTagMusic::setMusicBrainzTrackID(const String& musicBrainzTrackID) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMusicBrainzTrackIDRaw(infoTag, musicBrainzTrackID); + } + + void InfoTagMusic::setMusicBrainzArtistID(const std::vector<String>& musicBrainzArtistID) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMusicBrainzArtistIDRaw(infoTag, musicBrainzArtistID); + } + + void InfoTagMusic::setMusicBrainzAlbumID(const String& musicBrainzAlbumID) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMusicBrainzAlbumIDRaw(infoTag, musicBrainzAlbumID); + } + + void InfoTagMusic::setMusicBrainzReleaseGroupID(const String& musicBrainzReleaseGroupID) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMusicBrainzReleaseGroupIDRaw(infoTag, musicBrainzReleaseGroupID); + } + + void InfoTagMusic::setMusicBrainzAlbumArtistID( + const std::vector<String>& musicBrainzAlbumArtistID) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMusicBrainzAlbumArtistIDRaw(infoTag, musicBrainzAlbumArtistID); + } + + void InfoTagMusic::setComment(const String& comment) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setCommentRaw(infoTag, comment); + } + + void InfoTagMusic::setDbIdRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int dbId, const String& type) + { + infoTag->SetDatabaseId(dbId, type); + } + + void InfoTagMusic::setURLRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& url) + { + infoTag->SetURL(url); + } + + void InfoTagMusic::setMediaTypeRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& mediaType) + { + infoTag->SetType(mediaType); + } + + void InfoTagMusic::setTrackRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int track) + { + infoTag->SetTrackNumber(track); + } + + void InfoTagMusic::setDiscRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int disc) + { + infoTag->SetDiscNumber(disc); + } + + void InfoTagMusic::setDurationRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int duration) + { + infoTag->SetDuration(duration); + } + + void InfoTagMusic::setYearRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int year) + { + infoTag->SetYear(year); + } + + void InfoTagMusic::setReleaseDateRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& releaseDate) + { + infoTag->SetReleaseDate(releaseDate); + } + + void InfoTagMusic::setListenersRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int listeners) + { + infoTag->SetListeners(listeners); + } + + void InfoTagMusic::setPlayCountRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int playcount) + { + infoTag->SetPlayCount(playcount); + } + + void InfoTagMusic::setGenresRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const std::vector<String>& genres) + { + infoTag->SetGenre(genres); + } + + void InfoTagMusic::setAlbumRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& album) + { + infoTag->SetAlbum(album); + } + + void InfoTagMusic::setArtistRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& artist) + { + infoTag->SetArtist(artist); + } + + void InfoTagMusic::setAlbumArtistRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& albumArtist) + { + infoTag->SetAlbumArtist(albumArtist); + } + + void InfoTagMusic::setTitleRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& title) + { + infoTag->SetTitle(title); + } + + void InfoTagMusic::setRatingRaw(MUSIC_INFO::CMusicInfoTag* infoTag, float rating) + { + infoTag->SetRating(rating); + } + + void InfoTagMusic::setUserRatingRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int userrating) + { + infoTag->SetUserrating(userrating); + } + + void InfoTagMusic::setLyricsRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& lyrics) + { + infoTag->SetLyrics(lyrics); + } + + void InfoTagMusic::setLastPlayedRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& lastPlayed) + { + infoTag->SetLastPlayed(lastPlayed); + } + + void InfoTagMusic::setMusicBrainzTrackIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzTrackID) + { + infoTag->SetMusicBrainzTrackID(musicBrainzTrackID); + } + + void InfoTagMusic::setMusicBrainzArtistIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const std::vector<String>& musicBrainzArtistID) + { + infoTag->SetMusicBrainzArtistID(musicBrainzArtistID); + } + + void InfoTagMusic::setMusicBrainzAlbumIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzAlbumID) + { + infoTag->SetMusicBrainzAlbumID(musicBrainzAlbumID); + } + + void InfoTagMusic::setMusicBrainzReleaseGroupIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzReleaseGroupID) + { + infoTag->SetMusicBrainzReleaseGroupID(musicBrainzReleaseGroupID); + } + + void InfoTagMusic::setMusicBrainzAlbumArtistIDRaw( + MUSIC_INFO::CMusicInfoTag* infoTag, const std::vector<String>& musicBrainzAlbumArtistID) + { + infoTag->SetMusicBrainzAlbumArtistID(musicBrainzAlbumArtistID); + } + + void InfoTagMusic::setCommentRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& comment) + { + infoTag->SetComment(comment); + } + } +} + diff --git a/xbmc/interfaces/legacy/InfoTagMusic.h b/xbmc/interfaces/legacy/InfoTagMusic.h new file mode 100644 index 0000000..835a705 --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagMusic.h @@ -0,0 +1,1034 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" + +#include <vector> + +namespace MUSIC_INFO +{ +class CMusicInfoTag; +} + +namespace XBMCAddon +{ + namespace xbmc + { + // + /// \defgroup python_InfoTagMusic InfoTagMusic + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's music info tag class.** + /// + /// \python_class{ xbmc.InfoTagMusic([offscreen]) } + /// + /// Access and / or modify the music metadata of a ListItem. + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// tag = xbmc.Player().getMusicInfoTag() + /// + /// title = tag.getTitle() + /// url = tag.getURL() + /// ... + /// ~~~~~~~~~~~~~ + // + class InfoTagMusic : public AddonClass + { + private: + MUSIC_INFO::CMusicInfoTag* infoTag; + bool offscreen; + bool owned; + + public: +#ifndef SWIG + explicit InfoTagMusic(const MUSIC_INFO::CMusicInfoTag* tag); + explicit InfoTagMusic(MUSIC_INFO::CMusicInfoTag* tag, bool offscreen = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ xbmc.InfoTagMusic([offscreen]) } + /// Create a music info tag. + /// + /// @param offscreen [opt] bool (default `False`) - if GUI based locks should be + /// avoided. Most of the times listitems are created + /// offscreen and added later to a container + /// for display (e.g. plugins) or they are not + /// even displayed (e.g. python scrapers). + /// In such cases, there is no need to lock the + /// GUI when creating the items (increasing your addon + /// performance). + /// Note however, that if you are creating listitems + /// and managing the container itself (e.g using + /// WindowXML or WindowXMLDialog classes) subsquent + /// modifications to the item will require locking. + /// Thus, in such cases, use the default value (`False`). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Added **offscreen** argument. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// musicinfo = xbmc.InfoTagMusic(offscreen=False) + /// ... + /// ~~~~~~~~~~~~~ + /// + InfoTagMusic(...); +#else + explicit InfoTagMusic(bool offscreen = false); +#endif + ~InfoTagMusic() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getDbId() } + /// Get identification number of tag in database. + /// + /// @return [integer] database id. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getDbId(); +#else + int getDbId(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getURL() } + /// Returns url of source as string from music info tag. + /// + /// @return [string] Url of source + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getURL(); +#else + String getURL(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getTitle() } + /// Returns the title from music as string on info tag. + /// + /// @return [string] Music title + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getTitle(); +#else + String getTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMediaType() } + /// Get the media type of the music item. + /// + /// @return [string] media type + /// + /// Available strings about media type for music: + /// | String | Description | + /// |---------------:|:--------------------------------------------------| + /// | artist | If it is defined as an artist + /// | album | If it is defined as an album + /// | song | If it is defined as a song + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getMediaType(); +#else + String getMediaType(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getArtist() } + /// Returns the artist from music as string if present. + /// + /// @return [string] Music artist + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getArtist(); +#else + String getArtist(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getAlbum() } + /// Returns the album from music tag as string if present. + /// + /// @return [string] Music album name + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getAlbum(); +#else + String getAlbum(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getAlbumArtist() } + /// Returns the album artist from music tag as string if present. + /// + /// @return [string] Music album artist name + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getAlbumArtist(); +#else + String getAlbumArtist(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getGenre() } + /// Returns the genre name from music tag as string if present. + /// + /// @return [string] Genre name + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getGenres()** instead. + /// + getGenre(); +#else + String getGenre(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getGenres() } + /// Returns the list of genres from music tag if present. + /// + /// @return [list] List of genres + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getGenres(); +#else + std::vector<String> getGenres(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getDuration() } + /// Returns the duration of music as integer from info tag. + /// + /// @return [integer] Duration + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getDuration(); +#else + int getDuration(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getYear() } + /// Returns the year of music as integer from info tag. + /// + /// @return [integer] Year + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// + getYear(); +#else + int getYear(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getRating() } + /// Returns the scraped rating as integer. + /// + /// @return [integer] Rating + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getRating(); +#else + int getRating(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getUserRating() } + /// Returns the user rating as integer (-1 if not existing) + /// + /// @return [integer] User rating + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getUserRating(); +#else + int getUserRating(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getTrack() } + /// Returns the track number (if present) from music info tag as integer. + /// + /// @return [integer] Track number + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getTrack(); +#else + int getTrack(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getDisc() } + /// Returns the disk number (if present) from music info tag as integer. + /// + /// @return [integer] Disc number + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getDisc(); +#else + /** + * getDisc() -- returns an integer.\n + */ + int getDisc(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getReleaseDate() } + /// Returns the release date as string from music info tag (if present). + /// + /// @return [string] Release date + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getReleaseDate(); +#else + String getReleaseDate(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getListeners() } + /// Returns the listeners as integer from music info tag. + /// + /// @return [integer] Listeners + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getListeners(); +#else + int getListeners(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getPlayCount() } + /// Returns the number of carried out playbacks. + /// + /// @return [integer] Playback count + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPlayCount(); +#else + int getPlayCount(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getLastPlayed() } + /// Returns last played time as string from music info tag. + /// + /// @return [string] Last played date / time on tag + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getLastPlayedAsW3C()** instead. + /// + getLastPlayed(); +#else + String getLastPlayed(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getLastPlayedAsW3C() } + /// Returns last played time as string in W3C format (YYYY-MM-DDThh:mm:ssTZD). + /// + /// @return [string] Last played datetime (W3C) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getLastPlayedAsW3C(); +#else + String getLastPlayedAsW3C(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getComment() } + /// Returns comment as string from music info tag. + /// + /// @return [string] Comment on tag + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getComment(); +#else + String getComment(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getLyrics() } + /// Returns a string from lyrics. + /// + /// @return [string] Lyrics on tag + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getLyrics(); +#else + String getLyrics(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMusicBrainzTrackID() } + /// Returns the MusicBrainz Recording ID from music info tag (if present). + /// + /// @return [string] MusicBrainz Recording ID + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getMusicBrainzTrackID(); +#else + String getMusicBrainzTrackID(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMusicBrainzArtistID() } + /// Returns the MusicBrainz Artist IDs from music info tag (if present). + /// + /// @return [list] MusicBrainz Artist IDs + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getMusicBrainzArtistID(); +#else + std::vector<String> getMusicBrainzArtistID(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMusicBrainzAlbumID() } + /// Returns the MusicBrainz Release ID from music info tag (if present). + /// + /// @return [string] MusicBrainz Release ID + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getMusicBrainzAlbumID(); +#else + String getMusicBrainzAlbumID(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMusicBrainzReleaseGroupID() } + /// Returns the MusicBrainz Release Group ID from music info tag (if present). + /// + /// @return [string] MusicBrainz Release Group ID + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getMusicBrainzReleaseGroupID(); +#else + String getMusicBrainzReleaseGroupID(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ getMusicBrainzAlbumArtistID() } + /// Returns the MusicBrainz Release Artist IDs from music info tag (if present). + /// + /// @return [list] MusicBrainz Release Artist IDs + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getMusicBrainzAlbumArtistID(); +#else + std::vector<String> getMusicBrainzAlbumArtistID(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setDbId(dbId, type) } + /// Set the database identifier of the music item. + /// + /// @param dbId integer - Database identifier. + /// @param type string - Media type of the item. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDbId(...); +#else + void setDbId(int dbId, const String& type); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setURL(url) } + /// Set the URL of the music item. + /// + /// @param url string - URL. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setURL(...); +#else + void setURL(const String& url); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMediaType(mediaType) } + /// Set the media type of the music item. + /// + /// @param mediaType string - Media type. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMediaType(...); +#else + void setMediaType(const String& mediaType); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setTrack(track) } + /// Set the track number of the song. + /// + /// @param track integer - Track number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTrack(...); +#else + void setTrack(int track); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setDisc(disc) } + /// Set the disc number of the song. + /// + /// @param disc integer - Disc number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDisc(...); +#else + void setDisc(int disc); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setDuration(duration) } + /// Set the duration of the song. + /// + /// @param duration integer - Duration in seconds. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDuration(...); +#else + void setDuration(int duration); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setYear(year) } + /// Set the year of the music item. + /// + /// @param year integer - Year. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setYear(...); +#else + void setYear(int year); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setReleaseDate(releaseDate) } + /// Set the release date of the music item. + /// + /// @param releaseDate string - Release date in ISO8601 format (YYYY, YYYY-MM or YYYY-MM-DD). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setReleaseDate(...); +#else + void setReleaseDate(const String& releaseDate); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setListeners(listeners) } + /// Set the number of listeners of the music item. + /// + /// @param listeners integer - Number of listeners. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setListeners(...); +#else + void setListeners(int listeners); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setPlayCount(playcount) } + /// Set the playcount of the music item. + /// + /// @param playcount integer - Playcount. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPlayCount(...); +#else + void setPlayCount(int playcount); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setGenres(genres) } + /// Set the genres of the music item. + /// + /// @param genres list - Genres. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setGenres(...); +#else + void setGenres(const std::vector<String>& genres); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setAlbum(album) } + /// Set the album of the music item. + /// + /// @param album string - Album. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setAlbum(...); +#else + void setAlbum(const String& album); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setArtist(artist) } + /// Set the artist(s) of the music item. + /// + /// @param artist string - Artist(s). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setArtist(...); +#else + void setArtist(const String& artist); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setAlbumArtist(albumArtist) } + /// Set the album artist(s) of the music item. + /// + /// @param albumArtist string - Album artist(s). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setAlbumArtist(...); +#else + void setAlbumArtist(const String& albumArtist); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setTitle(title) } + /// Set the title of the music item. + /// + /// @param title string - Title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTitle(...); +#else + void setTitle(const String& title); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setRating(rating) } + /// Set the rating of the music item. + /// + /// @param rating float - Rating. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setRating(...); +#else + void setRating(float rating); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setUserRating(userrating) } + /// Set the user rating of the music item. + /// + /// @param userrating integer - User rating. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setUserRating(...); +#else + void setUserRating(int userrating); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setLyrics(lyrics) } + /// Set the lyrics of the song. + /// + /// @param lyrics string - Lyrics. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLyrics(...); +#else + void setLyrics(const String& lyrics); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setLastPlayed(lastPlayed) } + /// Set the last played date of the music item. + /// + /// @param lastPlayed string - Last played date (YYYY-MM-DD HH:MM:SS). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLastPlayed(...); +#else + void setLastPlayed(const String& lastPlayed); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMusicBrainzTrackID(musicBrainzTrackID) } + /// Set the MusicBrainz track ID of the song. + /// + /// @param musicBrainzTrackID string - MusicBrainz track ID. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMusicBrainzTrackID(...); +#else + void setMusicBrainzTrackID(const String& musicBrainzTrackID); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMusicBrainzArtistID(musicBrainzArtistID) } + /// Set the MusicBrainz artist IDs of the music item. + /// + /// @param musicBrainzArtistID list - MusicBrainz artist IDs. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMusicBrainzArtistID(...); +#else + void setMusicBrainzArtistID(const std::vector<String>& musicBrainzArtistID); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMusicBrainzAlbumID(musicBrainzAlbumID) } + /// Set the MusicBrainz album ID of the music item. + /// + /// @param musicBrainzAlbumID string - MusicBrainz album ID. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMusicBrainzAlbumID(...); +#else + void setMusicBrainzAlbumID(const String& musicBrainzAlbumID); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMusicBrainzReleaseGroupID(musicBrainzReleaseGroupID) } + /// Set the MusicBrainz release group ID of the music item. + /// + /// @param musicBrainzReleaseGroupID string - MusicBrainz release group ID. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMusicBrainzReleaseGroupID(...); +#else + void setMusicBrainzReleaseGroupID(const String& musicBrainzReleaseGroupID); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setMusicBrainzAlbumArtistID(musicBrainzAlbumArtistID) } + /// Set the MusicBrainz album artist IDs of the music item. + /// + /// @param musicBrainzAlbumArtistID list - MusicBrainz album artist IDs. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMusicBrainzAlbumArtistID(...); +#else + void setMusicBrainzAlbumArtistID(const std::vector<String>& musicBrainzAlbumArtistID); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagMusic + /// @brief \python_func{ setComment(comment) } + /// Set the comment of the music item. + /// + /// @param comment string - Comment. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setComment(...); +#else + void setComment(const String& comment); +#endif + +#ifndef SWIG + static void setDbIdRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int dbId, const String& type); + static void setURLRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& url); + static void setMediaTypeRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& mediaType); + static void setTrackRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int track); + static void setDiscRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int disc); + static void setDurationRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int duration); + static void setYearRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int year); + static void setReleaseDateRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& releaseDate); + static void setListenersRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int listeners); + static void setPlayCountRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int playcount); + static void setGenresRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const std::vector<String>& genres); + static void setAlbumRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& album); + static void setArtistRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& artist); + static void setAlbumArtistRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& albumArtist); + static void setTitleRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& title); + static void setRatingRaw(MUSIC_INFO::CMusicInfoTag* infoTag, float rating); + static void setUserRatingRaw(MUSIC_INFO::CMusicInfoTag* infoTag, int userrating); + static void setLyricsRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& lyrics); + static void setLastPlayedRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& lastPlayed); + static void setMusicBrainzTrackIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzTrackID); + static void setMusicBrainzArtistIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const std::vector<String>& musicBrainzArtistID); + static void setMusicBrainzAlbumIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzAlbumID); + static void setMusicBrainzReleaseGroupIDRaw(MUSIC_INFO::CMusicInfoTag* infoTag, + const String& musicBrainzReleaseGroupID); + static void setMusicBrainzAlbumArtistIDRaw( + MUSIC_INFO::CMusicInfoTag* infoTag, const std::vector<String>& musicBrainzAlbumArtistID); + static void setCommentRaw(MUSIC_INFO::CMusicInfoTag* infoTag, const String& comment); +#endif + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/InfoTagPicture.cpp b/xbmc/interfaces/legacy/InfoTagPicture.cpp new file mode 100644 index 0000000..892e2be --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagPicture.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "InfoTagPicture.h" + +#include "AddonUtils.h" +#include "guilib/guiinfo/GUIInfoLabels.h" +#include "interfaces/legacy/Exception.h" +#include "pictures/PictureInfoTag.h" +#include "utils/StringUtils.h" + +using namespace XBMCAddonUtils; + +namespace XBMCAddon +{ +namespace xbmc +{ + +InfoTagPicture::InfoTagPicture(bool offscreen /* = false */) + : infoTag(new CPictureInfoTag), offscreen(offscreen), owned(true) +{ +} + +InfoTagPicture::InfoTagPicture(const CPictureInfoTag* tag) + : infoTag(new CPictureInfoTag(*tag)), offscreen(true), owned(true) +{ +} + +InfoTagPicture::InfoTagPicture(CPictureInfoTag* tag, bool offscreen /* = false */) + : infoTag(tag), offscreen(offscreen), owned(false) +{ +} + +InfoTagPicture::~InfoTagPicture() +{ + if (owned) + delete infoTag; +} + +String InfoTagPicture::getResolution() +{ + return infoTag->GetInfo(SLIDESHOW_RESOLUTION); +} + +String InfoTagPicture::getDateTimeTaken() +{ + return infoTag->GetDateTimeTaken().GetAsW3CDateTime(); +} + +void InfoTagPicture::setResolution(int width, int height) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setResolutionRaw(infoTag, width, height); +} + +void InfoTagPicture::setDateTimeTaken(const String& datetimetaken) +{ + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDateTimeTakenRaw(infoTag, datetimetaken); +} + +void InfoTagPicture::setResolutionRaw(CPictureInfoTag* infoTag, const String& resolution) +{ + infoTag->SetInfo("resolution", resolution); +} + +void InfoTagPicture::setResolutionRaw(CPictureInfoTag* infoTag, int width, int height) +{ + if (width <= 0) + throw WrongTypeException("InfoTagPicture.setResolution: width must be greater than zero (0)"); + if (height <= 0) + throw WrongTypeException("InfoTagPicture.setResolution: height must be greater than zero (0)"); + + setResolutionRaw(infoTag, StringUtils::Format("{:d},{:d}", width, height)); +} + +void InfoTagPicture::setDateTimeTakenRaw(CPictureInfoTag* infoTag, String datetimetaken) +{ + // try to parse the datetimetaken as from W3C format and adjust it to the EXIF datetime format YYYY:MM:DD HH:MM:SS + CDateTime w3cDateTimeTaken; + if (w3cDateTimeTaken.SetFromW3CDateTime(datetimetaken)) + { + datetimetaken = StringUtils::Format("{:4d}:{:2d}:{:2d} {:2d}:{:2d}:{:2d}", + w3cDateTimeTaken.GetYear(), w3cDateTimeTaken.GetMonth(), + w3cDateTimeTaken.GetDay(), w3cDateTimeTaken.GetHour(), + w3cDateTimeTaken.GetMinute(), w3cDateTimeTaken.GetSecond()); + } + + infoTag->SetInfo("exiftime", datetimetaken); +} + +} // namespace xbmc +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/InfoTagPicture.h b/xbmc/interfaces/legacy/InfoTagPicture.h new file mode 100644 index 0000000..a03bf0f --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagPicture.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" + +class CPictureInfoTag; + +namespace XBMCAddon +{ +namespace xbmc +{ + +/// +/// \defgroup python_InfoTagPicture InfoTagPicture +/// \ingroup python_xbmc +/// @{ +/// @brief **Kodi's picture info tag class.** +/// +/// \python_class{ InfoTagPicture() } +/// +/// Access and / or modify the picture metadata of a ListItem. +/// +///------------------------------------------------------------------------- +/// @python_v20 New class added. +/// +/// **Example:** +/// ~~~~~~~~~~~~~{.py} +/// ... +/// tag = item.getPictureInfoTag() +/// +/// datetime_taken = tag.getDateTimeTaken() +/// tag.setResolution(1920, 1080) +/// ... +/// ~~~~~~~~~~~~~ +/// +class InfoTagPicture : public AddonClass +{ +private: + CPictureInfoTag* infoTag; + bool offscreen; + bool owned; + +public: +#ifndef SWIG + explicit InfoTagPicture(const CPictureInfoTag* tag); + explicit InfoTagPicture(CPictureInfoTag* tag, bool offscreen = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagPicture + /// @brief \python_func{ xbmc.InfoTagPicture([offscreen]) } + /// Create a picture info tag. + /// + /// @param offscreen [opt] bool (default `False`) - if GUI based locks should be + /// avoided. Most of the times listitems are created + /// offscreen and added later to a container + /// for display (e.g. plugins) or they are not + /// even displayed (e.g. python scrapers). + /// In such cases, there is no need to lock the + /// GUI when creating the items (increasing your addon + /// performance). + /// Note however, that if you are creating listitems + /// and managing the container itself (e.g using + /// WindowXML or WindowXMLDialog classes) subsquent + /// modifications to the item will require locking. + /// Thus, in such cases, use the default value (`False`). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// pictureinfo = xbmc.InfoTagPicture(offscreen=False) + /// ... + /// ~~~~~~~~~~~~~ + /// + InfoTagPicture(...); +#else + explicit InfoTagPicture(bool offscreen = false); +#endif + ~InfoTagPicture() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagPicture + /// @brief \python_func{ getResolution() } + /// Get the resolution of the picture in the format "w x h". + /// + /// @return [string] Resolution of the picture in the format "w x h". + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getResolution(); +#else + String getResolution(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagPicture + /// @brief \python_func{ getDateTimeTaken() } + /// Get the date and time at which the picture was taken in W3C format. + /// + /// @return [string] Date and time at which the picture was taken in W3C format. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getDirector(); +#else + String getDateTimeTaken(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagPicture + /// @brief \python_func{ setResolution(width, height) } + /// Sets the resolution of the picture. + /// + /// @param width int - Width of the picture in pixels. + /// @param height int - Height of the picture in pixels. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setResolution(...); +#else + void setResolution(int width, int height); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagPicture + /// @brief \python_func{ setDateTimeTaken(datetimetaken) } + /// Sets the date and time at which the picture was taken in W3C format. + /// The following formats are supported: + /// - YYYY + /// - YYYY-MM-DD + /// - YYYY-MM-DDThh:mm[TZD] + /// - YYYY-MM-DDThh:mm:ss[TZD] + /// where the timezone (TZD) is always optional and can be in one of the + /// following formats: + /// - Z (for UTC) + /// - +hh:mm + /// - -hh:mm + /// + /// @param datetimetaken string - Date and time at which the picture was taken in W3C format. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDateTimeTaken(...); +#else + void setDateTimeTaken(const String& datetimetaken); +#endif + +#ifndef SWIG + static void setResolutionRaw(CPictureInfoTag* infoTag, const String& resolution); + static void setResolutionRaw(CPictureInfoTag* infoTag, int width, int height); + static void setDateTimeTakenRaw(CPictureInfoTag* infoTag, String datetimetaken); +#endif +}; + +} // namespace xbmc +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/InfoTagRadioRDS.cpp b/xbmc/interfaces/legacy/InfoTagRadioRDS.cpp new file mode 100644 index 0000000..b60ea4c --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagRadioRDS.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "InfoTagRadioRDS.h" + +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRRadioRDSInfoTag.h" +#include "utils/StringUtils.h" + +namespace XBMCAddon +{ + namespace xbmc + { + InfoTagRadioRDS::InfoTagRadioRDS() = default; + + InfoTagRadioRDS::InfoTagRadioRDS(const std::shared_ptr<PVR::CPVRChannel>& channel) + { + if (channel) + infoTag = channel->GetRadioRDSInfoTag(); + } + + InfoTagRadioRDS::~InfoTagRadioRDS() = default; + + String InfoTagRadioRDS::getTitle() + { + if (infoTag) + return infoTag->GetTitle(); + return ""; + } + + String InfoTagRadioRDS::getBand() + { + if (infoTag) + return infoTag->GetBand(); + return ""; + } + + String InfoTagRadioRDS::getArtist() + { + if (infoTag) + return infoTag->GetArtist(); + return ""; + } + + String InfoTagRadioRDS::getComposer() + { + if (infoTag) + return infoTag->GetComposer(); + return ""; + } + + String InfoTagRadioRDS::getConductor() + { + if (infoTag) + return infoTag->GetConductor(); + return ""; + } + + String InfoTagRadioRDS::getAlbum() + { + if (infoTag) + return infoTag->GetAlbum(); + return ""; + } + + String InfoTagRadioRDS::getComment() + { + if (infoTag) + return infoTag->GetComment(); + return ""; + } + + int InfoTagRadioRDS::getAlbumTrackNumber() + { + if (infoTag) + return infoTag->GetAlbumTrackNumber(); + return 0; + } + + String InfoTagRadioRDS::getInfoNews() + { + if (infoTag) + return infoTag->GetInfoNews(); + return ""; + } + + String InfoTagRadioRDS::getInfoNewsLocal() + { + if (infoTag) + return infoTag->GetInfoNewsLocal(); + return ""; + } + + String InfoTagRadioRDS::getInfoSport() + { + if (infoTag) + return infoTag->GetInfoSport(); + return ""; + } + + String InfoTagRadioRDS::getInfoStock() + { + if (infoTag) + return infoTag->GetInfoStock(); + return ""; + } + + String InfoTagRadioRDS::getInfoWeather() + { + if (infoTag) + return infoTag->GetInfoWeather(); + return ""; + } + + String InfoTagRadioRDS::getInfoHoroscope() + { + if (infoTag) + return infoTag->GetInfoHoroscope(); + return ""; + } + + String InfoTagRadioRDS::getInfoCinema() + { + if (infoTag) + return infoTag->GetInfoCinema(); + return ""; + } + + String InfoTagRadioRDS::getInfoLottery() + { + if (infoTag) + return infoTag->GetInfoLottery(); + return ""; + } + + String InfoTagRadioRDS::getInfoOther() + { + if (infoTag) + return infoTag->GetInfoOther(); + return ""; + } + + String InfoTagRadioRDS::getEditorialStaff() + { + if (infoTag) + return infoTag->GetEditorialStaff(); + return ""; + } + + String InfoTagRadioRDS::getProgStation() + { + if (infoTag) + return infoTag->GetProgStation(); + return ""; + } + + String InfoTagRadioRDS::getProgStyle() + { + if (infoTag) + return infoTag->GetProgStyle(); + return ""; + } + + String InfoTagRadioRDS::getProgHost() + { + if (infoTag) + return infoTag->GetProgHost(); + return ""; + } + + String InfoTagRadioRDS::getProgWebsite() + { + if (infoTag) + return infoTag->GetProgWebsite(); + return ""; + } + + String InfoTagRadioRDS::getProgNow() + { + if (infoTag) + return infoTag->GetProgNow(); + return ""; + } + + String InfoTagRadioRDS::getProgNext() + { + if (infoTag) + return infoTag->GetProgNext(); + return ""; + } + + String InfoTagRadioRDS::getPhoneHotline() + { + if (infoTag) + return infoTag->GetPhoneHotline(); + return ""; + } + + String InfoTagRadioRDS::getEMailHotline() + { + if (infoTag) + return infoTag->GetEMailHotline(); + return ""; + } + + String InfoTagRadioRDS::getPhoneStudio() + { + if (infoTag) + return infoTag->GetPhoneStudio(); + return ""; + } + + String InfoTagRadioRDS::getEMailStudio() + { + if (infoTag) + return infoTag->GetEMailStudio(); + return ""; + } + + String InfoTagRadioRDS::getSMSStudio() + { + if (infoTag) + return infoTag->GetSMSStudio(); + return ""; + } + + } +} + diff --git a/xbmc/interfaces/legacy/InfoTagRadioRDS.h b/xbmc/interfaces/legacy/InfoTagRadioRDS.h new file mode 100644 index 0000000..6a68221 --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagRadioRDS.h @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" + +#include <memory> + +namespace PVR +{ +class CPVRChannel; +class CPVRRadioRDSInfoTag; +} + +namespace XBMCAddon +{ + namespace xbmc + { + // + /// \defgroup python_InfoTagRadioRDS InfoTagRadioRDS + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's radio RDS info tag class.** + /// + /// \python_class{ InfoTagRadioRDS() } + /// + /// To get radio RDS info tag data of currently played PVR radio channel source. + /// + /// @note Info tag load is only be possible from present player class.\n + /// Also is all the data variable from radio channels and not known on beginning + /// of radio receiving. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// tag = xbmc.Player().getRadioRDSInfoTag() + /// + /// title = tag.getTitle() + /// artist = tag.getArtist() + /// ... + /// ~~~~~~~~~~~~~ + // + class InfoTagRadioRDS : public AddonClass + { + private: + std::shared_ptr<PVR::CPVRRadioRDSInfoTag> infoTag; + + public: +#ifndef SWIG + explicit InfoTagRadioRDS(const std::shared_ptr<PVR::CPVRChannel>& channel); +#endif + InfoTagRadioRDS(); + ~InfoTagRadioRDS() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getTitle() } + /// Title of the item on the air; i.e. song title. + /// + /// @return Title + /// + getTitle(); +#else + String getTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getBand() } + /// Band of the item on air. + /// + /// @return Band + /// + getBand(); +#else + String getBand(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getArtist() } + /// Artist of the item on air. + /// + /// @return Artist + /// + getArtist(); +#else + String getArtist(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getComposer() } + /// Get the Composer of the music. + /// + /// @return Composer + /// + getComposer(); +#else + String getComposer(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getConductor() } + /// Get the Conductor of the Band. + /// + /// @return Conductor + /// + getConductor(); +#else + String getConductor(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getAlbum() } + /// Album of item on air. + /// + /// @return Album name + /// + getAlbum(); +#else + String getAlbum(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getComment() } + /// Get Comment text from channel. + /// + /// @return Comment + /// + getComment(); +#else + String getComment(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getAlbumTrackNumber() } + /// Get the album track number of currently sended music. + /// + /// @return Track Number + /// + getAlbumTrackNumber(); +#else + int getAlbumTrackNumber(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoNews() } + /// Get News informations. + /// + /// @return News Information + /// + getInfoNews(); +#else + String getInfoNews(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoNewsLocal() } + /// Get Local news informations. + /// + /// @return Local News Information + /// + getInfoNewsLocal(); +#else + String getInfoNewsLocal(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoSport() } + /// Get Sport informations. + /// + /// @return Sport Information + /// + getInfoSport(); +#else + String getInfoSport(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoStock() } + /// Get Stock informations. + /// + /// @return Stock Information + /// + getInfoStock(); +#else + String getInfoStock(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoWeather() } + /// Get Weather informations. + /// + /// @return Weather Information + /// + getInfoWeather(); +#else + String getInfoWeather(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoHoroscope() } + /// Get Horoscope informations. + /// + /// @return Horoscope Information + /// + getInfoHoroscope(); +#else + String getInfoHoroscope(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoCinema() } + /// Get Cinema informations. + /// + /// @return Cinema Information + /// + getInfoCinema(); +#else + String getInfoCinema(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoLottery() } + /// Get Lottery informations. + /// + /// @return Lottery Information + /// + getInfoLottery(); +#else + String getInfoLottery(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getInfoOther() } + /// Get other informations. + /// + /// @return Other Information + /// + getInfoOther(); +#else + String getInfoOther(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getEditorialStaff() } + /// Get Editorial Staff names. + /// + /// @return Editorial Staff + /// + getEditorialStaff(); +#else + String getEditorialStaff(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgStation() } + /// Name describing station. + /// + /// @return Program Station + /// + getProgStation(); +#else + String getProgStation(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgStyle() } + /// The the radio channel style currently used. + /// + /// @return Program Style + /// + getProgStyle(); +#else + String getProgStyle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgHost() } + /// Host of current radio show. + /// + /// @return Program Host + /// + getProgHost(); +#else + String getProgHost(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgWebsite() } + /// Link to URL (web page) for radio station homepage. + /// + /// @return Program Website + /// + getProgWebsite(); +#else + String getProgWebsite(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgNow() } + /// Current radio program show. + /// + /// @return Program Now + /// + getProgNow(); +#else + String getProgNow(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getProgNext() } + /// Next program show. + /// + /// @return Program Next + /// + getProgNext(); +#else + String getProgNext(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getPhoneHotline() } + /// Telephone number of the radio station's hotline. + /// + /// @return Phone Hotline + /// + getPhoneHotline(); +#else + String getPhoneHotline(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getEMailHotline() } + /// Email address of the radio station's studio. + /// + /// @return EMail Hotline + /// + getEMailHotline(); +#else + String getEMailHotline(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getPhoneStudio() } + /// Telephone number of the radio station's studio. + /// + /// @return Phone Studio + /// + getPhoneStudio(); +#else + String getPhoneStudio(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getEMailStudio() } + /// Email address of radio station studio. + /// + /// @return EMail Studio + /// + getEMailStudio(); +#else + String getEMailStudio(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// @ingroup python_InfoTagRadioRDS + /// @brief \python_func{ getSMSStudio() } + /// SMS (Text Messaging) number for studio. + /// + /// @return SMS Studio + /// + getSMSStudio(); +#else + String getSMSStudio(); +#endif + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/InfoTagVideo.cpp b/xbmc/interfaces/legacy/InfoTagVideo.cpp new file mode 100644 index 0000000..8bc5fd1 --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagVideo.cpp @@ -0,0 +1,1063 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "InfoTagVideo.h" + +#include "AddonUtils.h" +#include "ServiceBroker.h" +#include "interfaces/legacy/Exception.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <utility> + +namespace XBMCAddon +{ + namespace xbmc + { + Actor::Actor(const String& name /* = emptyString */, + const String& role /* = emptyString */, + int order /* = -1 */, + const String& thumbnail /* = emptyString */) + : m_name(name), m_role(role), m_order(order), m_thumbnail(thumbnail) + { + if (m_name.empty()) + throw WrongTypeException("Actor: name property must not be empty"); + } + + SActorInfo Actor::ToActorInfo() const + { + SActorInfo actorInfo; + actorInfo.strName = m_name; + actorInfo.strRole = m_role; + actorInfo.order = m_order; + actorInfo.thumbUrl = CScraperUrl(m_thumbnail); + if (!actorInfo.thumbUrl.GetFirstThumbUrl().empty()) + actorInfo.thumb = CScraperUrl::GetThumbUrl(actorInfo.thumbUrl.GetFirstUrlByType()); + + return actorInfo; + } + + VideoStreamDetail::VideoStreamDetail(int width /* = 0 */, + int height /* = 0 */, + float aspect /* = 0.0f */, + int duration /* = 0 */, + const String& codec /* = emptyString */, + const String& stereoMode /* = emptyString */, + const String& language /* = emptyString */, + const String& hdrType /* = emptyString */) + : m_width(width), + m_height(height), + m_aspect(aspect), + m_duration(duration), + m_codec(codec), + m_stereoMode(stereoMode), + m_language(language), + m_hdrType(hdrType) + { + } + + CStreamDetailVideo* VideoStreamDetail::ToStreamDetailVideo() const + { + auto streamDetail = new CStreamDetailVideo(); + streamDetail->m_iWidth = m_width; + streamDetail->m_iHeight = m_height; + streamDetail->m_fAspect = m_aspect; + streamDetail->m_iDuration = m_duration; + streamDetail->m_strCodec = m_codec; + streamDetail->m_strStereoMode = m_stereoMode; + streamDetail->m_strLanguage = m_language; + streamDetail->m_strHdrType = m_hdrType; + + return streamDetail; + } + + AudioStreamDetail::AudioStreamDetail(int channels /* = -1 */, + const String& codec /* = emptyString */, + const String& language /* = emptyString */) + : m_channels(channels), m_codec(codec), m_language(language) + { + } + + CStreamDetailAudio* AudioStreamDetail::ToStreamDetailAudio() const + { + auto streamDetail = new CStreamDetailAudio(); + streamDetail->m_iChannels = m_channels; + streamDetail->m_strCodec = m_codec; + streamDetail->m_strLanguage = m_language; + + return streamDetail; + } + + SubtitleStreamDetail::SubtitleStreamDetail(const String& language /* = emptyString */) + : m_language(language) + { + } + + CStreamDetailSubtitle* SubtitleStreamDetail::ToStreamDetailSubtitle() const + { + auto streamDetail = new CStreamDetailSubtitle(); + streamDetail->m_strLanguage = m_language; + + return streamDetail; + } + + InfoTagVideo::InfoTagVideo(bool offscreen /* = false */) + : infoTag(new CVideoInfoTag), offscreen(offscreen), owned(true) + { + } + + InfoTagVideo::InfoTagVideo(const CVideoInfoTag* tag) + : infoTag(new CVideoInfoTag(*tag)), offscreen(true), owned(true) + { + } + + InfoTagVideo::InfoTagVideo(CVideoInfoTag* tag, bool offscreen /* = false */) + : infoTag(tag), offscreen(offscreen), owned(false) + { + } + + InfoTagVideo::~InfoTagVideo() + { + if (owned) + delete infoTag; + } + + int InfoTagVideo::getDbId() + { + return infoTag->m_iDbId; + } + + String InfoTagVideo::getDirector() + { + return StringUtils::Join(infoTag->m_director, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + + std::vector<String> InfoTagVideo::getDirectors() + { + return infoTag->m_director; + } + + String InfoTagVideo::getWritingCredits() + { + return StringUtils::Join(infoTag->m_writingCredits, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + + std::vector<String> InfoTagVideo::getWriters() + { + return infoTag->m_writingCredits; + } + + String InfoTagVideo::getGenre() + { + return StringUtils::Join(infoTag->m_genre, CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + + std::vector<String> InfoTagVideo::getGenres() + { + return infoTag->m_genre; + } + + String InfoTagVideo::getTagLine() + { + return infoTag->m_strTagLine; + } + + String InfoTagVideo::getPlotOutline() + { + return infoTag->m_strPlotOutline; + } + + String InfoTagVideo::getPlot() + { + return infoTag->m_strPlot; + } + + String InfoTagVideo::getPictureURL() + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + infoTag->m_strPictureURL.Parse(); + return infoTag->m_strPictureURL.GetFirstThumbUrl(); + } + + String InfoTagVideo::getTVShowTitle() + { + return infoTag->m_strShowTitle; + } + + String InfoTagVideo::getTitle() + { + return infoTag->m_strTitle; + } + + String InfoTagVideo::getMediaType() + { + return infoTag->m_type; + } + + String InfoTagVideo::getVotes() + { + CLog::Log( + LOGWARNING, + "InfoTagVideo.getVotes() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.getVotesAsInt()."); + + return std::to_string(getVotesAsInt()); + } + + int InfoTagVideo::getVotesAsInt(const String& type /* = "" */) + { + return infoTag->GetRating(type).votes; + } + + String InfoTagVideo::getCast() + { + return infoTag->GetCast(true); + } + + std::vector<Actor*> InfoTagVideo::getActors() + { + std::vector<Actor*> actors; + actors.reserve(infoTag->m_cast.size()); + + for (const auto& cast : infoTag->m_cast) + actors.push_back(new Actor(cast.strName, cast.strRole, cast.order, cast.thumbUrl.GetFirstUrlByType().m_url)); + + return actors; + } + + String InfoTagVideo::getFile() + { + return infoTag->m_strFile; + } + + String InfoTagVideo::getPath() + { + return infoTag->m_strPath; + } + + String InfoTagVideo::getFilenameAndPath() + { + return infoTag->m_strFileNameAndPath; + } + + String InfoTagVideo::getIMDBNumber() + { + return infoTag->GetUniqueID(); + } + + int InfoTagVideo::getSeason() + { + return infoTag->m_iSeason; + } + + int InfoTagVideo::getEpisode() + { + return infoTag->m_iEpisode; + } + + int InfoTagVideo::getYear() + { + return infoTag->GetYear(); + } + + double InfoTagVideo::getRating(const String& type /* = "" */) + { + return static_cast<double>(infoTag->GetRating(type).rating); + } + + int InfoTagVideo::getUserRating() + { + return infoTag->m_iUserRating; + } + + int InfoTagVideo::getPlayCount() + { + return infoTag->GetPlayCount(); + } + + String InfoTagVideo::getLastPlayed() + { + CLog::Log(LOGWARNING, "InfoTagVideo.getLastPlayed() is deprecated and might be removed in " + "future Kodi versions. Please use InfoTagVideo.getLastPlayedAsW3C()."); + + return infoTag->m_lastPlayed.GetAsLocalizedDateTime(); + } + + String InfoTagVideo::getLastPlayedAsW3C() + { + return infoTag->m_lastPlayed.GetAsW3CDateTime(); + } + + String InfoTagVideo::getOriginalTitle() + { + return infoTag->m_strOriginalTitle; + } + + String InfoTagVideo::getPremiered() + { + CLog::Log(LOGWARNING, "InfoTagVideo.getPremiered() is deprecated and might be removed in " + "future Kodi versions. Please use InfoTagVideo.getPremieredAsW3C()."); + + return infoTag->GetPremiered().GetAsLocalizedDate(); + } + + String InfoTagVideo::getPremieredAsW3C() + { + return infoTag->GetPremiered().GetAsW3CDate(); + } + + String InfoTagVideo::getFirstAired() + { + CLog::Log(LOGWARNING, "InfoTagVideo.getFirstAired() is deprecated and might be removed in " + "future Kodi versions. Please use InfoTagVideo.getFirstAiredAsW3C()."); + + return infoTag->m_firstAired.GetAsLocalizedDate(); + } + + String InfoTagVideo::getFirstAiredAsW3C() + { + return infoTag->m_firstAired.GetAsW3CDate(); + } + + String InfoTagVideo::getTrailer() + { + return infoTag->m_strTrailer; + } + + std::vector<std::string> InfoTagVideo::getArtist() + { + return infoTag->m_artist; + } + + String InfoTagVideo::getAlbum() + { + return infoTag->m_strAlbum; + } + + int InfoTagVideo::getTrack() + { + return infoTag->m_iTrack; + } + + unsigned int InfoTagVideo::getDuration() + { + return infoTag->GetDuration(); + } + + double InfoTagVideo::getResumeTime() + { + return infoTag->GetResumePoint().timeInSeconds; + } + + double InfoTagVideo::getResumeTimeTotal() + { + return infoTag->GetResumePoint().totalTimeInSeconds; + } + + String InfoTagVideo::getUniqueID(const char* key) + { + return infoTag->GetUniqueID(key); + } + + void InfoTagVideo::setUniqueID(const String& uniqueID, + const String& type /* = "" */, + bool isDefault /* = false */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setUniqueIDRaw(infoTag, uniqueID, type, isDefault); + } + + void InfoTagVideo::setUniqueIDs(const std::map<String, String>& uniqueIDs, + const String& defaultUniqueID /* = "" */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setUniqueIDsRaw(infoTag, uniqueIDs, defaultUniqueID); + } + + void InfoTagVideo::setDbId(int dbId) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDbIdRaw(infoTag, dbId); + } + + void InfoTagVideo::setYear(int year) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setYearRaw(infoTag, year); + } + + void InfoTagVideo::setEpisode(int episode) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setEpisodeRaw(infoTag, episode); + } + + void InfoTagVideo::setSeason(int season) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSeasonRaw(infoTag, season); + } + + void InfoTagVideo::setSortEpisode(int sortEpisode) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSortEpisodeRaw(infoTag, sortEpisode); + } + + void InfoTagVideo::setSortSeason(int sortSeason) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSortSeasonRaw(infoTag, sortSeason); + } + + void InfoTagVideo::setEpisodeGuide(const String& episodeGuide) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setEpisodeGuideRaw(infoTag, episodeGuide); + } + + void InfoTagVideo::setTop250(int top250) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTop250Raw(infoTag, top250); + } + + void InfoTagVideo::setSetId(int setId) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSetIdRaw(infoTag, setId); + } + + void InfoTagVideo::setTrackNumber(int trackNumber) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTrackNumberRaw(infoTag, trackNumber); + } + + void InfoTagVideo::setRating(float rating, + int votes /* = 0 */, + const String& type /* = "" */, + bool isDefault /* = false */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setRatingRaw(infoTag, rating, votes, type, isDefault); + } + + void InfoTagVideo::setRatings(const std::map<String, Tuple<float, int>>& ratings, + const String& defaultRating /* = "" */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setRatingsRaw(infoTag, ratings, defaultRating); + } + + void InfoTagVideo::setUserRating(int userRating) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setUserRatingRaw(infoTag, userRating); + } + + void InfoTagVideo::setPlaycount(int playcount) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPlaycountRaw(infoTag, playcount); + } + + void InfoTagVideo::setMpaa(const String& mpaa) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setMpaaRaw(infoTag, mpaa); + } + + void InfoTagVideo::setPlot(const String& plot) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPlotRaw(infoTag, plot); + } + + void InfoTagVideo::setPlotOutline(const String& plotOutline) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPlotOutlineRaw(infoTag, plotOutline); + } + + void InfoTagVideo::setTitle(const String& title) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTitleRaw(infoTag, title); + } + + void InfoTagVideo::setOriginalTitle(const String& originalTitle) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setOriginalTitleRaw(infoTag, originalTitle); + } + + void InfoTagVideo::setSortTitle(const String& sortTitle) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSortTitleRaw(infoTag, sortTitle); + } + + void InfoTagVideo::setTagLine(const String& tagLine) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTagLineRaw(infoTag, tagLine); + } + + void InfoTagVideo::setTvShowTitle(const String& tvshowTitle) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTvShowTitleRaw(infoTag, tvshowTitle); + } + + void InfoTagVideo::setTvShowStatus(const String& tvshowStatus) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTvShowStatusRaw(infoTag, tvshowStatus); + } + + void InfoTagVideo::setGenres(std::vector<String> genre) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setGenresRaw(infoTag, std::move(genre)); + } + + void InfoTagVideo::setCountries(std::vector<String> countries) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setCountriesRaw(infoTag, std::move(countries)); + } + + void InfoTagVideo::setDirectors(std::vector<String> directors) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDirectorsRaw(infoTag, std::move(directors)); + } + + void InfoTagVideo::setStudios(std::vector<String> studios) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setStudiosRaw(infoTag, std::move(studios)); + } + + void InfoTagVideo::setWriters(std::vector<String> writers) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setWritersRaw(infoTag, std::move(writers)); + } + + void InfoTagVideo::setDuration(int duration) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDurationRaw(infoTag, duration); + } + + void InfoTagVideo::setPremiered(const String& premiered) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPremieredRaw(infoTag, premiered); + } + + void InfoTagVideo::setSet(const String& set) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSetRaw(infoTag, set); + } + + void InfoTagVideo::setSetOverview(const String& setOverview) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setSetOverviewRaw(infoTag, setOverview); + } + + void InfoTagVideo::setTags(std::vector<String> tags) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTagsRaw(infoTag, std::move(tags)); + } + + void InfoTagVideo::setProductionCode(const String& productionCode) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setProductionCodeRaw(infoTag, productionCode); + } + + void InfoTagVideo::setFirstAired(const String& firstAired) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setFirstAiredRaw(infoTag, firstAired); + } + + void InfoTagVideo::setLastPlayed(const String& lastPlayed) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setLastPlayedRaw(infoTag, lastPlayed); + } + + void InfoTagVideo::setAlbum(const String& album) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setAlbumRaw(infoTag, album); + } + + void InfoTagVideo::setVotes(int votes) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setVotesRaw(infoTag, votes); + } + + void InfoTagVideo::setTrailer(const String& trailer) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setTrailerRaw(infoTag, trailer); + } + + void InfoTagVideo::setPath(const String& path) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setPathRaw(infoTag, path); + } + + void InfoTagVideo::setFilenameAndPath(const String& filenameAndPath) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setFilenameAndPathRaw(infoTag, filenameAndPath); + } + + void InfoTagVideo::setIMDBNumber(const String& imdbNumber) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setIMDBNumberRaw(infoTag, imdbNumber); + } + + void InfoTagVideo::setDateAdded(const String& dateAdded) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setDateAddedRaw(infoTag, dateAdded); + } + + void InfoTagVideo::setMediaType(const String& mediaType) + { + setMediaTypeRaw(infoTag, mediaType); + } + + void InfoTagVideo::setShowLinks(std::vector<String> showLinks) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setShowLinksRaw(infoTag, std::move(showLinks)); + } + + void InfoTagVideo::setArtists(std::vector<String> artists) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setArtistsRaw(infoTag, std::move(artists)); + } + + void InfoTagVideo::setCast(const std::vector<const Actor*>& actors) + { + std::vector<SActorInfo> cast; + cast.reserve(actors.size()); + for (const auto& actor : actors) + cast.push_back(actor->ToActorInfo()); + + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setCastRaw(infoTag, std::move(cast)); + } + } + + void InfoTagVideo::setResumePoint(double time, double totalTime /* = 0.0 */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + setResumePointRaw(infoTag, time, totalTime); + } + + void InfoTagVideo::addSeason(int number, std::string name /* = "" */) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addSeasonRaw(infoTag, number, std::move(name)); + } + + void InfoTagVideo::addSeasons(const std::vector<Tuple<int, std::string>>& namedSeasons) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addSeasonsRaw(infoTag, namedSeasons); + } + + void InfoTagVideo::addVideoStream(const VideoStreamDetail* stream) + { + if (stream == nullptr) + return; + + auto streamDetail = stream->ToStreamDetailVideo(); + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addStreamRaw(infoTag, streamDetail); + } + } + + void InfoTagVideo::addAudioStream(const AudioStreamDetail* stream) + { + if (stream == nullptr) + return; + + auto streamDetail = stream->ToStreamDetailAudio(); + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addStreamRaw(infoTag, streamDetail); + } + } + + void InfoTagVideo::addSubtitleStream(const SubtitleStreamDetail* stream) + { + if (stream == nullptr) + return; + + auto streamDetail = stream->ToStreamDetailSubtitle(); + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addStreamRaw(infoTag, streamDetail); + } + } + + void InfoTagVideo::addAvailableArtwork(const std::string& url, + const std::string& art_type, + const std::string& preview, + const std::string& referrer, + const std::string& cache, + bool post, + bool isgz, + int season) + { + XBMCAddonUtils::GuiLock lock(languageHook, offscreen); + addAvailableArtworkRaw(infoTag, url, art_type, preview, referrer, cache, post, isgz, season); + } + + void InfoTagVideo::setDbIdRaw(CVideoInfoTag* infoTag, int dbId) + { + infoTag->m_iDbId = dbId; + } + + void InfoTagVideo::setUniqueIDRaw(CVideoInfoTag* infoTag, + const String& uniqueID, + const String& type /* = "" */, + bool isDefault /* = false */) + { + infoTag->SetUniqueID(uniqueID, type, isDefault); + } + + void InfoTagVideo::setUniqueIDsRaw(CVideoInfoTag* infoTag, + std::map<String, String> uniqueIDs, + const String& defaultUniqueID /* = "" */) + { + infoTag->SetUniqueIDs(uniqueIDs); + auto defaultUniqueIDEntry = uniqueIDs.find(defaultUniqueID); + if (defaultUniqueIDEntry != uniqueIDs.end()) + infoTag->SetUniqueID(defaultUniqueIDEntry->second, defaultUniqueIDEntry->first, true); + } + + void InfoTagVideo::setYearRaw(CVideoInfoTag* infoTag, int year) + { + infoTag->SetYear(year); + } + + void InfoTagVideo::setEpisodeRaw(CVideoInfoTag* infoTag, int episode) + { + infoTag->m_iEpisode = episode; + } + + void InfoTagVideo::setSeasonRaw(CVideoInfoTag* infoTag, int season) + { + infoTag->m_iSeason = season; + } + + void InfoTagVideo::setSortEpisodeRaw(CVideoInfoTag* infoTag, int sortEpisode) + { + infoTag->m_iSpecialSortEpisode = sortEpisode; + } + + void InfoTagVideo::setSortSeasonRaw(CVideoInfoTag* infoTag, int sortSeason) + { + infoTag->m_iSpecialSortSeason = sortSeason; + } + + void InfoTagVideo::setEpisodeGuideRaw(CVideoInfoTag* infoTag, const String& episodeGuide) + { + infoTag->SetEpisodeGuide(episodeGuide); + } + + void InfoTagVideo::setTop250Raw(CVideoInfoTag* infoTag, int top250) + { + infoTag->m_iTop250 = top250; + } + + void InfoTagVideo::setSetIdRaw(CVideoInfoTag* infoTag, int setId) + { + infoTag->m_set.id = setId; + } + + void InfoTagVideo::setTrackNumberRaw(CVideoInfoTag* infoTag, int trackNumber) + { + infoTag->m_iTrack = trackNumber; + } + + void InfoTagVideo::setRatingRaw(CVideoInfoTag* infoTag, + float rating, + int votes /* = 0 */, + const std::string& type /* = "" */, + bool isDefault /* = false */) + { + infoTag->SetRating(rating, votes, type, isDefault); + } + + void InfoTagVideo::setRatingsRaw(CVideoInfoTag* infoTag, + const std::map<String, Tuple<float, int>>& ratings, + const String& defaultRating /* = "" */) + { + RatingMap ratingMap; + for (const auto& rating : ratings) + ratingMap.emplace(rating.first, CRating{rating.second.first(), rating.second.second()}); + + infoTag->SetRatings(std::move(ratingMap), defaultRating); + } + + void InfoTagVideo::setUserRatingRaw(CVideoInfoTag* infoTag, int userRating) + { + infoTag->m_iUserRating = userRating; + } + + void InfoTagVideo::setPlaycountRaw(CVideoInfoTag* infoTag, int playcount) + { + infoTag->SetPlayCount(playcount); + } + + void InfoTagVideo::setMpaaRaw(CVideoInfoTag* infoTag, const String& mpaa) + { + infoTag->SetMPAARating(mpaa); + } + + void InfoTagVideo::setPlotRaw(CVideoInfoTag* infoTag, const String& plot) + { + infoTag->SetPlot(plot); + } + + void InfoTagVideo::setPlotOutlineRaw(CVideoInfoTag* infoTag, const String& plotOutline) + { + infoTag->SetPlotOutline(plotOutline); + } + + void InfoTagVideo::setTitleRaw(CVideoInfoTag* infoTag, const String& title) + { + infoTag->SetTitle(title); + } + + void InfoTagVideo::setOriginalTitleRaw(CVideoInfoTag* infoTag, const String& originalTitle) + { + infoTag->SetOriginalTitle(originalTitle); + } + + void InfoTagVideo::setSortTitleRaw(CVideoInfoTag* infoTag, const String& sortTitle) + { + infoTag->SetSortTitle(sortTitle); + } + + void InfoTagVideo::setTagLineRaw(CVideoInfoTag* infoTag, const String& tagLine) + { + infoTag->SetTagLine(tagLine); + } + + void InfoTagVideo::setTvShowTitleRaw(CVideoInfoTag* infoTag, const String& tvshowTitle) + { + infoTag->SetShowTitle(tvshowTitle); + } + + void InfoTagVideo::setTvShowStatusRaw(CVideoInfoTag* infoTag, const String& tvshowStatus) + { + infoTag->SetStatus(tvshowStatus); + } + + void InfoTagVideo::setGenresRaw(CVideoInfoTag* infoTag, std::vector<String> genre) + { + infoTag->SetGenre(std::move(genre)); + } + + void InfoTagVideo::setCountriesRaw(CVideoInfoTag* infoTag, std::vector<String> countries) + { + infoTag->SetCountry(std::move(countries)); + } + + void InfoTagVideo::setDirectorsRaw(CVideoInfoTag* infoTag, std::vector<String> directors) + { + infoTag->SetDirector(std::move(directors)); + } + + void InfoTagVideo::setStudiosRaw(CVideoInfoTag* infoTag, std::vector<String> studios) + { + infoTag->SetStudio(std::move(studios)); + } + + void InfoTagVideo::setWritersRaw(CVideoInfoTag* infoTag, std::vector<String> writers) + { + infoTag->SetWritingCredits(std::move(writers)); + } + + void InfoTagVideo::setDurationRaw(CVideoInfoTag* infoTag, int duration) + { + infoTag->SetDuration(duration); + } + + void InfoTagVideo::setPremieredRaw(CVideoInfoTag* infoTag, const String& premiered) + { + CDateTime premieredDate; + premieredDate.SetFromDateString(premiered); + infoTag->SetPremiered(premieredDate); + } + + void InfoTagVideo::setSetRaw(CVideoInfoTag* infoTag, const String& set) + { + infoTag->SetSet(set); + } + + void InfoTagVideo::setSetOverviewRaw(CVideoInfoTag* infoTag, const String& setOverview) + { + infoTag->SetSetOverview(setOverview); + } + + void InfoTagVideo::setTagsRaw(CVideoInfoTag* infoTag, std::vector<String> tags) + { + infoTag->SetTags(std::move(tags)); + } + + void InfoTagVideo::setProductionCodeRaw(CVideoInfoTag* infoTag, const String& productionCode) + { + infoTag->SetProductionCode(productionCode); + } + + void InfoTagVideo::setFirstAiredRaw(CVideoInfoTag* infoTag, const String& firstAired) + { + CDateTime firstAiredDate; + firstAiredDate.SetFromDateString(firstAired); + infoTag->m_firstAired = firstAiredDate; + } + + void InfoTagVideo::setLastPlayedRaw(CVideoInfoTag* infoTag, const String& lastPlayed) + { + CDateTime lastPlayedDate; + lastPlayedDate.SetFromDBDateTime(lastPlayed); + infoTag->m_lastPlayed = lastPlayedDate; + } + + void InfoTagVideo::setAlbumRaw(CVideoInfoTag* infoTag, const String& album) + { + infoTag->SetAlbum(album); + } + + void InfoTagVideo::setVotesRaw(CVideoInfoTag* infoTag, int votes) + { + infoTag->SetVotes(votes); + } + + void InfoTagVideo::setTrailerRaw(CVideoInfoTag* infoTag, const String& trailer) + { + infoTag->SetTrailer(trailer); + } + + void InfoTagVideo::setPathRaw(CVideoInfoTag* infoTag, const String& path) + { + infoTag->SetPath(path); + } + + void InfoTagVideo::setFilenameAndPathRaw(CVideoInfoTag* infoTag, const String& filenameAndPath) + { + infoTag->SetFileNameAndPath(filenameAndPath); + } + + void InfoTagVideo::setIMDBNumberRaw(CVideoInfoTag* infoTag, const String& imdbNumber) + { + infoTag->SetUniqueID(imdbNumber); + } + + void InfoTagVideo::setDateAddedRaw(CVideoInfoTag* infoTag, const String& dateAdded) + { + CDateTime dateAddedDate; + dateAddedDate.SetFromDBDateTime(dateAdded); + infoTag->m_dateAdded = dateAddedDate; + } + + void InfoTagVideo::setMediaTypeRaw(CVideoInfoTag* infoTag, const String& mediaType) + { + if (CMediaTypes::IsValidMediaType(mediaType)) + infoTag->m_type = mediaType; + } + + void InfoTagVideo::setShowLinksRaw(CVideoInfoTag* infoTag, std::vector<String> showLinks) + { + infoTag->SetShowLink(std::move(showLinks)); + } + + void InfoTagVideo::setArtistsRaw(CVideoInfoTag* infoTag, std::vector<String> artists) + { + infoTag->m_artist = std::move(artists); + } + + void InfoTagVideo::setCastRaw(CVideoInfoTag* infoTag, std::vector<SActorInfo> cast) + { + infoTag->m_cast = std::move(cast); + } + + void InfoTagVideo::setResumePointRaw(CVideoInfoTag* infoTag, + double time, + double totalTime /* = 0.0 */) + { + auto resumePoint = infoTag->GetResumePoint(); + resumePoint.timeInSeconds = time; + if (totalTime > 0.0) + resumePoint.totalTimeInSeconds = totalTime; + infoTag->SetResumePoint(resumePoint); + } + + void InfoTagVideo::addSeasonRaw(CVideoInfoTag* infoTag, int number, std::string name /* = "" */) + { + infoTag->m_namedSeasons[number] = std::move(name); + } + + void InfoTagVideo::addSeasonsRaw(CVideoInfoTag* infoTag, + const std::vector<Tuple<int, std::string>>& namedSeasons) + { + for (const auto& season : namedSeasons) + addSeasonRaw(infoTag, season.first(), season.second()); + } + + void InfoTagVideo::addStreamRaw(CVideoInfoTag* infoTag, CStreamDetail* stream) + { + infoTag->m_streamDetails.AddStream(stream); + } + + void InfoTagVideo::finalizeStreamsRaw(CVideoInfoTag* infoTag) + { + infoTag->m_streamDetails.DetermineBestStreams(); + } + + void InfoTagVideo::addAvailableArtworkRaw(CVideoInfoTag* infoTag, + const std::string& url, + const std::string& art_type, + const std::string& preview, + const std::string& referrer, + const std::string& cache, + bool post, + bool isgz, + int season) + { + infoTag->m_strPictureURL.AddParsedUrl(url, art_type, preview, referrer, cache, post, isgz, + season); + } + } +} diff --git a/xbmc/interfaces/legacy/InfoTagVideo.h b/xbmc/interfaces/legacy/InfoTagVideo.h new file mode 100644 index 0000000..91f925e --- /dev/null +++ b/xbmc/interfaces/legacy/InfoTagVideo.h @@ -0,0 +1,2772 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "Tuple.h" +#include "utils/StreamDetails.h" +#include "video/VideoInfoTag.h" + +namespace XBMCAddon +{ + namespace xbmc + { + /// + /// \defgroup python_xbmc_actor Actor + /// \ingroup python_xbmc + /// @{ + /// @brief **Actor class used in combination with InfoTagVideo.** + /// + /// \python_class{ xbmc.Actor([name, role, order, thumbnail]) } + /// + /// Represents a single actor in the cast of a video item wrapped by InfoTagVideo. + /// + /// + ///------------------------------------------------------------------------- + /// @python_v20 New class added. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// actor = xbmc.Actor('Sean Connery', 'James Bond', order=1) + /// ... + /// ~~~~~~~~~~~~~ + /// + class Actor : public AddonClass + { + public: +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor Actor + /// @brief \python_func{ xbmc.Actor([name, role, order, thumbnail]) } + /// Creates a single actor for the cast of a video item wrapped by InfoTagVideo. + /// + /// @param name [opt] string - Name of the actor. + /// @param role [opt] string - Role of the actor in the specific video item. + /// @param order [opt] integer - Order of the actor in the cast of the specific video item. + /// @param thumbnail [opt] string - Path / URL to the thumbnail of the actor. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// actor = xbmc.Actor('Sean Connery', 'James Bond', order=1) + /// ... + /// ~~~~~~~~~~~~~ + /// + Actor(...); +#else + explicit Actor(const String& name = emptyString, + const String& role = emptyString, + int order = -1, + const String& thumbnail = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ getName() } + /// Get the name of the actor. + /// + /// @return [string] Name of the actor + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getName(); +#else + String getName() const { return m_name; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ getRole() } + /// Get the role of the actor in the specific video item. + /// + /// @return [string] Role of the actor in the specific video item + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getRole(); +#else + String getRole() const { return m_role; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ getOrder() } + /// Get the order of the actor in the cast of the specific video item. + /// + /// @return [integer] Order of the actor in the cast of the specific video item + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getOrder(); +#else + int getOrder() const { return m_order; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ getThumbnail() } + /// Get the path / URL to the thumbnail of the actor. + /// + /// @return [string] Path / URL to the thumbnail of the actor + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getThumbnail(); +#else + String getThumbnail() const { return m_thumbnail; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ setName(name) } + /// Set the name of the actor. + /// + /// @param name string - Name of the actor. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setName(...); +#else + void setName(const String& name) { m_name = name; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ setRole(role) } + /// Set the role of the actor in the specific video item. + /// + /// @param role string - Role of the actor in the specific video item. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setRole(...); +#else + void setRole(const String& role) { m_role = role; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ setOrder(order) } + /// Set the order of the actor in the cast of the specific video item. + /// + /// @param order integer - Order of the actor in the cast of the specific video item. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setOrder(...); +#else + void setOrder(int order) { m_order = order; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_actor + /// @brief \python_func{ setThumbnail(thumbnail) } + /// Set the path / URL to the thumbnail of the actor. + /// + /// @param thumbnail string - Path / URL to the thumbnail of the actor. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setThumbnail(...); +#else + void setThumbnail(const String& thumbnail) { m_thumbnail = thumbnail; } +#endif + +#ifndef SWIG + SActorInfo ToActorInfo() const; +#endif + + private: + String m_name; + String m_role; + int m_order; + String m_thumbnail; + }; + /// @} + + /// + /// \defgroup python_xbmc_videostreamdetail VideoStreamDetail + /// \ingroup python_xbmc + /// @{ + /// @brief **Video stream details class used in combination with InfoTagVideo.** + /// + /// \python_class{ xbmc.VideoStreamDetail([width, height, aspect, duration, codec, stereoMode, language, hdrType]) } + /// + /// Represents a single selectable video stream for a video item wrapped by InfoTagVideo. + /// + /// + ///------------------------------------------------------------------------- + /// @python_v20 New class added. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// videostream = xbmc.VideoStreamDetail(1920, 1080, language='English') + /// ... + /// ~~~~~~~~~~~~~ + /// + class VideoStreamDetail : public AddonClass + { + public: +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ xbmc.VideoStreamDetail([width, height, aspect, duration, codec, stereomode, language, hdrtype]) } + /// Creates a single video stream details class for a video item wrapped by InfoTagVideo. + /// + /// @param width [opt] integer - Width of the video stream in pixel. + /// @param height [opt] integer - Height of the video stream in pixel. + /// @param aspect [opt] float - Aspect ratio of the video stream. + /// @param duration [opt] integer - Duration of the video stream in seconds. + /// @param codec [opt] string - Codec of the video stream. + /// @param stereomode [opt] string - Stereo mode of the video stream. + /// @param language [opt] string - Language of the video stream. + /// @param hdrtype [opt] string - HDR type of the video stream. + /// The following types are supported: + /// dolbyvision, hdr10, hlg + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// videostream = xbmc.VideoStreamDetail(1920, 1080, language='English') + /// ... + /// ~~~~~~~~~~~~~ + /// + VideoStreamDetail(...); +#else + explicit VideoStreamDetail(int width = 0, + int height = 0, + float aspect = 0.0f, + int duration = 0, + const String& codec = emptyString, + const String& stereomode = emptyString, + const String& language = emptyString, + const String& hdrtype = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getWidth() } + /// Get the width of the video stream in pixel. + /// + /// @return [integer] Width of the video stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getWidth(); +#else + int getWidth() const { return m_width; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getHeight() } + /// Get the height of the video stream in pixel. + /// + /// @return [integer] Height of the video stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getHeight(); +#else + int getHeight() const { return m_height; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getAspect() } + /// Get the aspect ratio of the video stream. + /// + /// @return [float] Aspect ratio of the video stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getAspect(); +#else + float getAspect() const { return m_aspect; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getDuration() } + /// Get the duration of the video stream in seconds. + /// + /// @return [float] Duration of the video stream in seconds + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getDuration(); +#else + int getDuration() const { return m_duration; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getCodec() } + /// Get the codec of the stream. + /// + /// @return [string] Codec of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getCodec(); +#else + String getCodec() const { return m_codec; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getStereoMode() } + /// Get the stereo mode of the video stream. + /// + /// @return [string] Stereo mode of the video stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getStereoMode(); +#else + String getStereoMode() const { return m_stereoMode; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getLanguage() } + /// Get the language of the stream. + /// + /// @return [string] Language of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getLanguage(); +#else + String getLanguage() const { return m_language; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ getHDRType() } + /// Get the HDR type of the stream. + /// + /// @return [string] HDR type of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getHDRType(); +#else + String getHDRType() const { return m_hdrType; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setWidth(width) } + /// Set the width of the video stream in pixel. + /// + /// @param width integer - Width of the video stream in pixel. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setWidth(...); +#else + void setWidth(int width) { m_width = width; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setHeight(height) } + /// Set the height of the video stream in pixel. + /// + /// @param height integer - Height of the video stream in pixel. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setHeight(...); +#else + void setHeight(int height) { m_height = height; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setAspect(aspect) } + /// Set the aspect ratio of the video stream. + /// + /// @param aspect float - Aspect ratio of the video stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setAspect(...); +#else + void setAspect(float aspect) { m_aspect = aspect; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setDuration(duration) } + /// Set the duration of the video stream in seconds. + /// + /// @param duration integer - Duration of the video stream in seconds. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDuration(...); +#else + void setDuration(int duration) { m_duration = duration; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setCodec(codec) } + /// Set the codec of the stream. + /// + /// @param codec string - Codec of the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setCodec(...); +#else + void setCodec(const String& codec) { m_codec = codec; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setStereoMode(stereomode) } + /// Set the stereo mode of the video stream. + /// + /// @param stereomode string - Stereo mode of the video stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setStereoMode(...); +#else + void setStereoMode(const String& stereomode) { m_stereoMode = stereomode; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setLanguage(language) } + /// Set the language of the stream. + /// + /// @param language string - Language of the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLanguage(...); +#else + void setLanguage(const String& language) { m_language = language; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_videostreamdetail + /// @brief \python_func{ setHDRType(hdrtype) } + /// Set the HDR type of the stream. + /// + /// @param hdrtype string - HDR type of the stream. + /// The following types are supported: + /// dolbyvision, hdr10, hlg + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setHDRType(...); +#else + void setHDRType(const String& hdrtype) { m_hdrType = hdrtype; } +#endif + +#ifndef SWIG + CStreamDetailVideo* ToStreamDetailVideo() const; +#endif + + private: + int m_width; + int m_height; + float m_aspect; + int m_duration; + String m_codec; + String m_stereoMode; + String m_language; + String m_hdrType; + }; + /// @} + + /// + /// \defgroup python_xbmc_audiostreamdetail AudioStreamDetail + /// \ingroup python_xbmc + /// @{ + /// @brief **Audio stream details class used in combination with InfoTagVideo.** + /// + /// \python_class{ xbmc.AudioStreamDetail([channels, codec, language]) } + /// + /// Represents a single selectable audio stream for a video item wrapped by InfoTagVideo. + /// + /// + ///------------------------------------------------------------------------- + /// @python_v20 New class added. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// audiostream = xbmc.AudioStreamDetail(6, 'DTS', 'English') + /// ... + /// ~~~~~~~~~~~~~ + /// + class AudioStreamDetail : public AddonClass + { + public: +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail AudioStreamDetail + /// @brief \python_func{ xbmc.AudioStreamDetail([channels, codec, language]) } + /// Creates a single audio stream details class for a video item wrapped by InfoTagVideo. + /// + /// @param channels [opt] integer - Number of channels in the audio stream. + /// @param codec [opt] string - Codec of the audio stream. + /// @param language [opt] string - Language of the audio stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// audiostream = xbmc.AudioStreamDetail(6, 'DTS', 'English') + /// ... + /// ~~~~~~~~~~~~~ + /// + AudioStreamDetail(...); +#else + explicit AudioStreamDetail(int channels = -1, + const String& codec = emptyString, + const String& language = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ getChannels() } + /// Get the number of channels in the stream. + /// + /// @return [integer] Number of channels in the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getChannels(); +#else + int getChannels() const { return m_channels; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ getCodec() } + /// Get the codec of the stream. + /// + /// @return [string] Codec of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getCodec(); +#else + String getCodec() const { return m_codec; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ getLanguage() } + /// Get the language of the stream. + /// + /// @return [string] Language of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getLanguage(); +#else + String getLanguage() const { return m_language; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ setChannels(channels) } + /// Set the number of channels in the stream. + /// + /// @param channels integer - Number of channels in the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setChannels(...); +#else + void setChannels(int channels) { m_channels = channels; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ setCodec(codec) } + /// Set the codec of the stream. + /// + /// @param codec string - Codec of the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setCodec(...); +#else + void setCodec(const String& codec) { m_codec = codec; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_audiostreamdetail + /// @brief \python_func{ setLanguage(language) } + /// Set the language of the stream. + /// + /// @param language string - Language of the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLanguage(...); +#else + void setLanguage(const String& language) { m_language = language; } +#endif + +#ifndef SWIG + CStreamDetailAudio* ToStreamDetailAudio() const; +#endif + + private: + int m_channels; + String m_codec; + String m_language; + }; + /// @} + + /// + /// \defgroup python_xbmc_subtitlestreamdetail SubtitleStreamDetail + /// \ingroup python_xbmc + /// @{ + /// @brief **Subtitle stream details class used in combination with InfoTagVideo.** + /// + /// \python_class{ xbmc.SubtitleStreamDetail([language]) } + /// + /// Represents a single selectable subtitle stream for a video item wrapped by InfoTagVideo. + /// + /// + ///------------------------------------------------------------------------- + /// @python_v20 New class added. + /// + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// subtitlestream = xbmc.SubtitleStreamDetail('English') + /// ... + /// ~~~~~~~~~~~~~ + /// + class SubtitleStreamDetail : public AddonClass + { + public: +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_subtitlestreamdetail SubtitleStreamDetail + /// @brief \python_func{ xbmc.SubtitleStreamDetail([language]) } + /// Creates a single subtitle stream details class for a video item wrapped by InfoTagVideo. + /// + /// @param language [opt] string - Language of the subtitle. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// subtitlestream = xbmc.SubtitleStreamDetail('English') + /// ... + /// ~~~~~~~~~~~~~ + /// + SubtitleStreamDetail(...); +#else + explicit SubtitleStreamDetail(const String& language = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_subtitlestreamdetail + /// @brief \python_func{ getLanguage() } + /// Get the language of the stream. + /// + /// @return [string] Language of the stream + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getLanguage(); +#else + String getLanguage() const { return m_language; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_subtitlestreamdetail + /// @brief \python_func{ setLanguage(language) } + /// Set the language of the stream. + /// + /// @param language string - Language of the stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLanguage(...); +#else + void setLanguage(const String& language) { m_language = language; } +#endif + +#ifndef SWIG + CStreamDetailSubtitle* ToStreamDetailSubtitle() const; +#endif + + private: + String m_language; + }; + /// @} + + /// + /// \defgroup python_InfoTagVideo InfoTagVideo + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's video info tag class.** + /// + /// \python_class{ xbmc.InfoTagVideo([offscreen]) } + /// + /// Access and / or modify the video metadata of a ListItem. + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// tag = xbmc.Player().getVideoInfoTag() + /// + /// title = tag.getTitle() + /// file = tag.getFile() + /// ... + /// ~~~~~~~~~~~~~ + /// + class InfoTagVideo : public AddonClass + { + private: + CVideoInfoTag* infoTag; + bool offscreen; + bool owned; + + public: +#ifndef SWIG + explicit InfoTagVideo(const CVideoInfoTag* tag); + explicit InfoTagVideo(CVideoInfoTag* tag, bool offscreen = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ xbmc.InfoTagVideo([offscreen]) } + /// Create a video info tag. + /// + /// @param offscreen [opt] bool (default `False`) - if GUI based locks should be + /// avoided. Most of the times listitems are created + /// offscreen and added later to a container + /// for display (e.g. plugins) or they are not + /// even displayed (e.g. python scrapers). + /// In such cases, there is no need to lock the + /// GUI when creating the items (increasing your addon + /// performance). + /// Note however, that if you are creating listitems + /// and managing the container itself (e.g using + /// WindowXML or WindowXMLDialog classes) subsquent + /// modifications to the item will require locking. + /// Thus, in such cases, use the default value (`False`). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Added **offscreen** argument. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// videoinfo = xbmc.InfoTagVideo(offscreen=False) + /// ... + /// ~~~~~~~~~~~~~ + /// + InfoTagVideo(...); +#else + explicit InfoTagVideo(bool offscreen = false); +#endif + ~InfoTagVideo() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getDbId() } + /// Get identification number of tag in database + /// + /// @return [integer] database id + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getDbId(); +#else + int getDbId(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getDirector() } + /// Get [film director](https://en.wikipedia.org/wiki/Film_director) + /// who has made the film (if present). + /// + /// @return [string] Film director name. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getDirectors()** instead. + /// + getDirector(); +#else + String getDirector(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getDirectors() } + /// Get a list of [film directors](https://en.wikipedia.org/wiki/Film_director) + /// who have made the film (if present). + /// + /// @return [list] List of film director names. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getDirectors(); +#else + std::vector<String> getDirectors(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getWritingCredits() } + /// Get the writing credits if present from video info tag. + /// + /// @return [string] Writing credits + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getWriters()** instead. + /// + getWritingCredits(); +#else + String getWritingCredits(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getWriters() } + /// Get the list of writers (if present) from video info tag. + /// + /// @return [list] List of writers + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getWriters(); +#else + std::vector<String> getWriters(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getGenre() } + /// To get the [Video Genre](https://en.wikipedia.org/wiki/Film_genre) + /// if available. + /// + /// @return [string] Genre name + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getGenres()** instead. + /// + getGenre(); +#else + String getGenre(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getGenres() } + /// Get the list of [Video Genres](https://en.wikipedia.org/wiki/Film_genre) + /// if available. + /// + /// @return [list] List of genres + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getGenres(); +#else + std::vector<String> getGenres(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getTagLine() } + /// Get video tag line if available. + /// + /// @return [string] Video tag line + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getTagLine(); +#else + String getTagLine(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPlotOutline() } + /// Get the outline plot of the video if present. + /// + /// @return [string] Outline plot + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPlotOutline(); +#else + String getPlotOutline(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPlot() } + /// Get the plot of the video if present. + /// + /// @return [string] Plot + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPlot(); +#else + String getPlot(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPictureURL() } + /// Get a picture URL of the video to show as screenshot. + /// + /// @return [string] Picture URL + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPictureURL(); +#else + String getPictureURL(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getTitle() } + /// Get the video title. + /// + /// @return [string] Video title + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getTitle(); +#else + String getTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getTVShowTitle() } + /// Get the video TV show title. + /// + /// @return [string] TV show title + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getTVShowTitle(); +#else + String getTVShowTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getMediaType() } + /// Get the media type of the video. + /// + /// @return [string] media type + /// + /// Available strings about media type for video: + /// | String | Description | + /// |---------------:|:--------------------------------------------------| + /// | video | For normal video + /// | set | For a selection of video + /// | musicvideo | To define it as music video + /// | movie | To define it as normal movie + /// | tvshow | If this is it defined as tvshow + /// | season | The type is used as a series season + /// | episode | The type is used as a series episode + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getMediaType(); +#else + String getMediaType(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getVotes() } + /// Get the video votes if available from video info tag. + /// + /// @return [string] Votes + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getVotesAsInt()** instead. + /// + getVotes(); +#else + String getVotes(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getVotesAsInt([type]) } + /// Get the votes of the rating (if available) as an integer. + /// + /// @param type [opt] string - the type of the rating. + /// - Some rating type values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - type name + /// | tvdb | string - type name + /// | tmdb | string - type name + /// | anidb | string - type name + /// + /// @return [integer] Votes + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getVotesAsInt(type); +#else + int getVotesAsInt(const String& type = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getCast() } + /// To get the cast of the video when available. + /// + /// @return [string] Video casts + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getActors()** instead. + /// + getCast(); +#else + String getCast(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getActors() } + /// Get the cast of the video if available. + /// + /// @return [list] List of actors + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getActors(); +#else + std::vector<Actor*> getActors(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getFile() } + /// To get the video file name. + /// + /// @return [string] File name + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getFile(); +#else + String getFile(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPath() } + /// To get the path where the video is stored. + /// + /// @return [string] Path + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPath(); +#else + String getPath(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getFilenameAndPath() } + /// To get the full path with filename where the video is stored. + /// + /// @return [string] File name and Path + /// + /// + ///----------------------------------------------------------------------- + /// @python_v19 New function added. + /// + getFilenameAndPath(); +#else + String getFilenameAndPath(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getIMDBNumber() } + /// To get the [IMDb](https://en.wikipedia.org/wiki/Internet_Movie_Database) + /// number of the video (if present). + /// + /// @return [string] IMDb number + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getIMDBNumber(); +#else + String getIMDBNumber(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getSeason() } + /// To get season number of a series + /// + /// @return [integer] season number + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getSeason(); +#else + int getSeason(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getEpisode() } + /// To get episode number of a series + /// + /// @return [integer] episode number + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getEpisode(); +#else + int getEpisode(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getYear() } + /// Get production year of video if present. + /// + /// @return [integer] Production Year + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getYear(); +#else + int getYear(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getRating([type]) } + /// Get the video rating if present as float (double where supported). + /// + /// @param type [opt] string - the type of the rating. + /// - Some rating type values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - type name + /// | tvdb | string - type name + /// | tmdb | string - type name + /// | anidb | string - type name + /// + /// @return [float] The rating of the video + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Optional `type` parameter added. + /// + getRating(type); +#else + double getRating(const String& type = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getUserRating() } + /// Get the user rating if present as integer. + /// + /// @return [integer] The user rating of the video + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getUserRating(); +#else + int getUserRating(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPlayCount() } + /// To get the number of plays of the video. + /// + /// @return [integer] Play Count + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getPlayCount(); +#else + int getPlayCount(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getLastPlayed() } + /// Get the last played date / time as string. + /// + /// @return [string] Last played date / time + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getLastPlayedAsW3C()** instead. + /// + getLastPlayed(); +#else + String getLastPlayed(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getLastPlayedAsW3C() } + /// Get last played datetime as string in W3C format (YYYY-MM-DDThh:mm:ssTZD). + /// + /// @return [string] Last played datetime (W3C) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getLastPlayedAsW3C(); +#else + String getLastPlayedAsW3C(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getOriginalTitle() } + /// To get the original title of the video. + /// + /// @return [string] Original title + /// + /// + ///----------------------------------------------------------------------- + /// + /// + getOriginalTitle(); +#else + String getOriginalTitle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPremiered() } + /// To get [premiered](https://en.wikipedia.org/wiki/Premiere) date + /// of the video, if available. + /// + /// @return [string] + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getPremieredAsW3C()** instead. + /// + getPremiered(); +#else + String getPremiered(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getPremieredAsW3C() } + /// Get [premiered](https://en.wikipedia.org/wiki/Premiere) date as string in W3C format (YYYY-MM-DD). + /// + /// @return [string] Premiered date (W3C) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getPremieredAsW3C(); +#else + String getPremieredAsW3C(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getFirstAired() } + /// Returns first aired date as string from info tag. + /// + /// @return [string] First aired date + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **getFirstAiredAsW3C()** instead. + /// + getFirstAired(); +#else + String getFirstAired(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getFirstAiredAsW3C() } + /// Get first aired date as string in W3C format (YYYY-MM-DD). + /// + /// @return [string] First aired date (W3C) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getFirstAiredAsW3C(); +#else + String getFirstAiredAsW3C(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getTrailer() } + /// To get the path where the trailer is stored. + /// + /// @return [string] Trailer path + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + getTrailer(); +#else + String getTrailer(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getArtist() } + /// To get the artist name (for musicvideos) + /// + /// @return [std::vector<std::string>] Artist name + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getArtist(); +#else + std::vector<std::string> getArtist(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getAlbum() } + /// To get the album name (for musicvideos) + /// + /// @return [string] Album name + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getAlbum(); +#else + String getAlbum(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getTrack() } + /// To get the track number (for musicvideos) + /// + /// @return [int] Track number + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getTrack(); +#else + int getTrack(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getDuration() } + /// To get the duration + /// + /// @return [unsigned int] Duration + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getDuration(); +#else + unsigned int getDuration(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getResumeTime()) } + /// Gets the resume time of the video item. + /// + /// @return [double] Resume time + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getResumeTime(...); +#else + double getResumeTime(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getResumeTimeTotal()) } + /// Gets the total duration stored with the resume time of the video item. + /// + /// @return [double] Total duration stored with the resume time + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getResumeTimeTotal(...); +#else + double getResumeTimeTotal(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ getUniqueID(key) } + /// Get the unique ID of the given key. + /// A unique ID is an identifier used by a (online) video database used to + /// identify a video in its database. + /// + /// @param key string - uniqueID name. + /// - Some default uniqueID values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - uniqueid name + /// | tvdb | string - uniqueid name + /// | tmdb | string - uniqueid name + /// | anidb | string - uniqueid name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getUniqueID(key); +#else + String getUniqueID(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setUniqueID(uniqueid, [type], [isdefault]) } + /// Set the given unique ID. + /// A unique ID is an identifier used by a (online) video database used to + /// identify a video in its database. + /// + /// @param uniqueid string - value of the unique ID. + /// @param type [opt] string - type / label of the unique ID. + /// @param isdefault [opt] bool - whether the given unique ID is the default unique ID. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setUniqueID(...); +#else + void setUniqueID(const String& uniqueid, const String& type = "", bool isdefault = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setUniqueIDs(values, defaultuniqueid) } + /// Set the given unique IDs. + /// A unique ID is an identifier used by a (online) video database used to + /// identify a video in its database. + /// + /// @param values dictionary - pairs of `{ 'label': 'value' }`. + /// @param defaultuniqueid [opt] string - the name of default uniqueID. + /// + /// - Some example values (any string possible): + /// | Label | Type | + /// |:-------------:|:--------------------------------------------------| + /// | imdb | string - uniqueid name + /// | tvdb | string - uniqueid name + /// | tmdb | string - uniqueid name + /// | anidb | string - uniqueid name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setUniqueIDs(...); +#else + void setUniqueIDs(const std::map<String, String>& uniqueIDs, + const String& defaultuniqueid = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setDbId(dbid) } + /// Set the database identifier of the video item. + /// + /// @param dbid integer - Database identifier. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDbId(...); +#else + void setDbId(int dbid); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setYear(year) } + /// Set the year of the video item. + /// + /// @param year integer - Year. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setYear(...); +#else + void setYear(int year); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setEpisode(episode) } + /// Set the episode number of the episode. + /// + /// @param episode integer - Episode number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setEpisode(...); +#else + void setEpisode(int episode); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSeason(season) } + /// Set the season number of the video item. + /// + /// @param season integer - Season number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSeason(...); +#else + void setSeason(int season); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSortEpisode(sortepisode) } + /// Set the episode sort number of the episode. + /// + /// @param sortepisode integer - Episode sort number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSortEpisode(...); +#else + void setSortEpisode(int sortepisode); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSortSeason(sortseason) } + /// Set the season sort number of the season. + /// + /// @param sortseason integer - Season sort number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSortSeason(...); +#else + void setSortSeason(int sortseason); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setEpisodeGuide(episodeguide) } + /// Set the episode guide of the video item. + /// + /// @param episodeguide string - Episode guide. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setEpisodeGuide(...); +#else + void setEpisodeGuide(const String& episodeguide); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTop250(top250) } + /// Set the top 250 number of the video item. + /// + /// @param top250 integer - Top 250 number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTop250(...); +#else + void setTop250(int top250); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSetId(setid) } + /// Set the movie set identifier of the video item. + /// + /// @param setid integer - Set identifier. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSetId(...); +#else + void setSetId(int setid); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTrackNumber(tracknumber) } + /// Set the track number of the music video item. + /// + /// @param tracknumber integer - Track number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTrackNumber(...); +#else + void setTrackNumber(int tracknumber); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setRating(rating, [votes], [type], [isdefault]) } + /// Set the rating of the video item. + /// + /// @param rating float - Rating number. + /// @param votes integer - Number of votes. + /// @param type string - Type of the rating. + /// @param isdefault bool - Whether the rating is the default or not. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setRating(...); +#else + void setRating(float rating, int votes = 0, const String& type = "", bool isdefault = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setRatings(ratings, [defaultrating]) } + /// Set the ratings of the video item. + /// + /// @param ratings dictionary - `{ 'type': (rating, votes) }`. + /// @param defaultrating string - Type / Label of the default rating. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setRatings(...); +#else + void setRatings(const std::map<String, Tuple<float, int>>& ratings, + const String& defaultrating = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setUserRating(userrating) } + /// Set the user rating of the video item. + /// + /// @param userrating integer - User rating. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setUserRating(...); +#else + void setUserRating(int userrating); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setPlaycount(playcount) } + /// Set the playcount of the video item. + /// + /// @param playcount integer - Playcount. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPlaycount(...); +#else + void setPlaycount(int playcount); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setMpaa(mpaa) } + /// Set the MPAA rating of the video item. + /// + /// @param mpaa string - MPAA rating. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMpaa(...); +#else + void setMpaa(const String& mpaa); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setPlot(plot) } + /// Set the plot of the video item. + /// + /// @param plot string - Plot. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPlot(...); +#else + void setPlot(const String& plot); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setPlotOutline(plotoutline) } + /// Set the plot outline of the video item. + /// + /// @param plotoutline string - Plot outline. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPlotOutline(...); +#else + void setPlotOutline(const String& plotoutline); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTitle(title) } + /// Set the title of the video item. + /// + /// @param title string - Title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTitle(...); +#else + void setTitle(const String& title); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setOriginalTitle(originaltitle) } + /// Set the original title of the video item. + /// + /// @param originaltitle string - Original title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setOriginalTitle(...); +#else + void setOriginalTitle(const String& originaltitle); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSortTitle(sorttitle) } + /// Set the sort title of the video item. + /// + /// @param sorttitle string - Sort title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSortTitle(...); +#else + void setSortTitle(const String& sorttitle); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTagLine(tagline) } + /// Set the tagline of the video item. + /// + /// @param tagline string - Tagline. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTagLine(...); +#else + void setTagLine(const String& tagline); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTvShowTitle(tvshowtitle) } + /// Set the TV show title of the video item. + /// + /// @param tvshowtitle string - TV show title. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTvShowTitle(...); +#else + void setTvShowTitle(const String& tvshowtitle); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTvShowStatus(tvshowstatus) } + /// Set the TV show status of the video item. + /// + /// @param status string - TV show status. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTvShowStatus(...); +#else + void setTvShowStatus(const String& status); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setGenres(genre) } + /// Set the genres of the video item. + /// + /// @param genre list - Genres. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setGenres(...); +#else + void setGenres(std::vector<String> genre); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setCountries(countries) } + /// Set the countries of the video item. + /// + /// @param countries list - Countries. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setCountries(...); +#else + void setCountries(std::vector<String> countries); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setDirectors(directors) } + /// Set the directors of the video item. + /// + /// @param directors list - Directors. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDirectors(...); +#else + void setDirectors(std::vector<String> directors); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setStudios(studios) } + /// Set the studios of the video item. + /// + /// @param studios list - Studios. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setStudios(...); +#else + void setStudios(std::vector<String> studios); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setWriters(writers) } + /// Set the writers of the video item. + /// + /// @param writers list - Writers. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setWriters(...); +#else + void setWriters(std::vector<String> writers); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setDuration(duration) } + /// Set the duration of the video item. + /// + /// @param duration integer - Duration in seconds. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDuration(...); +#else + void setDuration(int duration); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setPremiered(premiered) } + /// Set the premiere date of the video item. + /// + /// @param premiered string - Premiere date. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPremiered(...); +#else + void setPremiered(const String& premiered); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSet(set) } + /// Set the movie set (name) of the video item. + /// + /// @param set string - Movie set (name). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSet(...); +#else + void setSet(const String& set); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setSetOverview(setoverview) } + /// Set the movie set overview of the video item. + /// + /// @param setoverview string - Movie set overview. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setSetOverview(...); +#else + void setSetOverview(const String& setoverview); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTags(tags) } + /// Set the tags of the video item. + /// + /// @param tags list - Tags. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTags(...); +#else + void setTags(std::vector<String> tags); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setProductionCode(const String& productioncode) } + /// Set the production code of the video item. + /// + /// @param productioncode string - Production code. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setProductionCode(...); +#else + void setProductionCode(const String& productioncode); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setFirstAired(firstaired) } + /// Set the first aired date of the video item. + /// + /// @param firstaired string - First aired date. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setFirstAired(...); +#else + void setFirstAired(const String& firstaired); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setLastPlayed(lastplayed) } + /// Set the last played date of the video item. + /// + /// @param lastplayed string - Last played date (YYYY-MM-DD HH:MM:SS). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setLastPlayed(...); +#else + void setLastPlayed(const String& lastplayed); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setAlbum(album) } + /// Set the album of the video item. + /// + /// @param album string - Album. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setAlbum(...); +#else + void setAlbum(const String& album); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setVotes(votes) } + /// Set the number of votes of the video item. + /// + /// @param votes integer - Number of votes. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setVotes(...); +#else + void setVotes(int votes); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setTrailer(trailer) } + /// Set the trailer of the video item. + /// + /// @param trailer string - Trailer. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setTrailer(...); +#else + void setTrailer(const String& trailer); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setPath(path) } + /// Set the path of the video item. + /// + /// @param path string - Path. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setPath(...); +#else + void setPath(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setFilenameAndPath(filenameandpath) } + /// Set the filename and path of the video item. + /// + /// @param filenameandpath string - Filename and path. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setFilenameAndPath(...); +#else + void setFilenameAndPath(const String& filenameandpath); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setIMDBNumber(imdbnumber) } + /// Set the IMDb number of the video item. + /// + /// @param imdbnumber string - IMDb number. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setIMDBNumber(...); +#else + void setIMDBNumber(const String& imdbnumber); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setDateAdded(dateadded) } + /// Set the date added of the video item. + /// + /// @param dateadded string - Date added (YYYY-MM-DD HH:MM:SS). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setDateAdded(...); +#else + void setDateAdded(const String& dateadded); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setMediaType(mediatype) } + /// Set the media type of the video item. + /// + /// @param mediatype string - Media type. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setMediaType(...); +#else + void setMediaType(const String& mediatype); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setShowLinks(showlinks) } + /// Set the TV show links of the movie. + /// + /// @param showlinks list - TV show links. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setShowLinks(...); +#else + void setShowLinks(std::vector<String> showlinks); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setArtists(artists) } + /// Set the artists of the music video item. + /// + /// @param artists list - Artists. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setArtists(...); +#else + void setArtists(std::vector<String> artists); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setCast(actors) } + /// Set the cast / actors of the video item. + /// + /// @param actors list - Cast / Actors. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setCast(...); +#else + void setCast(const std::vector<const Actor*>& actors); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ setResumePoint(time, [totaltime]) } + /// Set the resume point of the video item. + /// + /// @param time float - Resume point in seconds. + /// @param totaltime float - Total duration in seconds. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + setResumePoint(...); +#else + void setResumePoint(double time, double totaltime = 0.0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addSeason(number, [name]) } + /// Add a season with name. It needs at least the season number. + /// + /// @param number int - the number of the season. + /// @param name string - the name of the season. Default "". + /// + /// + ///----------------------------------------------------------------------- + /// + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # addSeason(number, name)) + /// infotagvideo.addSeason(1, "Murder House") + /// ... + /// ~~~~~~~~~~~~~ + /// + addSeason(...); +#else + void addSeason(int number, std::string name = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addSeasons(namedseasons) } + /// Add named seasons to the TV show. + /// + /// @param namedseasons list - `[ (season, name) ]`. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + addSeasons(...); +#else + void addSeasons(const std::vector<Tuple<int, std::string>>& namedseasons); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addVideoStream(stream) } + /// Add a video stream to the video item. + /// + /// @param stream VideoStreamDetail - Video stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + addVideoStream(...); +#else + void addVideoStream(const VideoStreamDetail* stream); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addAudioStream(stream) } + /// Add an audio stream to the video item. + /// + /// @param stream AudioStreamDetail - Audio stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + addAudioStream(...); +#else + void addAudioStream(const AudioStreamDetail* stream); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addSubtitleStream(stream) } + /// Add a subtitle stream to the video item. + /// + /// @param stream SubtitleStreamDetail - Subtitle stream. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + addSubtitleStream(...); +#else + void addSubtitleStream(const SubtitleStreamDetail* stream); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_InfoTagVideo + /// @brief \python_func{ addAvailableArtwork(images) } + /// Add an image to available artworks (needed for video scrapers) + /// + /// @param url string - image path url + /// @param arttype string - image type + /// @param preview [opt] string - image preview path url + /// @param referrer [opt] string - referrer url + /// @param cache [opt] string - filename in cache + /// @param post [opt] bool - use post to retrieve the image (default false) + /// @param isgz [opt] bool - use gzip to retrieve the image (default false) + /// @param season [opt] integer - number of season in case of season thumb + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// infotagvideo.addAvailableArtwork(path_to_image_1, "thumb") + /// ... + /// ~~~~~~~~~~~~~ + /// + addAvailableArtwork(...); +#else + void addAvailableArtwork(const std::string& url, + const std::string& arttype = "", + const std::string& preview = "", + const std::string& referrer = "", + const std::string& cache = "", + bool post = false, + bool isgz = false, + int season = -1); +#endif + /// @} + +#ifndef SWIG + static void setDbIdRaw(CVideoInfoTag* infoTag, int dbId); + static void setUniqueIDRaw(CVideoInfoTag* infoTag, + const String& uniqueID, + const String& type = "", + bool isDefault = false); + static void setUniqueIDsRaw(CVideoInfoTag* infoTag, + std::map<String, String> uniqueIDs, + const String& defaultUniqueID = ""); + static void setYearRaw(CVideoInfoTag* infoTag, int year); + static void setEpisodeRaw(CVideoInfoTag* infoTag, int episode); + static void setSeasonRaw(CVideoInfoTag* infoTag, int season); + static void setSortEpisodeRaw(CVideoInfoTag* infoTag, int sortEpisode); + static void setSortSeasonRaw(CVideoInfoTag* infoTag, int sortSeason); + static void setEpisodeGuideRaw(CVideoInfoTag* infoTag, const String& episodeGuide); + static void setTop250Raw(CVideoInfoTag* infoTag, int top250); + static void setSetIdRaw(CVideoInfoTag* infoTag, int setId); + static void setTrackNumberRaw(CVideoInfoTag* infoTag, int trackNumber); + static void setRatingRaw(CVideoInfoTag* infoTag, + float rating, + int votes = 0, + const std::string& type = "", + bool isDefault = false); + static void setRatingsRaw(CVideoInfoTag* infoTag, + const std::map<String, Tuple<float, int>>& ratings, + const String& defaultRating = ""); + static void setUserRatingRaw(CVideoInfoTag* infoTag, int userRating); + static void setPlaycountRaw(CVideoInfoTag* infoTag, int playcount); + static void setMpaaRaw(CVideoInfoTag* infoTag, const String& mpaa); + static void setPlotRaw(CVideoInfoTag* infoTag, const String& plot); + static void setPlotOutlineRaw(CVideoInfoTag* infoTag, const String& plotOutline); + static void setTitleRaw(CVideoInfoTag* infoTag, const String& title); + static void setOriginalTitleRaw(CVideoInfoTag* infoTag, const String& originalTitle); + static void setSortTitleRaw(CVideoInfoTag* infoTag, const String& sortTitle); + static void setTagLineRaw(CVideoInfoTag* infoTag, const String& tagLine); + static void setTvShowTitleRaw(CVideoInfoTag* infoTag, const String& tvshowTitle); + static void setTvShowStatusRaw(CVideoInfoTag* infoTag, const String& tvshowStatus); + static void setGenresRaw(CVideoInfoTag* infoTag, std::vector<String> genre); + static void setCountriesRaw(CVideoInfoTag* infoTag, std::vector<String> countries); + static void setDirectorsRaw(CVideoInfoTag* infoTag, std::vector<String> directors); + static void setStudiosRaw(CVideoInfoTag* infoTag, std::vector<String> studios); + static void setWritersRaw(CVideoInfoTag* infoTag, std::vector<String> writers); + static void setDurationRaw(CVideoInfoTag* infoTag, int duration); + static void setPremieredRaw(CVideoInfoTag* infoTag, const String& premiered); + static void setSetRaw(CVideoInfoTag* infoTag, const String& set); + static void setSetOverviewRaw(CVideoInfoTag* infoTag, const String& setOverview); + static void setTagsRaw(CVideoInfoTag* infoTag, std::vector<String> tags); + static void setProductionCodeRaw(CVideoInfoTag* infoTag, const String& productionCode); + static void setFirstAiredRaw(CVideoInfoTag* infoTag, const String& firstAired); + static void setLastPlayedRaw(CVideoInfoTag* infoTag, const String& lastPlayed); + static void setAlbumRaw(CVideoInfoTag* infoTag, const String& album); + static void setVotesRaw(CVideoInfoTag* infoTag, int votes); + static void setTrailerRaw(CVideoInfoTag* infoTag, const String& trailer); + static void setPathRaw(CVideoInfoTag* infoTag, const String& path); + static void setFilenameAndPathRaw(CVideoInfoTag* infoTag, const String& filenameAndPath); + static void setIMDBNumberRaw(CVideoInfoTag* infoTag, const String& imdbNumber); + static void setDateAddedRaw(CVideoInfoTag* infoTag, const String& dateAdded); + static void setMediaTypeRaw(CVideoInfoTag* infoTag, const String& mediaType); + static void setShowLinksRaw(CVideoInfoTag* infoTag, std::vector<String> showLinks); + static void setArtistsRaw(CVideoInfoTag* infoTag, std::vector<String> artists); + static void setCastRaw(CVideoInfoTag* infoTag, std::vector<SActorInfo> cast); + static void setResumePointRaw(CVideoInfoTag* infoTag, double time, double totalTime = 0.0); + + static void addSeasonRaw(CVideoInfoTag* infoTag, int number, std::string name = ""); + static void addSeasonsRaw(CVideoInfoTag* infoTag, + const std::vector<Tuple<int, std::string>>& namedSeasons); + + static void addStreamRaw(CVideoInfoTag* infoTag, CStreamDetail* stream); + static void finalizeStreamsRaw(CVideoInfoTag* infoTag); + + static void addAvailableArtworkRaw(CVideoInfoTag* infoTag, + const std::string& url, + const std::string& art_type = "", + const std::string& preview = "", + const std::string& referrer = "", + const std::string& cache = "", + bool post = false, + bool isgz = false, + int season = -1); +#endif + }; + } +} diff --git a/xbmc/interfaces/legacy/Keyboard.cpp b/xbmc/interfaces/legacy/Keyboard.cpp new file mode 100644 index 0000000..2b7e08c --- /dev/null +++ b/xbmc/interfaces/legacy/Keyboard.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Keyboard.h" + +#include "LanguageHook.h" +#include "guilib/GUIKeyboardFactory.h" +#include "messaging/ApplicationMessenger.h" +#include "utils/Variant.h" + +using namespace KODI::MESSAGING; + +namespace XBMCAddon +{ + namespace xbmc + { + + Keyboard::Keyboard(const String& line /* = nullString*/, const String& heading/* = nullString*/, bool hidden/* = false*/) + : strDefault(line), strHeading(heading), bHidden(hidden) + { + } + + Keyboard::~Keyboard() = default; + + void Keyboard::doModal(int autoclose) + { + DelayedCallGuard dg(languageHook); + // using keyboardfactory method to get native keyboard if there is. + strText = strDefault; + std::string text(strDefault); + bConfirmed = CGUIKeyboardFactory::ShowAndGetInput(text, CVariant{strHeading}, true, bHidden, autoclose * 1000); + strText = text; + } + + void Keyboard::setDefault(const String& line) + { + strDefault = line; + } + + void Keyboard::setHiddenInput(bool hidden) + { + bHidden = hidden; + } + + void Keyboard::setHeading(const String& heading) + { + strHeading = heading; + } + + String Keyboard::getText() + { + return strText; + } + + bool Keyboard::isConfirmed() + { + return bConfirmed; + } + } +} + diff --git a/xbmc/interfaces/legacy/Keyboard.h b/xbmc/interfaces/legacy/Keyboard.h new file mode 100644 index 0000000..94f6421 --- /dev/null +++ b/xbmc/interfaces/legacy/Keyboard.h @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "AddonString.h" +#include "Exception.h" + +class CGUIDialogKeyboardGeneric; + +namespace XBMCAddon +{ + namespace xbmc + { + XBMCCOMMONS_STANDARD_EXCEPTION(KeyboardException); + + /// + /// \defgroup python_keyboard Keyboard + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's keyboard class.** + /// + /// \python_class{ xbmc.Keyboard([default, heading, hidden]) } + /// + /// Creates a new Keyboard object with default text + /// heading and hidden input flag if supplied. + /// + /// @param default : [opt] string - default text entry. + /// @param heading : [opt] string - keyboard heading. + /// @param hidden : [opt] boolean - True for hidden text entry. + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// kb = xbmc.Keyboard('default', 'heading', True) + /// kb.setDefault('password') # optional + /// kb.setHeading('Enter password') # optional + /// kb.setHiddenInput(True) # optional + /// kb.doModal() + /// if (kb.isConfirmed()): + /// text = kb.getText() + /// .. + /// ~~~~~~~~~~~~~ + /// + class Keyboard : public AddonClass + { + public: +#ifndef SWIG + String strDefault; + String strHeading; + bool bHidden; + String strText; + bool bConfirmed = false; +#endif + + Keyboard(const String& line = emptyString, const String& heading = emptyString, bool hidden = false); + ~Keyboard() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_keyboard + /// @brief \python_func{ doModal([autoclose]) } + /// Show keyboard and wait for user action. + /// + /// @param autoclose [opt] integer - milliseconds to autoclose + /// dialog. (default=do not autoclose) + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// kb.doModal(30000) + /// .. + /// ~~~~~~~~~~~~~ + /// + doModal(...); +#else + void doModal(int autoclose = 0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + // setDefault() Method + /// + /// \ingroup python_keyboard + /// @brief \python_func{ setDefault(line) } + /// Set the default text entry. + /// + /// @param line string - default text entry. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// kb.setDefault('password') + /// .. + /// ~~~~~~~~~~~~~ + /// + setDefault(...); +#else + void setDefault(const String& line = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_keyboard + /// @brief \python_func{ setHiddenInput(hidden) } + /// Allows hidden text entry. + /// + /// @param hidden boolean - True for hidden text entry. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// kb.setHiddenInput(True) + /// .. + /// ~~~~~~~~~~~~~ + /// + setHiddenInput(...); +#else + void setHiddenInput(bool hidden = false); +#endif + + // setHeading() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_keyboard + /// @brief \python_func{ setHeading(heading) } + /// Set the keyboard heading. + /// + /// @param heading string - keyboard heading. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// kb.setHeading('Enter password') + /// .. + /// ~~~~~~~~~~~~~ + /// + setHeading(...); +#else + void setHeading(const String& heading); +#endif + + // getText() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_keyboard + /// @brief \python_func{ getText() } + /// Returns the user input as a string. + /// + /// @note This will always return the text entry even if you cancel the keyboard. + /// Use the isConfirmed() method to check if user cancelled the keyboard. + /// + /// @return get the in keyboard entered text + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// text = kb.getText() + /// .. + /// ~~~~~~~~~~~~~ + /// + getText(); +#else + String getText(); +#endif + + // isConfirmed() Method +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_keyboard + /// @brief \python_func{ isConfirmed() } + /// Returns False if the user cancelled the input. + /// + /// @return true if confirmed, if cancelled false + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// if (kb.isConfirmed()): + /// .. + /// ~~~~~~~~~~~~~ + /// + isConfirmed(); +#else + bool isConfirmed(); +#endif + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/LanguageHook.cpp b/xbmc/interfaces/legacy/LanguageHook.cpp new file mode 100644 index 0000000..c3b70c6 --- /dev/null +++ b/xbmc/interfaces/legacy/LanguageHook.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "LanguageHook.h" + +#include "utils/GlobalsHandling.h" + +namespace XBMCAddon +{ + // just need a place for the vtab + LanguageHook::~LanguageHook() = default; + + static thread_local LanguageHook* addonLanguageHookTls; + static bool threadLocalInitialized = false; + static xbmcutil::InitFlag initer(threadLocalInitialized); + + void LanguageHook::SetLanguageHook(LanguageHook* languageHook) + { + XBMC_TRACE; + languageHook->Acquire(); + addonLanguageHookTls = languageHook; + } + + LanguageHook* LanguageHook::GetLanguageHook() + { + return threadLocalInitialized ? addonLanguageHookTls : NULL; + } + + void LanguageHook::ClearLanguageHook() + { + LanguageHook* lh = addonLanguageHookTls; + addonLanguageHookTls = NULL; + if (lh) + lh->Release(); + } +} diff --git a/xbmc/interfaces/legacy/LanguageHook.h b/xbmc/interfaces/legacy/LanguageHook.h new file mode 100644 index 0000000..86abdac --- /dev/null +++ b/xbmc/interfaces/legacy/LanguageHook.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "CallbackHandler.h" +#include "threads/Event.h" + +/** + * This class is an interface that can be used to define programming language + * specific hooks. + */ + +class IPlayerCallback; + +namespace XBMCAddon +{ + namespace xbmc + { + class Monitor; + } + + class LanguageHook : public AddonClass + { + protected: + inline LanguageHook() = default; + + public: + ~LanguageHook() override; + + /** + * If the scripting language needs special handling for calls + * that block or are delayed in any other means, this should + * be overloaded. + * + * In Python when control is passed to a native + * method that blocks, you need to allow other threads in + * Python to run by using Py_BEGIN_ALLOW_THREADS. This is + * the place to put that functionality + */ + virtual void DelayedCallOpen() { } + + /** + * If the scripting language needs special handling for calls + * that block or are delayed in any other means, this should + * be overloaded. + * + * In Python when control is passed to a native + * method that blocks, you need to allow other threads in + * Python to run by using Py_BEGIN_ALLOW_THREADS. When that + * delayed method ends you need to restore the Python thread + * state using Py_END_ALLOW_THREADS. This is the place to put + * that functionality + */ + virtual void DelayedCallClose() { } + + virtual void MakePendingCalls() {} + + /** + * For scripting languages that need a global callback handler, this + * method should be overloaded to supply one. + */ + virtual CallbackHandler* GetCallbackHandler() { return NULL; } + + /** + * This is a callback method that can be overridden to receive a callback + * when an AddonClass that has this language hook is on is being constructed. + * It is called from the constructor of AddonClass so the implementor + * cannot assume the subclasses have been built or that calling a + * virtual function on the AddonClass will work as expected. + */ + virtual void Constructing(AddonClass* beingConstructed) { } + + /** + * This is a callback method that can be overridden to receive a callback + * when an AddonClass that has this language hook is on is being destructed. + * It is called from the destructor of AddonClass so the implementor + * should assume the subclasses have been torn down and that calling a + * virtual function on the AddonClass will not work as expected. + */ + virtual void Destructing(AddonClass* beingDestructed) { } + + /** + * This method should be done a different way but since the only other way + * I can think to do it requires an InheritableThreadLocal C++ equivalent, + * I'm going to defer to this technique for now. + * + * Currently (for python) the scripting language has the Addon id injected + * into it as a global variable. Therefore the only way to retrieve it is + * to use scripting language specific calls. So until I figure out a + * better way to do this, this is how I need to retrieve it. + */ + virtual String GetAddonId() { return emptyString; } + virtual String GetAddonVersion() { return emptyString; } + virtual long GetInvokerId() { return -1; } + + virtual void RegisterPlayerCallback(IPlayerCallback* player) = 0; + virtual void UnregisterPlayerCallback(IPlayerCallback* player) = 0; + virtual void RegisterMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0; + virtual void UnregisterMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0; + virtual bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds) = 0; + + static void SetLanguageHook(LanguageHook* languageHook); + static LanguageHook* GetLanguageHook(); + static void ClearLanguageHook(); + }; + + /** + * This class can be used to access the language hook's DelayedCallOpen + * and DelayedCallClose. It should be used whenever an API method + * is written such that it can block for an indefinite amount of time + * since certain scripting languages (like Python) need to do extra + * work for delayed calls (like free the python locks and handle + * callbacks). + */ + class DelayedCallGuard + { + LanguageHook* languageHook; + bool clearOnExit = false; + + public: + inline explicit DelayedCallGuard(LanguageHook* languageHook_) : languageHook(languageHook_) + { if (languageHook) languageHook->DelayedCallOpen(); } + + inline DelayedCallGuard() : languageHook(LanguageHook::GetLanguageHook()) + { if (languageHook) languageHook->DelayedCallOpen(); } + + inline ~DelayedCallGuard() + { + if (clearOnExit) LanguageHook::ClearLanguageHook(); + if (languageHook) languageHook->DelayedCallClose(); + } + + inline LanguageHook* getLanguageHook() { return languageHook; } + }; + + class SetLanguageHookGuard + { + public: + inline explicit SetLanguageHookGuard(LanguageHook* languageHook) { LanguageHook::SetLanguageHook(languageHook); } + inline ~SetLanguageHookGuard() { LanguageHook::ClearLanguageHook(); } + }; + +} + diff --git a/xbmc/interfaces/legacy/List.h b/xbmc/interfaces/legacy/List.h new file mode 100644 index 0000000..932fa4e --- /dev/null +++ b/xbmc/interfaces/legacy/List.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include <list> + +namespace XBMCAddon +{ + template <class T> class List : public std::list<T> + { + public: + static List<T> nullList; + }; +} diff --git a/xbmc/interfaces/legacy/ListItem.cpp b/xbmc/interfaces/legacy/ListItem.cpp new file mode 100644 index 0000000..64410ad --- /dev/null +++ b/xbmc/interfaces/legacy/ListItem.cpp @@ -0,0 +1,1056 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ListItem.h" + +#include "AddonUtils.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "games/GameTypes.h" +#include "games/tags/GameInfoTag.h" +#include "music/tags/MusicInfoTag.h" +#include "pictures/PictureInfoTag.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" +#include "video/VideoInfoTag.h" + +#include <cstdlib> +#include <sstream> +#include <utility> + +namespace XBMCAddon +{ + namespace xbmcgui + { + ListItem::ListItem(const String& label, + const String& label2, + const String& path, + bool offscreen) : + m_offscreen(offscreen) + { + item.reset(); + + // create CFileItem + item.reset(new CFileItem()); + if (!item) // not sure if this is really possible + return; + + if (!label.empty()) + item->SetLabel( label ); + if (!label2.empty()) + item->SetLabel2( label2 ); + if (!path.empty()) + item->SetPath(path); + } + + ListItem::~ListItem() + { + item.reset(); + } + + String ListItem::getLabel() + { + if (!item) return ""; + + String ret; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + ret = item->GetLabel(); + } + + return ret; + } + + String ListItem::getLabel2() + { + if (!item) return ""; + + String ret; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + ret = item->GetLabel2(); + } + + return ret; + } + + void ListItem::setLabel(const String& label) + { + if (!item) return; + // set label + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + item->SetLabel(label); + } + } + + void ListItem::setLabel2(const String& label) + { + if (!item) return; + // set label + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + item->SetLabel2(label); + } + } + + String ListItem::getDateTime() + { + if (!item) + return ""; + + String ret; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + if (item->m_dateTime.IsValid()) + ret = item->m_dateTime.GetAsW3CDateTime(); + } + + return ret; + } + + void ListItem::setDateTime(const String& dateTime) + { + if (!item) + return; + // set datetime + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + setDateTimeRaw(dateTime); + } + } + + void ListItem::setArt(const Properties& dictionary) + { + if (!item) return; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + for (const auto& it : dictionary) + addArtRaw(it.first, it.second); + } + } + + void ListItem::setIsFolder(bool isFolder) + { + if (!item) + return; + + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + setIsFolderRaw(isFolder); + } + } + + void ListItem::setUniqueIDs(const Properties& dictionary, const String& defaultrating /* = "" */) + { + CLog::Log( + LOGWARNING, + "ListItem.setUniqueIDs() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.setUniqueIDs()."); + + if (!item) + return; + + std::map<String, String> uniqueIDs; + for (const auto& it : dictionary) + uniqueIDs.emplace(it.first, it.second); + + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + xbmc::InfoTagVideo::setUniqueIDsRaw(GetVideoInfoTag(), uniqueIDs, defaultrating); + } + } + + void ListItem::setRating(const std::string& type, + float rating, + int votes /* = 0 */, + bool defaultt /* = false */) + { + CLog::Log(LOGWARNING, + "ListItem.setRating() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.setRating()."); + + if (!item) return; + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + xbmc::InfoTagVideo::setRatingRaw(GetVideoInfoTag(), rating, votes, type, defaultt); + } + + void ListItem::addSeason(int number, std::string name /* = "" */) + { + CLog::Log(LOGWARNING, + "ListItem.addSeason() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.addSeason()."); + + if (!item) return; + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + xbmc::InfoTagVideo::addSeasonRaw(GetVideoInfoTag(), number, std::move(name)); + } + + void ListItem::select(bool selected) + { + if (!item) return; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + item->Select(selected); + } + } + + + bool ListItem::isSelected() + { + if (!item) return false; + + bool ret; + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + ret = item->IsSelected(); + } + + return ret; + } + + void ListItem::setProperty(const char * key, const String& value) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + String lowerKey = key; + StringUtils::ToLower(lowerKey); + if (lowerKey == "startoffset") + { // special case for start offset - don't actually store in a property + setStartOffsetRaw(strtod(value.c_str(), nullptr)); + } + else if (lowerKey == "mimetype") + { // special case for mime type - don't actually stored in a property, + item->SetMimeType(value); + } + else if (lowerKey == "totaltime") + { + CLog::Log(LOGWARNING, + "\"{}\" in ListItem.setProperty() is deprecated and might be removed in future " + "Kodi versions. Please use InfoTagVideo.setResumePoint().", + lowerKey); + + CBookmark resumePoint(GetVideoInfoTag()->GetResumePoint()); + resumePoint.totalTimeInSeconds = atof(value.c_str()); + GetVideoInfoTag()->SetResumePoint(resumePoint); + } + else if (lowerKey == "resumetime") + { + CLog::Log(LOGWARNING, + "\"{}\" in ListItem.setProperty() is deprecated and might be removed in future " + "Kodi versions. Please use InfoTagVideo.setResumePoint().", + lowerKey); + + xbmc::InfoTagVideo::setResumePointRaw(GetVideoInfoTag(), atof(value.c_str())); + } + else if (lowerKey == "specialsort") + setSpecialSortRaw(value); + else if (lowerKey == "fanart_image") + item->SetArt("fanart", value); + else + addPropertyRaw(lowerKey, value); + } + + void ListItem::setProperties(const Properties& dictionary) + { + for (const auto& it : dictionary) + setProperty(it.first.c_str(), it.second); + } + + String ListItem::getProperty(const char* key) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + String lowerKey = key; + StringUtils::ToLower(lowerKey); + std::string value; + if (lowerKey == "startoffset") + { // special case for start offset - don't actually store in a property, + // we store it in item.m_lStartOffset instead + value = StringUtils::Format("{:f}", CUtil::ConvertMilliSecsToSecs(item->GetStartOffset())); + } + else if (lowerKey == "totaltime") + { + CLog::Log(LOGWARNING, + "\"{}\" in ListItem.getProperty() is deprecated and might be removed in future " + "Kodi versions. Please use InfoTagVideo.getResumeTimeTotal().", + lowerKey); + + value = StringUtils::Format("{:f}", GetVideoInfoTag()->GetResumePoint().totalTimeInSeconds); + } + else if (lowerKey == "resumetime") + { + CLog::Log(LOGWARNING, + "\"{}\" in ListItem.getProperty() is deprecated and might be removed in future " + "Kodi versions. Please use InfoTagVideo.getResumeTime().", + lowerKey); + + value = StringUtils::Format("{:f}", GetVideoInfoTag()->GetResumePoint().timeInSeconds); + } + else if (lowerKey == "fanart_image") + value = item->GetArt("fanart"); + else + value = item->GetProperty(lowerKey).asString(); + + return value; + } + + String ListItem::getArt(const char* key) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return item->GetArt(key); + } + + bool ListItem::isFolder() const + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return item->m_bIsFolder; + } + + String ListItem::getUniqueID(const char* key) + { + CLog::Log( + LOGWARNING, + "ListItem.getUniqueID() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.getUniqueID()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return GetVideoInfoTag()->GetUniqueID(key); + } + + float ListItem::getRating(const char* key) + { + CLog::Log(LOGWARNING, + "ListItem.getRating() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.getRating()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return GetVideoInfoTag()->GetRating(key).rating; + } + + int ListItem::getVotes(const char* key) + { + CLog::Log(LOGWARNING, + "ListItem.getVotes() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.getVotesAsInt()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return GetVideoInfoTag()->GetRating(key).votes; + } + + void ListItem::setPath(const String& path) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + setPathRaw(path); + } + + void ListItem::setMimeType(const String& mimetype) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + setMimeTypeRaw(mimetype); + } + + void ListItem::setContentLookup(bool enable) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + setContentLookupRaw(enable); + } + + String ListItem::getPath() + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return item->GetPath(); + } + + void ListItem::setInfo(const char* type, const InfoLabelDict& infoLabels) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + + bool hasDeprecatedInfoLabel = false; + if (StringUtils::CompareNoCase(type, "video") == 0) + { + using InfoTagVideo = xbmc::InfoTagVideo; + auto videotag = GetVideoInfoTag(); + for (const auto& it : infoLabels) + { + const auto key = StringUtils::ToLower(it.first); + const InfoLabelValue& alt = it.second; + const String value(alt.which() == first ? alt.former() : emptyString); + + if (key == "count") + setCountRaw(strtol(value.c_str(), nullptr, 10)); + else if (key == "size") + setSizeRaw(static_cast<int64_t>(strtoll(value.c_str(), nullptr, 10))); + else if (key == "overlay") + { + long overlay = strtol(value.c_str(), nullptr, 10); + if (overlay >= 0 && overlay <= 8) + item->SetOverlayImage(static_cast<CGUIListItem::GUIIconOverlay>(overlay)); + } + else if (key == "date") + setDateTimeRaw(value); + else + { + hasDeprecatedInfoLabel = true; + + if (key == "dbid") + InfoTagVideo::setDbIdRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "year") + InfoTagVideo::setYearRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "episode") + InfoTagVideo::setEpisodeRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "season") + InfoTagVideo::setSeasonRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "sortepisode") + InfoTagVideo::setSortEpisodeRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "sortseason") + InfoTagVideo::setSortSeasonRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "episodeguide") + InfoTagVideo::setEpisodeGuideRaw(videotag, value); + else if (key == "showlink") + InfoTagVideo::setShowLinksRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "top250") + InfoTagVideo::setTop250Raw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "setid") + InfoTagVideo::setSetIdRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "tracknumber") + InfoTagVideo::setTrackNumberRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "rating") + InfoTagVideo::setRatingRaw(videotag, + static_cast<float>(strtod(value.c_str(), nullptr))); + else if (key == "userrating") + InfoTagVideo::setUserRatingRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "watched") // backward compat - do we need it? + InfoTagVideo::setPlaycountRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "playcount") + InfoTagVideo::setPlaycountRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "cast" || key == "castandrole") + { + if (alt.which() != second) + throw WrongTypeException("When using \"cast\" or \"castandrole\" you need to " + "supply a list of tuples for the value in the dictionary"); + + std::vector<SActorInfo> cast; + cast.reserve(alt.later().size()); + for (const auto& castEntry : alt.later()) + { + // castEntry can be a string meaning it's the actor or it can be a tuple meaning it's the + // actor and the role. + const String& actor = + castEntry.which() == first ? castEntry.former() : castEntry.later().first(); + SActorInfo info; + info.strName = actor; + if (castEntry.which() == second) + info.strRole = static_cast<const String&>(castEntry.later().second()); + cast.push_back(std::move(info)); + } + InfoTagVideo::setCastRaw(videotag, std::move(cast)); + } + else if (key == "artist") + { + if (alt.which() != second) + throw WrongTypeException("When using \"artist\" you need to supply a list of " + "strings for the value in the dictionary"); + + std::vector<String> artists; + artists.reserve(alt.later().size()); + for (const auto& castEntry : alt.later()) + { + auto actor = + castEntry.which() == first ? castEntry.former() : castEntry.later().first(); + artists.push_back(std::move(actor)); + } + InfoTagVideo::setArtistsRaw(videotag, artists); + } + else if (key == "genre") + InfoTagVideo::setGenresRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "country") + InfoTagVideo::setCountriesRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "director") + InfoTagVideo::setDirectorsRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "mpaa") + InfoTagVideo::setMpaaRaw(videotag, value); + else if (key == "plot") + InfoTagVideo::setPlotRaw(videotag, value); + else if (key == "plotoutline") + InfoTagVideo::setPlotOutlineRaw(videotag, value); + else if (key == "title") + InfoTagVideo::setTitleRaw(videotag, value); + else if (key == "originaltitle") + InfoTagVideo::setOriginalTitleRaw(videotag, value); + else if (key == "sorttitle") + InfoTagVideo::setSortTitleRaw(videotag, value); + else if (key == "duration") + InfoTagVideo::setDurationRaw(videotag, strtol(value.c_str(), nullptr, 10)); + else if (key == "studio") + InfoTagVideo::setStudiosRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "tagline") + InfoTagVideo::setTagLineRaw(videotag, value); + else if (key == "writer" || key == "credits") + InfoTagVideo::setWritersRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "tvshowtitle") + InfoTagVideo::setTvShowTitleRaw(videotag, value); + else if (key == "premiered") + InfoTagVideo::setPremieredRaw(videotag, value); + else if (key == "status") + InfoTagVideo::setTvShowStatusRaw(videotag, value); + else if (key == "set") + InfoTagVideo::setSetRaw(videotag, value); + else if (key == "setoverview") + InfoTagVideo::setSetOverviewRaw(videotag, value); + else if (key == "tag") + InfoTagVideo::setTagsRaw(videotag, getVideoStringArray(alt, key, value)); + else if (key == "imdbnumber") + InfoTagVideo::setIMDBNumberRaw(videotag, value); + else if (key == "code") + InfoTagVideo::setProductionCodeRaw(videotag, value); + else if (key == "aired") + InfoTagVideo::setFirstAiredRaw(videotag, value); + else if (key == "lastplayed") + InfoTagVideo::setLastPlayedRaw(videotag, value); + else if (key == "album") + InfoTagVideo::setAlbumRaw(videotag, value); + else if (key == "votes") + InfoTagVideo::setVotesRaw(videotag, StringUtils::ReturnDigits(value)); + else if (key == "trailer") + InfoTagVideo::setTrailerRaw(videotag, value); + else if (key == "path") + InfoTagVideo::setPathRaw(videotag, value); + else if (key == "filenameandpath") + InfoTagVideo::setFilenameAndPathRaw(videotag, value); + else if (key == "dateadded") + InfoTagVideo::setDateAddedRaw(videotag, value); + else if (key == "mediatype") + InfoTagVideo::setMediaTypeRaw(videotag, value); + else + CLog::Log(LOGERROR, "NEWADDON Unknown Video Info Key \"{}\"", key); + } + } + + if (hasDeprecatedInfoLabel) + { + CLog::Log( + LOGWARNING, + "Setting most video properties through ListItem.setInfo() is deprecated and might be " + "removed in future Kodi versions. Please use the respective setter in InfoTagVideo."); + } + } + else if (StringUtils::CompareNoCase(type, "music") == 0) + { + String mediaType; + int dbId = -1; + + using InfoTagMusic = xbmc::InfoTagMusic; + auto musictag = GetMusicInfoTag(); + for (const auto& it : infoLabels) + { + const auto key = StringUtils::ToLower(it.first); + const auto& alt = it.second; + const String value(alt.which() == first ? alt.former() : emptyString); + + //! @todo add the rest of the infolabels + if (key == "count") + setCountRaw(strtol(value.c_str(), nullptr, 10)); + else if (key == "size") + setSizeRaw(static_cast<int64_t>(strtoll(value.c_str(), nullptr, 10))); + else if (key == "date") + setDateTimeRaw(value); + else + { + hasDeprecatedInfoLabel = true; + + if (key == "dbid") + dbId = static_cast<int>(strtol(value.c_str(), NULL, 10)); + else if (key == "mediatype") + mediaType = value; + else if (key == "tracknumber") + InfoTagMusic::setTrackRaw(musictag, strtol(value.c_str(), NULL, 10)); + else if (key == "discnumber") + InfoTagMusic::setDiscRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "duration") + InfoTagMusic::setDurationRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "year") + InfoTagMusic::setYearRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "listeners") + InfoTagMusic::setListenersRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "playcount") + InfoTagMusic::setPlayCountRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "genre") + InfoTagMusic::setGenresRaw(musictag, getMusicStringArray(alt, key, value)); + else if (key == "album") + InfoTagMusic::setAlbumRaw(musictag, value); + else if (key == "artist") + InfoTagMusic::setArtistRaw(musictag, value); + else if (key == "title") + InfoTagMusic::setTitleRaw(musictag, value); + else if (key == "rating") + InfoTagMusic::setRatingRaw(musictag, + static_cast<float>(strtod(value.c_str(), nullptr))); + else if (key == "userrating") + InfoTagMusic::setUserRatingRaw(musictag, strtol(value.c_str(), nullptr, 10)); + else if (key == "lyrics") + InfoTagMusic::setLyricsRaw(musictag, value); + else if (key == "lastplayed") + InfoTagMusic::setLastPlayedRaw(musictag, value); + else if (key == "musicbrainztrackid") + InfoTagMusic::setMusicBrainzTrackIDRaw(musictag, value); + else if (key == "musicbrainzartistid") + InfoTagMusic::setMusicBrainzArtistIDRaw(musictag, + getMusicStringArray(alt, key, value)); + else if (key == "musicbrainzalbumid") + InfoTagMusic::setMusicBrainzAlbumIDRaw(musictag, value); + else if (key == "musicbrainzalbumartistid") + InfoTagMusic::setMusicBrainzAlbumArtistIDRaw(musictag, + getMusicStringArray(alt, key, value)); + else if (key == "comment") + InfoTagMusic::setCommentRaw(musictag, value); + else + CLog::Log(LOGERROR, "NEWADDON Unknown Music Info Key \"{}\"", key); + } + + // This should probably be set outside of the loop but since the original + // implementation set it inside of the loop, I'll leave it that way. - Jim C. + musictag->SetLoaded(true); + } + + if (dbId > 0 && !mediaType.empty()) + InfoTagMusic::setDbIdRaw(musictag, dbId, mediaType); + + if (hasDeprecatedInfoLabel) + { + CLog::Log( + LOGWARNING, + "Setting most music properties through ListItem.setInfo() is deprecated and might be " + "removed in future Kodi versions. Please use the respective setter in InfoTagMusic."); + } + } + else if (StringUtils::CompareNoCase(type, "pictures") == 0) + { + for (const auto& it : infoLabels) + { + const auto key = StringUtils::ToLower(it.first); + const auto& alt = it.second; + const String value(alt.which() == first ? alt.former() : emptyString); + + if (key == "count") + setCountRaw(strtol(value.c_str(), nullptr, 10)); + else if (key == "size") + setSizeRaw(static_cast<int64_t>(strtoll(value.c_str(), nullptr, 10))); + else if (key == "title") + setTitleRaw(value); + else if (key == "picturepath") + setPathRaw(value); + else if (key == "date") + setDateTimeRaw(value); + else + { + hasDeprecatedInfoLabel = true; + + String exifkey = key; + if (!StringUtils::StartsWithNoCase(exifkey, "exif:") || exifkey.length() < 6) + { + CLog::Log(LOGWARNING, "ListItem.setInfo: unknown pictures info key \"{}\"", key); + continue; + } + + exifkey = StringUtils::Mid(exifkey, 5); + if (exifkey == "resolution") + xbmc::InfoTagPicture::setResolutionRaw(item->GetPictureInfoTag(), value); + else if (exifkey == "exiftime") + xbmc::InfoTagPicture::setDateTimeTakenRaw(item->GetPictureInfoTag(), value); + else + CLog::Log(LOGWARNING, "ListItem.setInfo: unknown pictures info key \"{}\"", key); + } + } + + if (hasDeprecatedInfoLabel) + { + CLog::Log(LOGWARNING, "Setting most picture properties through ListItem.setInfo() is " + "deprecated and might be removed in future Kodi versions. Please " + "use the respective setter in InfoTagPicture."); + } + } + else if (StringUtils::EqualsNoCase(type, "game")) + { + auto gametag = item->GetGameInfoTag(); + for (const auto& it : infoLabels) + { + const auto key = StringUtils::ToLower(it.first); + const auto& alt = it.second; + const String value(alt.which() == first ? alt.former() : emptyString); + + if (key == "title") + { + setTitleRaw(value); + xbmc::InfoTagGame::setTitleRaw(gametag, value); + } + else if (key == "platform") + xbmc::InfoTagGame::setPlatformRaw(gametag, value); + else if (key == "genres") + xbmc::InfoTagGame::setGenresRaw(gametag, getStringArray(alt, key, value, ",")); + else if (key == "publisher") + xbmc::InfoTagGame::setPublisherRaw(gametag, value); + else if (key == "developer") + xbmc::InfoTagGame::setDeveloperRaw(gametag, value); + else if (key == "overview") + xbmc::InfoTagGame::setOverviewRaw(gametag, value); + else if (key == "year") + xbmc::InfoTagGame::setYearRaw(gametag, strtoul(value.c_str(), nullptr, 10)); + else if (key == "gameclient") + xbmc::InfoTagGame::setGameClientRaw(gametag, value); + } + + if (!infoLabels.empty()) + { + CLog::Log( + LOGWARNING, + "Setting game properties through ListItem.setInfo() is deprecated and might be " + "removed in future Kodi versions. Please use the respective setter in InfoTagGame."); + } + } + else + CLog::Log(LOGWARNING, "ListItem.setInfo: unknown \"type\" parameter value: {}", type); + } // end ListItem::setInfo + + void ListItem::setCast(const std::vector<Properties>& actors) + { + CLog::Log(LOGWARNING, + "ListItem.setCast() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.setCast()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + std::vector<SActorInfo> cast; + cast.reserve(actors.size()); + for (const auto& dictionary : actors) + { + SActorInfo info; + for (auto it = dictionary.begin(); it != dictionary.end(); ++it) + { + const String& key = it->first; + const String& value = it->second; + if (key == "name") + info.strName = value; + else if (key == "role") + info.strRole = value; + else if (key == "thumbnail") + { + info.thumbUrl = CScraperUrl(value); + if (!info.thumbUrl.GetFirstThumbUrl().empty()) + info.thumb = CScraperUrl::GetThumbUrl(info.thumbUrl.GetFirstUrlByType()); + } + else if (key == "order") + info.order = strtol(value.c_str(), nullptr, 10); + } + cast.push_back(std::move(info)); + } + xbmc::InfoTagVideo::setCastRaw(GetVideoInfoTag(), std::move(cast)); + } + + void ListItem::setAvailableFanart(const std::vector<Properties>& images) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + auto infoTag = GetVideoInfoTag(); + infoTag->m_fanart.Clear(); + for (const auto& dictionary : images) + { + std::string image; + std::string preview; + std::string colors; + for (const auto& it : dictionary) + { + const String& key = it.first; + const String& value = it.second; + if (key == "image") + image = value; + else if (key == "preview") + preview = value; + else if (key == "colors") + colors = value; + } + infoTag->m_fanart.AddFanart(image, preview, colors); + } + infoTag->m_fanart.Pack(); + } + + void ListItem::addAvailableArtwork(const std::string& url, + const std::string& art_type, + const std::string& preview, + const std::string& referrer, + const std::string& cache, + bool post, + bool isgz, + int season) + { + CLog::Log(LOGWARNING, "ListItem.addAvailableArtwork() is deprecated and might be removed in " + "future Kodi versions. Please use InfoTagVideo.addAvailableArtwork()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + xbmc::InfoTagVideo::addAvailableArtworkRaw(GetVideoInfoTag(), url, art_type, preview, + referrer, cache, post, isgz, season); + } + + void ListItem::addStreamInfo(const char* cType, const Properties& dictionary) + { + CLog::Log( + LOGWARNING, + "ListItem.addStreamInfo() is deprecated and might be removed in future Kodi versions. " + "Please use InfoTagVideo.addVideoStream(), InfoTagVideo.addAudioStream() and " + "InfoTagVideo.addSubtitleStream()."); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + + auto infoTag = GetVideoInfoTag(); + if (StringUtils::CompareNoCase(cType, "video") == 0) + { + CStreamDetailVideo* video = new CStreamDetailVideo; + for (const auto& it : dictionary) + { + const String& key = it.first; + const String value(it.second.c_str()); + + if (key == "codec") + video->m_strCodec = value; + else if (key == "aspect") + video->m_fAspect = static_cast<float>(atof(value.c_str())); + else if (key == "width") + video->m_iWidth = strtol(value.c_str(), nullptr, 10); + else if (key == "height") + video->m_iHeight = strtol(value.c_str(), nullptr, 10); + else if (key == "duration") + video->m_iDuration = strtol(value.c_str(), nullptr, 10); + else if (key == "stereomode") + video->m_strStereoMode = value; + else if (key == "language") + video->m_strLanguage = value; + } + xbmc::InfoTagVideo::addStreamRaw(infoTag, video); + } + else if (StringUtils::CompareNoCase(cType, "audio") == 0) + { + CStreamDetailAudio* audio = new CStreamDetailAudio; + for (const auto& it : dictionary) + { + const String& key = it.first; + const String& value = it.second; + + if (key == "codec") + audio->m_strCodec = value; + else if (key == "language") + audio->m_strLanguage = value; + else if (key == "channels") + audio->m_iChannels = strtol(value.c_str(), nullptr, 10); + } + xbmc::InfoTagVideo::addStreamRaw(infoTag, audio); + } + else if (StringUtils::CompareNoCase(cType, "subtitle") == 0) + { + CStreamDetailSubtitle* subtitle = new CStreamDetailSubtitle; + for (const auto& it : dictionary) + { + const String& key = it.first; + const String& value = it.second; + + if (key == "language") + subtitle->m_strLanguage = value; + } + xbmc::InfoTagVideo::addStreamRaw(infoTag, subtitle); + } + xbmc::InfoTagVideo::finalizeStreamsRaw(infoTag); + } // end ListItem::addStreamInfo + + void ListItem::addContextMenuItems(const std::vector<Tuple<String,String> >& items, bool replaceItems /* = false */) + { + for (size_t i = 0; i < items.size(); ++i) + { + auto& tuple = items[i]; + if (tuple.GetNumValuesSet() != 2) + throw ListItemException("Must pass in a list of tuples of pairs of strings. One entry in the list only has %d elements.",tuple.GetNumValuesSet()); + + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + item->SetProperty(StringUtils::Format("contextmenulabel({})", i), tuple.first()); + item->SetProperty(StringUtils::Format("contextmenuaction({})", i), tuple.second()); + } + } + + void ListItem::setSubtitles(const std::vector<String>& paths) + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + addSubtitlesRaw(paths); + } + + xbmc::InfoTagVideo* ListItem::getVideoInfoTag() + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return new xbmc::InfoTagVideo(GetVideoInfoTag(), m_offscreen); + } + + xbmc::InfoTagMusic* ListItem::getMusicInfoTag() + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return new xbmc::InfoTagMusic(GetMusicInfoTag(), m_offscreen); + } + + xbmc::InfoTagPicture* ListItem::getPictureInfoTag() + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return new xbmc::InfoTagPicture(item->GetPictureInfoTag(), m_offscreen); + } + + xbmc::InfoTagGame* ListItem::getGameInfoTag() + { + XBMCAddonUtils::GuiLock lock(languageHook, m_offscreen); + return new xbmc::InfoTagGame(item->GetGameInfoTag(), m_offscreen); + } + + std::vector<std::string> ListItem::getStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value, + const std::string& separator) + { + if (alt.which() == first) + { + if (value.empty()) + value = alt.former(); + return StringUtils::Split(value, separator); + } + + std::vector<std::string> els; + for (const auto& el : alt.later()) + { + if (el.which() == second) + throw WrongTypeException("When using \"%s\" you need to supply a string or list of strings for the value in the dictionary", tag.c_str()); + els.emplace_back(el.former()); + } + return els; + } + + std::vector<std::string> ListItem::getVideoStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value /* = "" */) + { + return getStringArray( + alt, tag, std::move(value), + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_videoItemSeparator); + } + + std::vector<std::string> ListItem::getMusicStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value /* = "" */) + { + return getStringArray( + alt, tag, std::move(value), + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator); + } + + CVideoInfoTag* ListItem::GetVideoInfoTag() + { + return item->GetVideoInfoTag(); + } + + const CVideoInfoTag* ListItem::GetVideoInfoTag() const + { + return item->GetVideoInfoTag(); + } + + MUSIC_INFO::CMusicInfoTag* ListItem::GetMusicInfoTag() + { + return item->GetMusicInfoTag(); + } + + const MUSIC_INFO::CMusicInfoTag* ListItem::GetMusicInfoTag() const + { + return item->GetMusicInfoTag(); + } + + void ListItem::setTitleRaw(std::string title) + { + item->m_strTitle = std::move(title); + } + + void ListItem::setPathRaw(const std::string& path) + { + item->SetPath(path); + } + + void ListItem::setCountRaw(int count) + { + item->m_iprogramCount = count; + } + + void ListItem::setSizeRaw(int64_t size) + { + item->m_dwSize = size; + } + + void ListItem::setDateTimeRaw(const std::string& dateTime) + { + if (dateTime.length() == 10) + { + int year = strtol(dateTime.substr(dateTime.size() - 4).c_str(), nullptr, 10); + int month = strtol(dateTime.substr(3, 4).c_str(), nullptr, 10); + int day = strtol(dateTime.substr(0, 2).c_str(), nullptr, 10); + item->m_dateTime.SetDate(year, month, day); + } + else + item->m_dateTime.SetFromW3CDateTime(dateTime); + } + + void ListItem::setIsFolderRaw(bool isFolder) + { + item->m_bIsFolder = isFolder; + } + + void ListItem::setStartOffsetRaw(double startOffset) + { + // we store the offset in frames, or 1/75th of a second + item->SetStartOffset(CUtil::ConvertSecsToMilliSecs(startOffset)); + } + + void ListItem::setMimeTypeRaw(const std::string& mimetype) + { + item->SetMimeType(mimetype); + } + + void ListItem::setSpecialSortRaw(std::string specialSort) + { + StringUtils::ToLower(specialSort); + + if (specialSort == "bottom") + item->SetSpecialSort(SortSpecialOnBottom); + else if (specialSort == "top") + item->SetSpecialSort(SortSpecialOnTop); + } + + void ListItem::setContentLookupRaw(bool enable) + { + item->SetContentLookup(enable); + } + + void ListItem::addArtRaw(std::string type, const std::string& url) + { + StringUtils::ToLower(type); + item->SetArt(type, url); + } + + void ListItem::addPropertyRaw(std::string type, const CVariant& value) + { + StringUtils::ToLower(type); + item->SetProperty(type, value); + } + + void ListItem::addSubtitlesRaw(const std::vector<std::string>& subtitles) + { + for (size_t i = 0; i < subtitles.size(); ++i) + // subtitle:{} index starts from 1 + addPropertyRaw(StringUtils::Format("subtitle:{}", i + 1), subtitles[i]); + } + } +} diff --git a/xbmc/interfaces/legacy/ListItem.h b/xbmc/interfaces/legacy/ListItem.h new file mode 100644 index 0000000..82513e0 --- /dev/null +++ b/xbmc/interfaces/legacy/ListItem.h @@ -0,0 +1,1291 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "AddonString.h" +#include "Alternative.h" +#include "Dictionary.h" +#include "FileItem.h" +#include "InfoTagGame.h" +#include "InfoTagMusic.h" +#include "InfoTagPicture.h" +#include "InfoTagVideo.h" +#include "ListItem.h" +#include "Tuple.h" +#include "commons/Exception.h" + +#include <map> +#include <utility> +#include <vector> + + +namespace XBMCAddon +{ + namespace xbmcgui + { + XBMCCOMMONS_STANDARD_EXCEPTION(ListItemException); + + // This is a type that represents either a String or a String Tuple + typedef Alternative<StringOrInt,Tuple<String, StringOrInt> > InfoLabelStringOrTuple; + + // This type is either a String or a list of InfoLabelStringOrTuple types + typedef Alternative<StringOrInt, std::vector<InfoLabelStringOrTuple> > InfoLabelValue; + + // The type contains the dictionary values for the ListItem::setInfo call. + // The values in the dictionary can be either a String, or a list of items. + // If it's a list of items then the items can be either a String or a Tuple. + typedef Dictionary<InfoLabelValue> InfoLabelDict; + + // + /// \defgroup python_xbmcgui_listitem ListItem + /// \ingroup python_xbmcgui + /// @{ + /// @brief **Selectable window list item.** + /// + class ListItem : public AddonClass + { + public: +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + CFileItemPtr item; + bool m_offscreen; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief Selectable window list item. + /// + /// The list item control is used for creating item lists in Kodi + /// + /// \python_class{ ListItem([label, label2, path, offscreen]) } + /// + /// @param label [opt] string (default `""`) - the label to display on the item + /// @param label2 [opt] string (default `""`) - the label2 of the item + /// @param path [opt] string (default `""`) - the path for the item + /// @param offscreen [opt] bool (default `False`) - if GUI based locks should be + /// avoided. Most of the times listitems are created + /// offscreen and added later to a container + /// for display (e.g. plugins) or they are not + /// even displayed (e.g. python scrapers). + /// In such cases, there is no need to lock the + /// GUI when creating the items (increasing your addon + /// performance). + /// Note however, that if you are creating listitems + /// and managing the container itself (e.g using + /// WindowXML or WindowXMLDialog classes) subsquent + /// modifications to the item will require locking. + /// Thus, in such cases, use the default value (`False`). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v16 **iconImage** and **thumbnailImage** are deprecated. Use **setArt()**. + /// @python_v18 Added **offscreen** argument. + /// @python_v19 Removed **iconImage** and **thumbnailImage**. Use **setArt()**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem = xbmcgui.ListItem('Casino Royale') + /// ... + /// ~~~~~~~~~~~~~ + /// + ListItem([label, label2, path, offscreen]); +#else + ListItem(const String& label = emptyString, + const String& label2 = emptyString, + const String& path = emptyString, + bool offscreen = false); +#endif + +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + inline explicit ListItem(CFileItemPtr pitem) : item(std::move(pitem)), m_offscreen(false) {} + + static inline AddonClass::Ref<ListItem> fromString(const String& str) + { + AddonClass::Ref<ListItem> ret = AddonClass::Ref<ListItem>(new ListItem()); + ret->item.reset(new CFileItem(str)); + return ret; + } +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + ~ListItem() override; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getLabel() } + /// Returns the listitem label. + /// + /// @return Label of item + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getLabel() + /// label = listitem.getLabel() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel(); +#else + String getLabel(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getLabel2() } + /// Returns the second listitem label. + /// + /// @return Second label of item + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getLabel2() + /// label = listitem.getLabel2() + /// ... + /// ~~~~~~~~~~~~~ + /// + getLabel2(); +#else + String getLabel2(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setLabel(label) } + /// Sets the listitem's label. + /// + /// @param label string or unicode - text string. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setLabel(label) + /// listitem.setLabel('Casino Royale') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel(...); +#else + void setLabel(const String& label); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setLabel2(label) } + /// Sets the listitem's label2. + /// + /// @param label string or unicode - text string. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setLabel2(label) + /// listitem.setLabel2('Casino Royale') + /// ... + /// ~~~~~~~~~~~~~ + /// + setLabel2(...); +#else + void setLabel2(const String& label); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getDateTime() } + /// Returns the list item's datetime in W3C format (YYYY-MM-DDThh:mm:ssTZD). + /// + /// @return string or unicode - datetime string (W3C). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # getDateTime() + /// strDateTime = listitem.getDateTime() + /// ... + /// ~~~~~~~~~~~~~ + /// + getDateTime(); +#else + String getDateTime(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setDateTime(dateTime) } + /// Sets the list item's datetime in W3C format. + /// The following formats are supported: + /// - YYYY + /// - YYYY-MM-DD + /// - YYYY-MM-DDThh:mm[TZD] + /// - YYYY-MM-DDThh:mm:ss[TZD] + /// where the timezone (TZD) is always optional and can be in one of the + /// following formats: + /// - Z (for UTC) + /// - +hh:mm + /// - -hh:mm + /// + /// @param label string or unicode - datetime string (W3C). + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setDate(dateTime) + /// listitem.setDateTime('2021-03-09T12:30:00Z') + /// ... + /// ~~~~~~~~~~~~~ + /// + setDateTime(...); +#else + void setDateTime(const String& dateTime); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setArt(values) } + /// Sets the listitem's art + /// + /// @param values dictionary - pairs of `{ label: value }`. + /// - Some default art values (any string possible): + /// | Label | Type | + /// |:-------------:|:--------------------------------------------------| + /// | thumb | string - image filename + /// | poster | string - image filename + /// | banner | string - image filename + /// | fanart | string - image filename + /// | clearart | string - image filename + /// | clearlogo | string - image filename + /// | landscape | string - image filename + /// | icon | string - image filename + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 New function added. + /// @python_v16 Added new label **icon**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setArt(values) + /// listitem.setArt({ 'poster': 'poster.png', 'banner' : 'banner.png' }) + /// ... + /// ~~~~~~~~~~~~~ + /// + setArt(...); +#else + void setArt(const Properties& dictionary); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setIsFolder(isFolder) } + /// Sets if this listitem is a folder. + /// + /// @param isFolder bool - True=folder / False=not a folder (default). + /// + /// + ///----------------------------------------------------------------------- + /// + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setIsFolder(isFolder) + /// listitem.setIsFolder(True) + /// ... + /// ~~~~~~~~~~~~~ + /// + setIsFolder(...); +#else + void setIsFolder(bool isFolder); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setUniqueIDs(values, defaultrating) } + /// Sets the listitem's uniqueID + /// + /// @param values dictionary - pairs of `{ label: value }`. + /// @param defaultrating [opt] string - the name of default rating. + /// + /// - Some example values (any string possible): + /// | Label | Type | + /// |:-------------:|:--------------------------------------------------| + /// | imdb | string - uniqueid name + /// | tvdb | string - uniqueid name + /// | tmdb | string - uniqueid name + /// | anidb | string - uniqueid name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.setUniqueIDs()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setUniqueIDs(values, defaultrating) + /// listitem.setUniqueIDs({ 'imdb': 'tt8938399', 'tmdb' : '9837493' }, "imdb") + /// ... + /// ~~~~~~~~~~~~~ + /// + setUniqueIDs(...); +#else + void setUniqueIDs(const Properties& dictionary, const String& defaultrating = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setRating(type, rating, votes = 0, defaultt = False) } + /// Sets a listitem's rating. It needs at least type and rating param + /// + /// @param type string - the type of the rating. Any string. + /// @param rating float - the value of the rating. + /// @param votes int - the number of votes. Default 0. + /// @param defaultt bool - is the default rating?. Default False. + /// - Some example type (any string possible): + /// | Label | Type | + /// |:-------------:|:--------------------------------------------------| + /// | imdb | string - rating type + /// | tvdb | string - rating type + /// | tmdb | string - rating type + /// | anidb | string - rating type + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.setRating()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setRating(type, rating, votes, defaultt)) + /// listitem.setRating("imdb", 4.6, 8940, True) + /// ... + /// ~~~~~~~~~~~~~ + /// + setRating(...); +#else + void setRating(const std::string& type, float rating, int votes = 0, bool defaultt = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ addSeason(number, name = "") } + /// Add a season with name to a listitem. It needs at least the season number + /// + /// @param number int - the number of the season. + /// @param name string - the name of the season. Default "". + /// + /// + ///----------------------------------------------------------------------- + /// + /// @python_v18 New function added. + /// @python_v20 Deprecated. Use **InfoTagVideo.addSeason()** or **InfoTagVideo.addSeasons()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # addSeason(number, name)) + /// listitem.addSeason(1, "Murder House") + /// ... + /// ~~~~~~~~~~~~~ + /// + addSeason(...); +#else + void addSeason(int number, std::string name = ""); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getArt(key) } + /// Returns a listitem art path as a string, similar to an infolabel.\n + /// + /// @param key string - art name. + /// - Some default art values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | thumb | string - image path + /// | poster | string - image path + /// | banner | string - image path + /// | fanart | string - image path + /// | clearart | string - image path + /// | clearlogo | string - image path + /// | landscape | string - image path + /// | icon | string - image path + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// poster = listitem.getArt('poster') + /// ... + /// ~~~~~~~~~~~~~ + /// + getArt(key); +#else + String getArt(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ isFolder() } + /// Returns whether the item is a folder or not. + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// isFolder = listitem.isFolder() + /// ... + /// ~~~~~~~~~~~~~ + /// + isFolder(); +#else + bool isFolder() const; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getUniqueID(key) } + /// Returns a listitem uniqueID as a string, similar to an infolabel.\n + /// + /// @param key string - uniqueID name. + /// - Some default uniqueID values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - uniqueid name + /// | tvdb | string - uniqueid name + /// | tmdb | string - uniqueid name + /// | anidb | string - uniqueid name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.getUniqueID()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// uniqueID = listitem.getUniqueID('imdb') + /// ... + /// ~~~~~~~~~~~~~ + /// + getUniqueID(key); +#else + String getUniqueID(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getRating(key) } + /// Returns a listitem rating as a float.\n + /// + /// @param key string - rating type. + /// - Some default key values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - type name + /// | tvdb | string - type name + /// | tmdb | string - type name + /// | anidb | string - type name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.getRating()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// rating = listitem.getRating('imdb') + /// ... + /// ~~~~~~~~~~~~~ + /// + getRating(key); +#else + float getRating(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getVotes(key) } + /// Returns a listitem votes as a integer.\n + /// + /// @param key string - rating type. + /// - Some default key values (any string possible): + /// | Label | Type | + /// |---------------|--------------------------------------------------| + /// | imdb | string - type name + /// | tvdb | string - type name + /// | tmdb | string - type name + /// | anidb | string - type name + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.getVotesAsInt()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// votes = listitem.getVotes('imdb') + /// ... + /// ~~~~~~~~~~~~~ + /// + getVotes(key); +#else + int getVotes(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ select(selected) } + /// Sets the listitem's selected status. + /// + /// @param selected bool - True=selected/False=not selected + /// + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # select(selected) + /// listitem.select(True) + /// ... + /// ~~~~~~~~~~~~~ + /// + select(...); +#else + void select(bool selected); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ isSelected() } + /// Returns the listitem's selected status. + /// + /// + /// @return bool - true if selected, otherwise false + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # isSelected() + /// selected = listitem.isSelected() + /// ... + /// ~~~~~~~~~~~~~ + /// + isSelected(); +#else + bool isSelected(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setInfo(type, infoLabels) } + /// Sets the listitem's infoLabels. + /// + /// @param type string - type of info labels + /// @param infoLabels dictionary - pairs of `{ label: value }` + /// + /// __Available types__ + /// | Command name | Description | + /// |:------------:|:----------------------| + /// | video | Video information + /// | music | Music information + /// | pictures | Pictures informanion + /// | game | Game information + /// + /// @note To set pictures exif info, prepend `exif:` to the label. Exif values must be passed + /// as strings, separate value pairs with a comma. <b>(eg. <c>{'exif:resolution': '720,480'}</c></b> + /// See \ref kodi_pictures_infotag for valid strings.\n + /// \n + /// You can use the above as keywords for arguments and skip certain optional arguments. + /// Once you use a keyword, all following arguments require the keyword. + /// + /// __General Values__ (that apply to all types): + /// | Info label | Description | + /// |--------------:|:---------------------------------------------------| + /// | count | integer (12) - can be used to store an id for later, or for sorting purposes + /// | size | long (1024) - size in bytes + /// | date | string (%d.%m.%Y / 01.01.2009) - file date + /// + /// __Video Values__: + /// | Info label | Description | + /// |--------------:|:---------------------------------------------------| + /// | genre | string (Comedy) or list of strings (["Comedy", "Animation", "Drama"]) + /// | country | string (Germany) or list of strings (["Germany", "Italy", "France"]) + /// | year | integer (2009) + /// | episode | integer (4) + /// | season | integer (1) + /// | sortepisode | integer (4) + /// | sortseason | integer (1) + /// | episodeguide | string (Episode guide) + /// | showlink | string (Battlestar Galactica) or list of strings (["Battlestar Galactica", "Caprica"]) + /// | top250 | integer (192) + /// | setid | integer (14) + /// | tracknumber | integer (3) + /// | rating | float (6.4) - range is 0..10 + /// | userrating | integer (9) - range is 1..10 (0 to reset) + /// | watched | deprecated - use playcount instead + /// | playcount | integer (2) - number of times this item has been played + /// | overlay | integer (2) - range is `0..7`. See \ref kodi_guilib_listitem_iconoverlay "Overlay icon types" for values + /// | cast | list (["Michal C. Hall","Jennifer Carpenter"]) - if provided a list of tuples cast will be interpreted as castandrole + /// | castandrole | list of tuples ([("Michael C. Hall","Dexter"),("Jennifer Carpenter","Debra")]) + /// | director | string (Dagur Kari) or list of strings (["Dagur Kari", "Quentin Tarantino", "Chrstopher Nolan"]) + /// | mpaa | string (PG-13) + /// | plot | string (Long Description) + /// | plotoutline | string (Short Description) + /// | title | string (Big Fan) + /// | originaltitle | string (Big Fan) + /// | sorttitle | string (Big Fan) + /// | duration | integer (245) - duration in seconds + /// | studio | string (Warner Bros.) or list of strings (["Warner Bros.", "Disney", "Paramount"]) + /// | tagline | string (An awesome movie) - short description of movie + /// | writer | string (Robert D. Siegel) or list of strings (["Robert D. Siegel", "Jonathan Nolan", "J.K. Rowling"]) + /// | tvshowtitle | string (Heroes) + /// | premiered | string (2005-03-04) + /// | status | string (Continuing) - status of a TVshow + /// | set | string (Batman Collection) - name of the collection + /// | setoverview | string (All Batman movies) - overview of the collection + /// | tag | string (cult) or list of strings (["cult", "documentary", "best movies"]) - movie tag + /// | imdbnumber | string (tt0110293) - IMDb code + /// | code | string (101) - Production code + /// | aired | string (2008-12-07) + /// | credits | string (Andy Kaufman) or list of strings (["Dagur Kari", "Quentin Tarantino", "Chrstopher Nolan"]) - writing credits + /// | lastplayed | string (%Y-%m-%d %h:%m:%s = 2009-04-05 23:16:04) + /// | album | string (The Joshua Tree) + /// | artist | list (['U2']) + /// | votes | string (12345 votes) + /// | path | string (/home/user/movie.avi) + /// | trailer | string (/home/user/trailer.avi) + /// | dateadded | string (%Y-%m-%d %h:%m:%s = 2009-04-05 23:16:04) + /// | mediatype | string - "video", "movie", "tvshow", "season", "episode" or "musicvideo" + /// | dbid | integer (23) - Only add this for items which are part of the local db. You also need to set the correct 'mediatype'! + /// + /// __Music Values__: + /// | Info label | Description | + /// |-------------------------:|:---------------------------------------------------| + /// | tracknumber | integer (8) + /// | discnumber | integer (2) + /// | duration | integer (245) - duration in seconds + /// | year | integer (1998) + /// | genre | string (Rock) + /// | album | string (Pulse) + /// | artist | string (Muse) + /// | title | string (American Pie) + /// | rating | float - range is between 0 and 10 + /// | userrating | integer - range is 1..10 + /// | lyrics | string (On a dark desert highway...) + /// | playcount | integer (2) - number of times this item has been played + /// | lastplayed | string (%Y-%m-%d %h:%m:%s = 2009-04-05 23:16:04) + /// | mediatype | string - "music", "song", "album", "artist" + /// | dbid | integer (23) - Only add this for items which are part of the local db. You also need to set the correct 'mediatype'! + /// | listeners | integer (25614) + /// | musicbrainztrackid | string (cd1de9af-0b71-4503-9f96-9f5efe27923c) + /// | musicbrainzartistid | string (d87e52c5-bb8d-4da8-b941-9f4928627dc8) + /// | musicbrainzalbumid | string (24944755-2f68-3778-974e-f572a9e30108) + /// | musicbrainzalbumartistid | string (d87e52c5-bb8d-4da8-b941-9f4928627dc8) + /// | comment | string (This is a great song) + /// + /// __Picture Values__: + /// | Info label | Description | + /// |--------------:|:---------------------------------------------------| + /// | title | string (In the last summer-1) + /// | picturepath | string (`/home/username/pictures/img001.jpg`) + /// | exif* | string (See \ref kodi_pictures_infotag for valid strings) + /// + /// __Game Values__: + /// | Info label | Description | + /// |--------------:|:---------------------------------------------------| + /// | title | string (Super Mario Bros.) + /// | platform | string (Atari 2600) + /// | genres | list (["Action","Strategy"]) + /// | publisher | string (Nintendo) + /// | developer | string (Square) + /// | overview | string (Long Description) + /// | year | integer (1980) + /// | gameclient | string (game.libretro.fceumm) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 Added new label **discnumber**. + /// @python_v15 **duration** has to be set in seconds. + /// @python_v16 Added new label **mediatype**. + /// @python_v17 + /// Added labels **setid**, **set**, **imdbnumber**, **code**, **dbid** (video), **path** and **userrating**. + /// Expanded the possible infoLabels for the option **mediatype**. + /// @python_v18 Added new **game** type and associated infolabels. + /// Added labels **dbid** (music), **setoverview**, **tag**, **sortepisode**, **sortseason**, **episodeguide**, **showlink**. + /// Extended labels **genre**, **country**, **director**, **studio**, **writer**, **tag**, **credits** to also use a list of strings. + /// @python_v20 Partially deprecated. Use explicit setters in **InfoTagVideo**, **InfoTagMusic**, **InfoTagPicture** or **InfoTagGame** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.setInfo('video', { 'genre': 'Comedy' }) + /// ... + /// ~~~~~~~~~~~~~ + /// + setInfo(...); +#else + void setInfo(const char* type, const InfoLabelDict& infoLabels); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setCast(actors) } + /// Set cast including thumbnails + /// + /// @param actors list of dictionaries (see below for relevant keys) + /// + /// - Keys: + /// | Label | Description | + /// |--------------:|:------------------------------------------------| + /// | name | string (Michael C. Hall) + /// | role | string (Dexter) + /// | thumbnail | string (http://www.someurl.com/someimage.png) + /// | order | integer (1) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// @python_v20 Deprecated. Use **InfoTagVideo.setCast()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// actors = [{"name": "Actor 1", "role": "role 1"}, {"name": "Actor 2", "role": "role 2"}] + /// listitem.setCast(actors) + /// ... + /// ~~~~~~~~~~~~~ + /// + setCast(...); +#else + void setCast(const std::vector<Properties>& actors); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setAvailableFanart(images) } + /// Set available images (needed for video scrapers) + /// + /// @param images list of dictionaries (see below for relevant keys) + /// + /// - Keys: + /// | Label | Description | + /// |--------------:|:------------------------------------------------| + /// | image | string (http://www.someurl.com/someimage.png) + /// | preview | [opt] string (http://www.someurl.com/somepreviewimage.png) + /// | colors | [opt] string (either comma separated Kodi hex values ("FFFFFFFF,DDDDDDDD") or TVDB RGB Int Triplets ("|68,69,59|69,70,58|78,78,68|")) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// fanart = [{"image": path_to_image_1, "preview": path_to_preview_1}, {"image": path_to_image_2, "preview": path_to_preview_2}] + /// listitem.setAvailableFanart(fanart) + /// ... + /// ~~~~~~~~~~~~~ + /// + setAvailableFanart(...); +#else + void setAvailableFanart(const std::vector<Properties>& images); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ addAvailableArtwork(images) } + /// Add an image to available artworks (needed for video scrapers) + /// + /// @param url string (image path url) + /// @param art_type string (image type) + /// @param preview [opt] string (image preview path url) + /// @param referrer [opt] string (referrer url) + /// @param cache [opt] string (filename in cache) + /// @param post [opt] bool (use post to retrieve the image, default false) + /// @param isgz [opt] bool (use gzip to retrieve the image, default false) + /// @param season [opt] integer (number of season in case of season thumb) + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// @python_v19 New param added (preview). + /// @python_v20 Deprecated. Use **InfoTagVideo.addAvailableArtwork()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.addAvailableArtwork(path_to_image_1, "thumb") + /// ... + /// ~~~~~~~~~~~~~ + /// + addAvailableArtwork(...); +#else + void addAvailableArtwork(const std::string& url, + const std::string& art_type = "", + const std::string& preview = "", + const std::string& referrer = "", + const std::string& cache = "", + bool post = false, + bool isgz = false, + int season = -1); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ addStreamInfo(type, values) } + /// Add a stream with details. + /// + /// @param type string - type of stream(video/audio/subtitle). + /// @param values dictionary - pairs of { label: value }. + /// + /// - Video Values: + /// | Label | Description | + /// |--------------:|:------------------------------------------------| + /// | codec | string (h264) + /// | aspect | float (1.78) + /// | width | integer (1280) + /// | height | integer (720) + /// | duration | integer (seconds) + /// + /// - Audio Values: + /// | Label | Description | + /// |--------------:|:------------------------------------------------| + /// | codec | string (dts) + /// | language | string (en) + /// | channels | integer (2) + /// + /// - Subtitle Values: + /// | Label | Description | + /// |--------------:|:------------------------------------------------| + /// | language | string (en) + /// + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 Deprecated. Use **InfoTagVideo.addVideoStream()**, **InfoTagVideo.addAudioStream()** or **InfoTagVideo.addSubtitleStream()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.addStreamInfo('video', { 'codec': 'h264', 'width' : 1280 }) + /// ... + /// ~~~~~~~~~~~~~ + /// + addStreamInfo(...); +#else + void addStreamInfo(const char* cType, const Properties& dictionary); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ addContextMenuItems([(label, action),*]) } + /// Adds item(s) to the context menu for media lists. + /// + /// @param items list - [(label, action),*] A list of tuples consisting of label and action pairs. + /// - label string or unicode - item's label. + /// - action string or unicode - any available \link page_List_of_built_in_functions built-in function \endlink . + /// + /// @note You can use the above as keywords for arguments and skip certain optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 Completely removed previously available argument **replaceItems**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.addContextMenuItems([('Theater Showtimes', 'RunScript(script.myaddon,title=Iron Man)')]) + /// ... + /// ~~~~~~~~~~~~~ + /// + addContextMenuItems(...); +#else + void addContextMenuItems(const std::vector<Tuple<String,String> >& items, bool replaceItems = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setProperty(key, value) } + /// Sets a listitem property, similar to an infolabel. + /// + /// @param key string - property name. + /// @param value string or unicode - value of property. + /// + /// @note Key is NOT case sensitive.\n + /// You can use the above as keywords for arguments and skip certain optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword.\n + /// \n + /// Some of these are treated internally by Kodi, such as the 'StartOffset' property, which is + /// the offset in seconds at which to start playback of an item. Others may be used in the skin + /// to add extra information, such as 'WatchedCount' for tvshow items + /// + /// - **Internal Properties** + /// | Key | Description | + /// |--------------:|:------------------------------------------------| + /// | inputstream | string (inputstream.adaptive) - Set the inputstream add-on that will be used to play the item + /// | IsPlayable | string - "true", "false" - Mark the item as playable, **mandatory for playable items** + /// | MimeType | string (application/x-mpegURL) - Set the MimeType of the item before playback + /// | ResumeTime | float (1962.0) - Set the resume point of the item in seconds + /// | SpecialSort | string - "top", "bottom" - The item will remain at the top or bottom of the current list + /// | StartOffset | float (60.0) - Set the offset in seconds at which to start playback of the item + /// | StartPercent | float (15.0) - Set the percentage at which to start playback of the item + /// | StationName | string ("My Station Name") - Used to enforce/override MusicPlayer.StationName infolabel from addons (e.g. in radio addons) + /// | TotalTime | float (7848.0) - Set the total time of the item in seconds + /// | OverrideInfotag | string - "true", "false" - When true will override all info from previous listitem + /// | ForceResolvePlugin | string - "true", "false" - When true ensures that a plugin will always receive callbacks to resolve paths (useful for playlist cases) + /// | rtsp_transport | string - "udp", "udp_multicast" or "tcp" - Allow to force the rtsp transport mode for rtsp streams + /// + ///----------------------------------------------------------------------- + /// @python_v20 OverrideInfotag property added + /// @python_v20 **ResumeTime** and **TotalTime** deprecated. Use **InfoTagVideo.setResumePoint()** instead. + /// @python_v20 ForceResolvePlugin property added + /// @python_v20 rtsp_transport property added + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.setProperty('AspectRatio', '1.85 : 1') + /// listitem.setProperty('StartOffset', '256.4') + /// ... + /// ~~~~~~~~~~~~~ + /// + setProperty(...); +#else + void setProperty(const char * key, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setProperties(values) } + /// Sets multiple properties for listitem's + /// + /// @param values dictionary - pairs of `{ label: value }`. + /// + /// @python_v18 New function added. + /// + ///----------------------------------------------------------------------- + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// # setProperties(values) + /// listitem.setProperties({ 'AspectRatio': '1.85', 'StartOffset' : '256.4' }) + /// ... + /// ~~~~~~~~~~~~~ + /// + setProperties(...); +#else + void setProperties(const Properties& dictionary); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getProperty(key) } + /// Returns a listitem property as a string, similar to an infolabel. + /// + /// @param key string - property name. + /// + /// @note Key is NOT case sensitive.\n + /// You can use the above as keywords for arguments and skip certain optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 **ResumeTime** and **TotalTime** deprecated. Use **InfoTagVideo.getResumeTime()** and **InfoTagVideo.getResumeTimeTotal()** instead. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// AspectRatio = listitem.getProperty('AspectRatio') + /// ... + /// ~~~~~~~~~~~~~ + /// + getProperty(...); +#else + String getProperty(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setPath(path) } + /// Sets the listitem's path. + /// + /// @param path string or unicode - path, activated when item is clicked. + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.setPath(path='/path/to/some/file.ext') + /// ... + /// ~~~~~~~~~~~~~ + /// + setPath(...); +#else + void setPath(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setMimeType(mimetype) } + /// Sets the listitem's mimetype if known. + /// + /// @param mimetype string or unicode - mimetype + /// + /// If known prehand, this can (but does not have to) avoid HEAD requests + /// being sent to HTTP servers to figure out file type. + /// + setMimeType(...); +#else + void setMimeType(const String& mimetype); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setContentLookup(enable) } + /// Enable or disable content lookup for item. + /// + /// If disabled, HEAD requests to e.g determine mime type will not be sent. + /// + /// @param enable bool to enable content lookup + /// + /// + ///----------------------------------------------------------------------- + /// @python_v16 New function added. + /// + setContentLookup(...); +#else + void setContentLookup(bool enable); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ setSubtitles(subtitleFiles) } + /// Sets subtitles for this listitem. + /// + /// @param subtitleFiles list with path to subtitle files + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem.setSubtitles(['special://temp/example.srt', 'http://example.com/example.srt']) + /// ... + /// ~~~~~~~~~~~~~ + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + setSubtitles(...); +#else + void setSubtitles(const std::vector<String>& subtitleFiles); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getPath() } + /// Returns the path of this listitem. + /// + /// @return [string] filename + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 New function added. + /// + /// + getPath(); +#else + String getPath(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getVideoInfoTag() } + /// Returns the VideoInfoTag for this item. + /// + /// @return video info tag + /// + /// + ///----------------------------------------------------------------------- + /// @python_v15 New function added. + /// + getVideoInfoTag(); +#else + xbmc::InfoTagVideo* getVideoInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getMusicInfoTag() } + /// Returns the MusicInfoTag for this item. + /// + /// @return music info tag + /// + /// + ///----------------------------------------------------------------------- + /// @python_v15 New function added. + /// + getMusicInfoTag(); +#else + xbmc::InfoTagMusic* getMusicInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getPictureInfoTag() } + /// Returns the InfoTagPicture for this item. + /// + /// @return picture info tag + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getPictureInfoTag(); +#else + xbmc::InfoTagPicture* getPictureInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_listitem + /// @brief \python_func{ getGameInfoTag() } + /// Returns the InfoTagGame for this item. + /// + /// @return game info tag + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getGameInfoTag(); +#else + xbmc::InfoTagGame* getGameInfoTag(); +#endif + +private: + std::vector<std::string> getStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value, + const std::string& separator); + std::vector<std::string> getVideoStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value = ""); + std::vector<std::string> getMusicStringArray(const InfoLabelValue& alt, + const std::string& tag, + std::string value = ""); + + CVideoInfoTag* GetVideoInfoTag(); + const CVideoInfoTag* GetVideoInfoTag() const; + + MUSIC_INFO::CMusicInfoTag* GetMusicInfoTag(); + const MUSIC_INFO::CMusicInfoTag* GetMusicInfoTag() const; + + void setTitleRaw(std::string title); + void setPathRaw(const std::string& path); + void setCountRaw(int count); + void setSizeRaw(int64_t size); + void setDateTimeRaw(const std::string& dateTime); + void setIsFolderRaw(bool isFolder); + void setStartOffsetRaw(double startOffset); + void setMimeTypeRaw(const std::string& mimetype); + void setSpecialSortRaw(std::string specialSort); + void setContentLookupRaw(bool enable); + void addArtRaw(std::string type, const std::string& url); + void addPropertyRaw(std::string type, const CVariant& value); + void addSubtitlesRaw(const std::vector<std::string>& subtitles); + }; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + typedef std::vector<ListItem*> ListItemList; +#endif + } +} diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp new file mode 100644 index 0000000..b9e4c05 --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +//! @todo Need a uniform way of returning an error status + +#include "ModuleXbmc.h" + +#include "AddonUtils.h" +#include "FileItem.h" +#include "GUIInfoManager.h" +#include "LangInfo.h" +#include "LanguageHook.h" +#include "ServiceBroker.h" +#include "Util.h" +#include "aojsonrpc.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationPowerHandling.h" +#include "cores/AudioEngine/Interfaces/AE.h" +#include "guilib/GUIAudioManager.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "guilib/TextureManager.h" +#include "input/WindowTranslator.h" +#include "messaging/ApplicationMessenger.h" +#include "network/Network.h" +#include "network/NetworkServices.h" +#include "playlists/PlayListTypes.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "storage/MediaManager.h" +#include "storage/discs/IDiscDriveHandler.h" +#include "threads/SystemClock.h" +#include "utils/Crc32.h" +#include "utils/ExecString.h" +#include "utils/FileExtensionProvider.h" +#include "utils/FileUtils.h" +#include "utils/LangCodeExpander.h" +#include "utils/MemUtils.h" +#include "utils/StringUtils.h" +#include "utils/SystemInfo.h" +#include "utils/XTimeUtils.h" +#include "utils/log.h" + +#include <vector> + +using namespace KODI; + +namespace XBMCAddon +{ + + namespace xbmc + { + /***************************************************************** + * start of xbmc methods + *****************************************************************/ + void log(const char* msg, int level) + { + // check for a valid loglevel + if (level < LOGDEBUG || level > LOGNONE) + level = LOGDEBUG; + CLog::Log(level, "{}", msg); + } + + void shutdown() + { + XBMC_TRACE; + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_SHUTDOWN); + } + + void restart() + { + XBMC_TRACE; + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_RESTART); + } + + void executescript(const char* script) + { + XBMC_TRACE; + if (! script) + return; + + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_SCRIPT, -1, -1, nullptr, script); + } + + void executebuiltin(const char* function, bool wait /* = false*/) + { + XBMC_TRACE; + if (! function) + return; + + // builtins is no anarchy + // enforce some rules here + // DialogBusy must not be activated, it is modal dialog + const CExecString exec(function); + if (!exec.IsValid()) + return; + + const std::string execute = exec.GetFunction(); + const std::vector<std::string> params = exec.GetParams(); + + if (StringUtils::EqualsNoCase(execute, "activatewindow") || + StringUtils::EqualsNoCase(execute, "closedialog")) + { + int win = CWindowTranslator::TranslateWindow(params[0]); + if (win == WINDOW_DIALOG_BUSY) + { + CLog::Log(LOGWARNING, "addons must not activate DialogBusy"); + return; + } + } + + if (wait) + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, + function); + else + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, + function); + } + + String executeJSONRPC(const char* jsonrpccommand) + { + XBMC_TRACE; + DelayedCallGuard dg; + String ret; + + if (! jsonrpccommand) + return ret; + + // String method = jsonrpccommand; + + CAddOnTransport transport; + CAddOnTransport::CAddOnClient client; + + return JSONRPC::CJSONRPC::MethodCall(/*method*/ jsonrpccommand, &transport, &client); + } + + void sleep(long timemillis) + { + XBMC_TRACE; + + XbmcThreads::EndTime<> endTime{std::chrono::milliseconds(timemillis)}; + while (!endTime.IsTimePast()) + { + LanguageHook* lh = NULL; + { + DelayedCallGuard dcguard; + lh = dcguard.getLanguageHook(); // borrow this + long nextSleep = endTime.GetTimeLeft().count(); + if (nextSleep > 100) + nextSleep = 100; // only sleep for 100 millis + KODI::TIME::Sleep(std::chrono::milliseconds(nextSleep)); + } + if (lh != NULL) + lh->MakePendingCalls(); + } + } + + String getLocalizedString(int id) + { + XBMC_TRACE; + String label; + if (id >= 30000 && id <= 30999) + label = g_localizeStringsTemp.Get(id); + else if (id >= 32000 && id <= 32999) + label = g_localizeStringsTemp.Get(id); + else + label = g_localizeStrings.Get(id); + + return label; + } + + String getSkinDir() + { + XBMC_TRACE; + return CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN); + } + + String getLanguage(int format /* = CLangCodeExpander::ENGLISH_NAME */, bool region /*= false*/) + { + XBMC_TRACE; + std::string lang = g_langInfo.GetEnglishLanguageName(); + + switch (format) + { + case CLangCodeExpander::ENGLISH_NAME: + { + if (region) + { + std::string region = "-" + g_langInfo.GetCurrentRegion(); + return (lang += region); + } + return lang; + } + case CLangCodeExpander::ISO_639_1: + { + std::string langCode; + g_LangCodeExpander.ConvertToISO6391(lang, langCode); + if (region) + { + std::string region = g_langInfo.GetRegionLocale(); + std::string region2Code; + g_LangCodeExpander.ConvertToISO6391(region, region2Code); + region2Code = "-" + region2Code; + return (langCode += region2Code); + } + return langCode; + } + case CLangCodeExpander::ISO_639_2: + { + std::string langCode; + g_LangCodeExpander.ConvertToISO6392B(lang, langCode); + if (region) + { + std::string region = g_langInfo.GetRegionLocale(); + std::string region3Code; + g_LangCodeExpander.ConvertToISO6392B(region, region3Code); + region3Code = "-" + region3Code; + return (langCode += region3Code); + } + + return langCode; + } + default: + return ""; + } + } + + String getIPAddress() + { + XBMC_TRACE; + char cTitleIP[32]; + sprintf(cTitleIP, "127.0.0.1"); + CNetworkInterface* iface = CServiceBroker::GetNetwork().GetFirstConnectedInterface(); + if (iface) + return iface->GetCurrentIPAddress(); + + return cTitleIP; + } + + long getDVDState() + { + XBMC_TRACE; + return static_cast<long>(CServiceBroker::GetMediaManager().GetDriveStatus()); + } + + long getFreeMem() + { + XBMC_TRACE; + KODI::MEMORY::MemoryStatus stat; + KODI::MEMORY::GetMemoryStatus(&stat); + return static_cast<long>(stat.availPhys / ( 1024 * 1024 )); + } + + // getCpuTemp() method + // ## Doesn't work right, use getInfoLabel('System.CPUTemperature') instead. + /*PyDoc_STRVAR(getCpuTemp__doc__, + "getCpuTemp() -- Returns the current cpu temperature as an integer." + "" + "example:" + " - cputemp = xbmc.getCpuTemp()"); + + PyObject* XBMC_GetCpuTemp(PyObject *self, PyObject *args) + { + unsigned short cputemp; + unsigned short cpudec; + + _outp(0xc004, (0x4c<<1)|0x01); + _outp(0xc008, 0x01); + _outpw(0xc000, _inpw(0xc000)); + _outp(0xc002, (0) ? 0x0b : 0x0a); + while ((_inp(0xc000) & 8)); + cputemp = _inpw(0xc006); + + _outp(0xc004, (0x4c<<1)|0x01); + _outp(0xc008, 0x10); + _outpw(0xc000, _inpw(0xc000)); + _outp(0xc002, (0) ? 0x0b : 0x0a); + while ((_inp(0xc000) & 8)); + cpudec = _inpw(0xc006); + + if (cpudec<10) cpudec = cpudec * 100; + if (cpudec<100) cpudec = cpudec *10; + + return PyInt_FromLong((long)(cputemp + cpudec / 1000.0f)); + }*/ + + String getInfoLabel(const char* cLine) + { + XBMC_TRACE; + if (!cLine) + { + String ret; + return ret; + } + + CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager(); + int ret = infoMgr.TranslateString(cLine); + //doesn't seem to be a single InfoTag? + //try full blown GuiInfoLabel then + if (ret == 0) + return GUILIB::GUIINFO::CGUIInfoLabel::GetLabel(cLine, INFO::DEFAULT_CONTEXT); + else + return infoMgr.GetLabel(ret, INFO::DEFAULT_CONTEXT); + } + + String getInfoImage(const char * infotag) + { + XBMC_TRACE; + if (!infotag) + { + String ret; + return ret; + } + + CGUIInfoManager& infoMgr = CServiceBroker::GetGUI()->GetInfoManager(); + int ret = infoMgr.TranslateString(infotag); + return infoMgr.GetImage(ret, WINDOW_INVALID); + } + + void playSFX(const char* filename, bool useCached) + { + XBMC_TRACE; + if (!filename) + return; + + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (CFileUtils::Exists(filename) && gui) + { + gui->GetAudioManager().PlayPythonSound(filename,useCached); + } + } + + void stopSFX() + { + XBMC_TRACE; + DelayedCallGuard dg; + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (gui) + gui->GetAudioManager().Stop(); + } + + void enableNavSounds(bool yesNo) + { + XBMC_TRACE; + CGUIComponent* gui = CServiceBroker::GetGUI(); + if (gui) + gui->GetAudioManager().Enable(yesNo); + } + + bool getCondVisibility(const char *condition) + { + XBMC_TRACE; + if (!condition) + return false; + + bool ret; + { + XBMCAddonUtils::GuiLock lock(nullptr, false); + + int id = CServiceBroker::GetGUI()->GetWindowManager().GetTopmostModalDialog(); + if (id == WINDOW_INVALID) + id = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(); + ret = CServiceBroker::GetGUI()->GetInfoManager().EvaluateBool(condition,id); + } + + return ret; + } + + int getGlobalIdleTime() + { + XBMC_TRACE; + auto& components = CServiceBroker::GetAppComponents(); + const auto appPower = components.GetComponent<CApplicationPowerHandling>(); + return appPower->GlobalIdleTime(); + } + + String getCacheThumbName(const String& path) + { + XBMC_TRACE; + auto crc = Crc32::ComputeFromLowerCase(path); + return StringUtils::Format("{:08x}.tbn", crc); + } + + Tuple<String,String> getCleanMovieTitle(const String& path, bool usefoldername) + { + XBMC_TRACE; + CFileItem item(path, false); + std::string strName = item.GetMovieName(usefoldername); + + std::string strTitleAndYear; + std::string strTitle; + std::string strYear; + CUtil::CleanString(strName, strTitle, strTitleAndYear, strYear, usefoldername); + return Tuple<String,String>(strTitle,strYear); + } + + String getRegion(const char* id) + { + XBMC_TRACE; + std::string result; + + if (StringUtils::CompareNoCase(id, "datelong") == 0) + { + result = g_langInfo.GetDateFormat(true); + StringUtils::Replace(result, "DDDD", "%A"); + StringUtils::Replace(result, "MMMM", "%B"); + StringUtils::Replace(result, "D", "%d"); + StringUtils::Replace(result, "YYYY", "%Y"); + } + else if (StringUtils::CompareNoCase(id, "dateshort") == 0) + { + result = g_langInfo.GetDateFormat(false); + StringUtils::Replace(result, "MM", "%m"); + StringUtils::Replace(result, "DD", "%d"); +#ifdef TARGET_WINDOWS + StringUtils::Replace(result, "M", "%#m"); + StringUtils::Replace(result, "D", "%#d"); +#else + StringUtils::Replace(result, "M", "%-m"); + StringUtils::Replace(result, "D", "%-d"); +#endif + StringUtils::Replace(result, "YYYY", "%Y"); + } + else if (StringUtils::CompareNoCase(id, "tempunit") == 0) + result = g_langInfo.GetTemperatureUnitString(); + else if (StringUtils::CompareNoCase(id, "speedunit") == 0) + result = g_langInfo.GetSpeedUnitString(); + else if (StringUtils::CompareNoCase(id, "time") == 0) + { + result = g_langInfo.GetTimeFormat(); + if (StringUtils::StartsWith(result, "HH")) + StringUtils::Replace(result, "HH", "%H"); + else + StringUtils::Replace(result, "H", "%H"); + StringUtils::Replace(result, "h", "%I"); + StringUtils::Replace(result, "mm", "%M"); + StringUtils::Replace(result, "ss", "%S"); + StringUtils::Replace(result, "xx", "%p"); + } + else if (StringUtils::CompareNoCase(id, "meridiem") == 0) + result = StringUtils::Format("{}/{}", g_langInfo.GetMeridiemSymbol(MeridiemSymbolAM), + g_langInfo.GetMeridiemSymbol(MeridiemSymbolPM)); + + return result; + } + + //! @todo Add a mediaType enum + String getSupportedMedia(const char* mediaType) + { + XBMC_TRACE; + String result; + if (StringUtils::CompareNoCase(mediaType, "video") == 0) + result = CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(); + else if (StringUtils::CompareNoCase(mediaType, "music") == 0) + result = CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(); + else if (StringUtils::CompareNoCase(mediaType, "picture") == 0) + result = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(); + + //! @todo implement + // else + // return an error + + return result; + } + + bool skinHasImage(const char* image) + { + XBMC_TRACE; + return CServiceBroker::GetGUI()->GetTextureManager().HasTexture(image); + } + + + bool startServer(int iTyp, bool bStart) + { + XBMC_TRACE; + DelayedCallGuard dg; + return CServiceBroker::GetNetwork().GetServices().StartServer( + static_cast<CNetworkServices::ESERVERS>(iTyp), bStart != 0); + } + + void audioSuspend() + { + IAE *ae = CServiceBroker::GetActiveAE(); + if (ae) + ae->Suspend(); + } + + void audioResume() + { + IAE *ae = CServiceBroker::GetActiveAE(); + if (ae) + ae->Resume(); + } + + String convertLanguage(const char* language, int format) + { + std::string convertedLanguage; + switch (format) + { + case CLangCodeExpander::ENGLISH_NAME: + { + g_LangCodeExpander.Lookup(language, convertedLanguage); + // maybe it's a check whether the language exists or not + if (convertedLanguage.empty()) + { + g_LangCodeExpander.ConvertToISO6392B(language, convertedLanguage); + g_LangCodeExpander.Lookup(convertedLanguage, convertedLanguage); + } + break; + } + case CLangCodeExpander::ISO_639_1: + g_LangCodeExpander.ConvertToISO6391(language, convertedLanguage); + break; + case CLangCodeExpander::ISO_639_2: + g_LangCodeExpander.ConvertToISO6392B(language, convertedLanguage); + break; + default: + return ""; + } + return convertedLanguage; + } + + String getUserAgent() + { + return CSysInfo::GetUserAgent(); + } + + int getSERVER_WEBSERVER() + { + return CNetworkServices::ES_WEBSERVER; + } + int getSERVER_AIRPLAYSERVER() + { + return CNetworkServices::ES_AIRPLAYSERVER; + } + int getSERVER_UPNPSERVER() + { + return CNetworkServices::ES_UPNPSERVER; + } + int getSERVER_UPNPRENDERER() + { + return CNetworkServices::ES_UPNPRENDERER; + } + int getSERVER_EVENTSERVER() + { + return CNetworkServices::ES_EVENTSERVER; + } + int getSERVER_JSONRPCSERVER() + { + return CNetworkServices::ES_JSONRPCSERVER; + } + int getSERVER_ZEROCONF() + { + return CNetworkServices::ES_ZEROCONF; + } + + int getPLAYLIST_MUSIC() + { + return PLAYLIST::TYPE_MUSIC; + } + int getPLAYLIST_VIDEO() + { + return PLAYLIST::TYPE_VIDEO; + } + int getTRAY_OPEN() + { + return static_cast<int>(TrayState::OPEN); + } + int getDRIVE_NOT_READY() + { + return static_cast<int>(DriveState::NOT_READY); + } + int getTRAY_CLOSED_NO_MEDIA() + { + return static_cast<int>(TrayState::CLOSED_NO_MEDIA); + } + int getTRAY_CLOSED_MEDIA_PRESENT() + { + return static_cast<int>(TrayState::CLOSED_MEDIA_PRESENT); + } + int getLOGDEBUG() { return LOGDEBUG; } + int getLOGINFO() { return LOGINFO; } + int getLOGWARNING() { return LOGWARNING; } + int getLOGERROR() { return LOGERROR; } + int getLOGFATAL() { return LOGFATAL; } + int getLOGNONE() { return LOGNONE; } + + // language string formats + int getISO_639_1() { return CLangCodeExpander::ISO_639_1; } + int getISO_639_2(){ return CLangCodeExpander::ISO_639_2; } + int getENGLISH_NAME() { return CLangCodeExpander::ENGLISH_NAME; } + + const int lLOGDEBUG = LOGDEBUG; + } +} diff --git a/xbmc/interfaces/legacy/ModuleXbmc.h b/xbmc/interfaces/legacy/ModuleXbmc.h new file mode 100644 index 0000000..0720d8a --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmc.h @@ -0,0 +1,898 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonString.h" +#include "Tuple.h" + +#include "utils/LangCodeExpander.h" +#include "swighelper.h" +#include <vector> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace XBMCAddon +{ + namespace xbmc + { +#ifndef SWIG + // This is a bit of a hack to get around a SWIG problem + extern const int lLOGDEBUG; +#endif +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + + // + /// \defgroup python_xbmc Library - xbmc + /// @{ + /// @brief **General functions on Kodi.** + /// + /// Offers classes and functions that provide information about the media + /// currently playing and that allow manipulation of the media player (such + /// as starting a new song). You can also find system information using the + /// functions available in this library. + // + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.log(msg[, level]) } + /// Write a string to Kodi's log file and the debug window. + /// + /// @param msg string - text to output. + /// @param level [opt] integer - log level to output at. + /// <em>(default=LOGDEBUG)</em> + /// | Value: | Description: | + /// |----------------:|---------------------------------------------------| + /// | xbmc.LOGDEBUG | In depth information about the status of Kodi. This information can pretty much only be deciphered by a developer or long time Kodi power user. + /// | xbmc.LOGINFO | Something has happened. It's not a problem, we just thought you might want to know. Fairly excessive output that most people won't care about. + /// | xbmc.LOGWARNING | Something potentially bad has happened. If Kodi did something you didn't expect, this is probably why. Watch for errors to follow. + /// | xbmc.LOGERROR | This event is bad. Something has failed. You likely noticed problems with the application be it skin artifacts, failure of playback a crash, etc. + /// | xbmc.LOGFATAL | We're screwed. Kodi is about to crash. + /// + /// @note Addon developers are advised to keep `LOGDEBUG` as the default + /// logging level and to use conservative logging (log only if needed). + /// Excessive logging makes it harder to debug kodi itself. + /// + /// Logging in kodi has a global configuration level that controls how text + /// is written to the log. This global logging behaviour can be changed in + /// the GUI (**Settings -> System -> Logging**) (debug toggle) or furthered + /// configured in advancedsettings (loglevel setting). + /// + /// Text is written to the log for the following conditions: + /// - loglevel == -1 (NONE, nothing at all is logged to the log) + /// - loglevel == 0 (NORMAL, shows `LOGINFO`, `LOGWARNING`, `LOGERROR` and `LOGFATAL`) - Default kodi behaviour + /// - loglevel == 1 (DEBUG, shows all) - Behaviour if you toggle debug log in the GUI + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v17 Default level changed from `LOGNOTICE` to `LOGDEBUG` + /// @python_v19 Removed `LOGNOTICE` (use `LOGINFO`) and `LOGSEVERE` (use `LOGFATAL`) + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.log(msg='This is a test string.', level=xbmc.LOGDEBUG); + /// .. + /// ~~~~~~~~~~~~~ + /// + log(...); +#else + void log(const char* msg, int level = lLOGDEBUG); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.shutdown() } + /// Shutdown the htpc. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.shutdown() + /// .. + /// ~~~~~~~~~~~~~ + /// + shutdown(); +#else + void shutdown(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.restart() } + /// Restart the htpc. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.restart() + /// .. + /// ~~~~~~~~~~~~~ + /// + restart(); +#else + void restart(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.executescript(script) } + /// Execute a python script. + /// + /// @param script string - script filename to execute. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.executescript('special://home/scripts/update.py') + /// .. + /// ~~~~~~~~~~~~~ + /// + executescript(...); +#else + void executescript(const char* script); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.executebuiltin(function) } + /// Execute a built in Kodi function. + /// + /// @param function string - builtin function to execute. + /// @param wait [opt] bool - If Kodi should wait for the + /// builtin function execution to finish + /// (default False) + /// + /// + /// \ref page_List_of_built_in_functions "List of builtin functions" + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.executebuiltin('Skin.SetString(abc,def)') + /// .. + /// ~~~~~~~~~~~~~ + /// + executebuiltin(...); +#else + void executebuiltin(const char* function, bool wait = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.executeJSONRPC(jsonrpccommand) } + /// Execute an JSONRPC command. + /// + /// @param jsonrpccommand string - jsonrpc command to execute. + /// @return jsonrpc return string + /// + /// + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// response = xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "JSONRPC.Introspect", "id": 1 }') + /// .. + /// ~~~~~~~~~~~~~ + /// + executeJSONRPC(...); +#else + String executeJSONRPC(const char* jsonrpccommand); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.sleep(time) } + /// Sleeps for 'time' (msec). + /// \anchor xbmc_Sleep + /// + /// @param time integer - number of msec to sleep. + /// + /// @throws PyExc_TypeError If time is not an integer. + /// + /// @warning This is useful if you need to sleep for a small amount of time + /// (milisecond range) somewhere in your addon logic. Please note that Kodi + /// will attempt to stop any running scripts when signaled to exit and wait for a maximum + /// of 5 seconds before trying to force stop your script. If your addon makes use + /// of \ref xbmc_Sleep "xbmc.sleep()" incorrectly (long periods of time, e.g. that exceed + /// the force stop waiting time) it may lead to Kodi hanging on shutdown. + /// In case your addon needs long sleep/idle periods use + /// \ref xbmc_Monitor_waitForAbort "xbmc.Monitor().waitForAbort(secs)" + /// instead. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.sleep(2000) # sleeps for 2 seconds + /// .. + /// ~~~~~~~~~~~~~ + /// + sleep(...); +#else + void sleep(long timemillis); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getLocalizedString(id) } + /// Get a localized 'unicode string'. + /// + /// @param id integer - id# for string you want to + /// localize. + /// @return Localized 'unicode string' + /// + /// @note See strings.po in `\language\{yourlanguage}\` for which id + /// you need for a string. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// locstr = xbmc.getLocalizedString(6) + /// .. + /// ~~~~~~~~~~~~~ + /// + getLocalizedString(...); +#else + String getLocalizedString(int id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getSkinDir() } + /// Get the active skin directory. + /// + /// @return The active skin directory as a string + /// + /// + /// @note This is not the full path like 'special://home/addons/MediaCenter', + /// but only 'MediaCenter'. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// skindir = xbmc.getSkinDir() + /// .. + /// ~~~~~~~~~~~~~ + /// + getSkinDir(); +#else + String getSkinDir(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getLanguage([format], [region]) } + /// Get the active language. + /// + /// @param format [opt] format of the returned language + /// string + /// | Value | Description + /// |------------------:|:-------------------------------------------------| + /// | xbmc.ISO_639_1 | Two letter code as defined in ISO 639-1 + /// | xbmc.ISO_639_2 | Three letter code as defined in ISO 639-2/T or ISO 639-2/B + /// | xbmc.ENGLISH_NAME | Full language name in English (default) + /// @param region [opt] append the region delimited by "-" + /// of the language (setting) to the + /// returned language string + /// @return The active language as a string + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v13 Added new options **format** and **region**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// language = xbmc.getLanguage(xbmc.ENGLISH_NAME) + /// .. + /// ~~~~~~~~~~~~~ + /// + getLanguage(...); +#else + String getLanguage(int format = CLangCodeExpander::ENGLISH_NAME, bool region = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getIPAddress() } + /// Get the current ip address. + /// + /// @return The current ip address as a string + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// ip = xbmc.getIPAddress() + /// .. + /// ~~~~~~~~~~~~~ + /// + getIPAddress(); +#else + String getIPAddress(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getDVDState() } + /// Returns the dvd state as an integer. + /// + /// @return Values for state are: + /// | Value | Name | + /// |------:|:-------------------------------| + /// | 1 | xbmc.DRIVE_NOT_READY + /// | 16 | xbmc.TRAY_OPEN + /// | 64 | xbmc.TRAY_CLOSED_NO_MEDIA + /// | 96 | xbmc.TRAY_CLOSED_MEDIA_PRESENT + /// + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dvdstate = xbmc.getDVDState() + /// .. + /// ~~~~~~~~~~~~~ + /// + getDVDState(); +#else + long getDVDState(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getFreeMem() } + /// Get amount of free memory in MB. + /// + /// @return The amount of free memory in MB as an integer + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// freemem = xbmc.getFreeMem() + /// .. + /// ~~~~~~~~~~~~~ + /// + getFreeMem(); +#else + long getFreeMem(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getInfoLabel(infotag) } + /// Get a info label + /// + /// @param infotag string - infoTag for value you want + /// returned. + /// @return InfoLabel as a string + /// + /// \ref modules__infolabels_boolean_conditions "List of InfoTags" + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// label = xbmc.getInfoLabel('Weather.Conditions') + /// .. + /// ~~~~~~~~~~~~~ + /// + getInfoLabel(...); +#else + String getInfoLabel(const char* cLine); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getInfoImage(infotag) } + /// Get filename including path to the InfoImage's thumbnail. + /// + /// @param infotag string - infotag for value you want + /// returned + /// @return Filename including path to the + /// InfoImage's thumbnail as a string + /// + /// + /// List of InfoTags - http://kodi.wiki/view/InfoLabels + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// filename = xbmc.getInfoImage('Weather.Conditions') + /// .. + /// ~~~~~~~~~~~~~ + /// + getInfoImage(...); +#else + String getInfoImage(const char * infotag); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.playSFX(filename,[useCached]) } + /// Plays a wav file by filename + /// + /// @param filename string - filename of the wav file to + /// play + /// @param useCached [opt] bool - False = Dump any + /// previously cached wav associated with + /// filename + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v14 Added new option **useCached**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.playSFX('special://xbmc/scripts/dingdong.wav') + /// xbmc.playSFX('special://xbmc/scripts/dingdong.wav',False) + /// .. + /// ~~~~~~~~~~~~~ + /// + playSFX(...); +#else + void playSFX(const char* filename, bool useCached = true); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.stopSFX() } + /// Stops wav file + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v14 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.stopSFX() + /// .. + /// ~~~~~~~~~~~~~ + /// + stopSFX(); +#else + void stopSFX(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.enableNavSounds(yesNo) } + /// Enables/Disables nav sounds + /// + /// @param yesNo bool - enable (True) or disable + /// (False) nav sounds + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.enableNavSounds(True) + /// .. + /// ~~~~~~~~~~~~~ + /// + enableNavSounds(...); +#else + void enableNavSounds(bool yesNo); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getCondVisibility(condition) } + /// Get visibility conditions + /// + /// @param condition string - condition to check + /// @return True (if the condition is verified) or False (otherwise) + /// + /// \ref modules__infolabels_boolean_conditions "List of boolean conditions" + /// + /// @note You can combine two (or more) of the above settings by using <b>"+"</b> as an AND operator, + /// <b>"|"</b> as an OR operator, <b>"!"</b> as a NOT operator, and <b>"["</b> and <b>"]"</b> to bracket expressions. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// visible = xbmc.getCondVisibility('[Control.IsVisible(41) + !Control.IsVisible(12)]') + /// .. + /// ~~~~~~~~~~~~~ + /// + getCondVisibility(...); +#else + bool getCondVisibility(const char *condition); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getGlobalIdleTime() } + /// Get the elapsed idle time in seconds. + /// + /// @return Elapsed idle time in seconds as an integer + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// t = xbmc.getGlobalIdleTime() + /// .. + /// ~~~~~~~~~~~~~ + /// + getGlobalIdleTime(); +#else + int getGlobalIdleTime(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getCacheThumbName(path) } + /// Get thumb cache filename. + /// + /// @param path string - path to file + /// @return Thumb cache filename + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// thumb = xbmc.getCacheThumbName('f:\\videos\\movie.avi') + /// .. + /// ~~~~~~~~~~~~~ + /// + getCacheThumbName(...); +#else + String getCacheThumbName(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getCleanMovieTitle(path[, usefoldername]) } + /// Get clean movie title and year string if available. + /// + /// @param path string - String to clean + /// @param usefoldername [opt] bool - use folder names (defaults + /// to false) + /// @return Clean movie title and year string if + /// available. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// title, year = xbmc.getCleanMovieTitle('/path/to/moviefolder/test.avi', True) + /// .. + /// ~~~~~~~~~~~~~ + /// + getCleanMovieTitle(...); +#else + Tuple<String,String> getCleanMovieTitle(const String& path, bool usefoldername = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getRegion(id) } + /// Returns your regions setting as a string for the specified id. + /// + /// @param id string - id of setting to return + /// @return Region setting + /// + /// @note choices are (dateshort, datelong, time, meridiem, tempunit, speedunit) + /// You can use the above as keywords for arguments. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// date_long_format = xbmc.getRegion('datelong') + /// .. + /// ~~~~~~~~~~~~~ + /// + getRegion(...); +#else + String getRegion(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getSupportedMedia(media) } + /// Get the supported file types for the specific media. + /// + /// @param media string - media type + /// @return Supported file types for the specific + /// media as a string + /// + /// + /// @note Media type can be (video, music, picture). + /// The return value is a pipe separated string of filetypes + /// (eg. '.mov |.avi').\n + /// You can use the above as keywords for arguments. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// mTypes = xbmc.getSupportedMedia('video') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSupportedMedia(...); +#else + String getSupportedMedia(const char* mediaType); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.skinHasImage(image) } + /// Check skin for presence of Image. + /// + /// @param image string - image filename + /// @return True if the image file exists in the skin + /// + /// + /// @note If the media resides in a subfolder include it. (eg. home-myfiles\\home-myfiles2.png). + /// You can use the above as keywords for arguments. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// exists = xbmc.skinHasImage('ButtonFocusedTexture.png') + /// .. + /// ~~~~~~~~~~~~~ + /// + skinHasImage(...); +#else + bool skinHasImage(const char* image); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmc + /// + /// @brief \python_func{ xbmc.startServer(typ, bStart, bWait) } + /// Start or stop a server. + /// + /// @param typ integer - use SERVER_* constants + /// - Used format of the returned language string + /// | Value | Description | + /// |--------------------------:|------------------------------------------------------------| + /// | xbmc.SERVER_WEBSERVER | [To control Kodi's builtin webserver](http://kodi.wiki/view/Webserver) + /// | xbmc.SERVER_AIRPLAYSERVER | [AirPlay is a proprietary protocol stack/suite developed by Apple Inc.](http://kodi.wiki/view/AirPlay) + /// | xbmc.SERVER_JSONRPCSERVER | [Control JSON-RPC HTTP/TCP socket-based interface](http://kodi.wiki/view/JSON-RPC_API) + /// | xbmc.SERVER_UPNPRENDERER | [UPnP client (aka UPnP renderer)](http://kodi.wiki/view/UPnP/Client) + /// | xbmc.SERVER_UPNPSERVER | [Control built-in UPnP A/V media server (UPnP-server)](http://kodi.wiki/view/UPnP/Server) + /// | xbmc.SERVER_EVENTSERVER | [Set eventServer part that accepts remote device input on all platforms](http://kodi.wiki/view/EventServer) + /// | xbmc.SERVER_ZEROCONF | [Control Kodi's Avahi Zeroconf](http://kodi.wiki/view/Zeroconf) + /// @param bStart bool - start (True) or stop (False) a server + /// @return bool - True or False + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v20 Removed option **bWait**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.startServer(xbmc.SERVER_AIRPLAYSERVER, False) + /// .. + /// ~~~~~~~~~~~~~ + /// + startServer(...); +#else + bool startServer(int iTyp, bool bStart); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.audioSuspend() } + /// Suspend Audio engine. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.audioSuspend() + /// .. + /// ~~~~~~~~~~~~~ + /// + audioSuspend(); +#else + void audioSuspend(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.audioResume() } + /// Resume Audio engine. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.audioResume() + /// .. + /// ~~~~~~~~~~~~~ + /// + audioResume(); +#else + void audioResume(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.getUserAgent() } + /// @brief Returns Kodi's HTTP UserAgent string + /// + /// @return HTTP user agent + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmc.getUserAgent() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// example output: + /// Kodi/17.0-ALPHA1 (X11; Linux x86_64) Ubuntu/15.10 App_Bitness/64 Version/17.0-ALPHA1-Git:2015-12-23-5770d28 + /// + getUserAgent(); +#else + String getUserAgent(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc + /// @brief \python_func{ xbmc.convertLanguage(language, format) } + /// @brief Returns the given language converted to the given format as a + /// string. + /// + /// @param language string either as name in English, two + /// letter code (ISO 639-1), or three + /// letter code (ISO 639-2/T(B) + /// @param format format of the returned language string + /// | Value | Description + /// |------------------:|:-------------------------------------------------| + /// | xbmc.ISO_639_1 | Two letter code as defined in ISO 639-1 + /// | xbmc.ISO_639_2 | Three letter code as defined in ISO 639-2/T or ISO 639-2/B + /// | xbmc.ENGLISH_NAME | Full language name in English (default) + /// @return Converted Language string + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v13 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// language = xbmc.convertLanguage(English, xbmc.ISO_639_2) + /// .. + /// ~~~~~~~~~~~~~ + /// + convertLanguage(...); +#else + String convertLanguage(const char* language, int format); +#endif + //@} +#ifndef DOXYGEN_SHOULD_SKIP_THIS + SWIG_CONSTANT_FROM_GETTER(int, SERVER_WEBSERVER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_AIRPLAYSERVER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_UPNPSERVER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_UPNPRENDERER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_EVENTSERVER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_JSONRPCSERVER); + SWIG_CONSTANT_FROM_GETTER(int, SERVER_ZEROCONF); + + SWIG_CONSTANT_FROM_GETTER(int, PLAYLIST_MUSIC); + SWIG_CONSTANT_FROM_GETTER(int, PLAYLIST_VIDEO); + SWIG_CONSTANT_FROM_GETTER(int, TRAY_OPEN); + SWIG_CONSTANT_FROM_GETTER(int, DRIVE_NOT_READY); + SWIG_CONSTANT_FROM_GETTER(int, TRAY_CLOSED_NO_MEDIA); + SWIG_CONSTANT_FROM_GETTER(int, TRAY_CLOSED_MEDIA_PRESENT); + SWIG_CONSTANT_FROM_GETTER(int, LOGDEBUG); + SWIG_CONSTANT_FROM_GETTER(int, LOGINFO); + SWIG_CONSTANT_FROM_GETTER(int, LOGWARNING); + SWIG_CONSTANT_FROM_GETTER(int, LOGERROR); + SWIG_CONSTANT_FROM_GETTER(int, LOGFATAL); + SWIG_CONSTANT_FROM_GETTER(int, LOGNONE); + + SWIG_CONSTANT_FROM_GETTER(int, ISO_639_1); + SWIG_CONSTANT_FROM_GETTER(int, ISO_639_2); + SWIG_CONSTANT_FROM_GETTER(int, ENGLISH_NAME); +#if 0 + void registerMonitor(Monitor* monitor); + void unregisterMonitor(Monitor* monitor); +#endif + } +} +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/xbmc/interfaces/legacy/ModuleXbmcgui.cpp b/xbmc/interfaces/legacy/ModuleXbmcgui.cpp new file mode 100644 index 0000000..104a755 --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcgui.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ModuleXbmcgui.h" + +#include "LanguageHook.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "windowing/GraphicContext.h" + +#include <mutex> + +#define NOTIFICATION_INFO "info" +#define NOTIFICATION_WARNING "warning" +#define NOTIFICATION_ERROR "error" + +namespace XBMCAddon +{ + namespace xbmcgui + { + long getCurrentWindowId() + { + DelayedCallGuard dg; + std::unique_lock<CCriticalSection> gl(CServiceBroker::GetWinSystem()->GetGfxContext()); + return CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow(); + } + + long getCurrentWindowDialogId() + { + DelayedCallGuard dg; + std::unique_lock<CCriticalSection> gl(CServiceBroker::GetWinSystem()->GetGfxContext()); + return CServiceBroker::GetGUI()->GetWindowManager().GetTopmostModalDialog(); + } + + long getScreenHeight() + { + XBMC_TRACE; + return CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight(); + } + + long getScreenWidth() + { + XBMC_TRACE; + return CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth(); + } + + const char* getNOTIFICATION_INFO() { return NOTIFICATION_INFO; } + const char* getNOTIFICATION_WARNING() { return NOTIFICATION_WARNING; } + const char* getNOTIFICATION_ERROR() { return NOTIFICATION_ERROR; } + + } +} diff --git a/xbmc/interfaces/legacy/ModuleXbmcgui.h b/xbmc/interfaces/legacy/ModuleXbmcgui.h new file mode 100644 index 0000000..d2d9558 --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcgui.h @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "guilib/GUIEditControl.h" +#include "swighelper.h" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace XBMCAddon +{ + namespace xbmcgui + { +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + + // + /// \defgroup python_xbmcgui Library - xbmcgui + /// @{ + /// @brief **GUI functions on Kodi.** + /// + /// Offers classes and functions that manipulate the Graphical User + /// Interface through windows, dialogs, and various control widgets. + // + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui + /// @brief \python_func{ xbmcgui.getCurrentWindowId() } + /// Returns the id for the current 'active' window as an integer. + /// + /// @return The currently active window Id + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// wid = xbmcgui.getCurrentWindowId() + /// .. + /// ~~~~~~~~~~~~~ + /// + getCurrentWindowId(); +#else + long getCurrentWindowId(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui + /// @brief \python_func{ xbmcgui.getCurrentWindowDialogId() } + /// Returns the id for the current 'active' dialog as an integer. + /// + /// @return The currently active dialog Id + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// wid = xbmcgui.getCurrentWindowDialogId() + /// .. + /// ~~~~~~~~~~~~~ + /// + getCurrentWindowDialogId(); +#else + long getCurrentWindowDialogId(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui + /// @brief \python_func{ getScreenHeight() } + /// Returns the height of this screen. + /// + /// @return Screen height + /// + /// + ///------------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getScreenHeight(); +#else + long getScreenHeight(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui + /// @brief \python_func{ getScreenWidth() } + /// Returns the width of this screen. + /// + /// @return Screen width + /// + /// + ///------------------------------------------------------------------------- + /// @python_v18 New function added. + /// + getScreenWidth(); +#else + long getScreenWidth(); +#endif + ///@} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + SWIG_CONSTANT2(int, ICON_OVERLAY_NONE, CGUIListItem::ICON_OVERLAY_NONE); + SWIG_CONSTANT2(int, ICON_OVERLAY_RAR, CGUIListItem::ICON_OVERLAY_RAR); + SWIG_CONSTANT2(int, ICON_OVERLAY_ZIP, CGUIListItem::ICON_OVERLAY_ZIP); + SWIG_CONSTANT2(int, ICON_OVERLAY_LOCKED, CGUIListItem::ICON_OVERLAY_LOCKED); + SWIG_CONSTANT2(int, ICON_OVERLAY_UNWATCHED, CGUIListItem::ICON_OVERLAY_UNWATCHED); + SWIG_CONSTANT2(int, ICON_OVERLAY_WATCHED, CGUIListItem::ICON_OVERLAY_WATCHED); + SWIG_CONSTANT2(int, ICON_OVERLAY_HD, CGUIListItem::ICON_OVERLAY_HD); + + SWIG_CONSTANT2(int, INPUT_TYPE_TEXT, CGUIEditControl::INPUT_TYPE_TEXT); + SWIG_CONSTANT2(int, INPUT_TYPE_NUMBER, CGUIEditControl::INPUT_TYPE_NUMBER); + SWIG_CONSTANT2(int, INPUT_TYPE_DATE, CGUIEditControl::INPUT_TYPE_DATE); + SWIG_CONSTANT2(int, INPUT_TYPE_TIME, CGUIEditControl::INPUT_TYPE_TIME); + SWIG_CONSTANT2(int, INPUT_TYPE_IPADDRESS, CGUIEditControl::INPUT_TYPE_IPADDRESS); + SWIG_CONSTANT2(int, INPUT_TYPE_PASSWORD, CGUIEditControl::INPUT_TYPE_PASSWORD); + SWIG_CONSTANT2(int, INPUT_TYPE_PASSWORD_MD5, CGUIEditControl::INPUT_TYPE_PASSWORD_MD5); + SWIG_CONSTANT2(int, INPUT_TYPE_SECONDS, CGUIEditControl::INPUT_TYPE_SECONDS); + SWIG_CONSTANT2(int, INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW, CGUIEditControl::INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW); + + SWIG_CONSTANT_FROM_GETTER(const char*, NOTIFICATION_INFO); + SWIG_CONSTANT_FROM_GETTER(const char*, NOTIFICATION_WARNING); + SWIG_CONSTANT_FROM_GETTER(const char*, NOTIFICATION_ERROR); + + SWIG_CONSTANT(int, INPUT_ALPHANUM); + SWIG_CONSTANT(int, INPUT_NUMERIC); + SWIG_CONSTANT(int, INPUT_DATE); + SWIG_CONSTANT(int, INPUT_TIME); + SWIG_CONSTANT(int, INPUT_IPADDRESS); + SWIG_CONSTANT(int, INPUT_PASSWORD); + + SWIG_CONSTANT(int, HORIZONTAL); + SWIG_CONSTANT(int, VERTICAL); + + SWIG_CONSTANT(int, PASSWORD_VERIFY); + SWIG_CONSTANT(int, ALPHANUM_HIDE_INPUT); + + } +} +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/xbmc/interfaces/legacy/ModuleXbmcplugin.cpp b/xbmc/interfaces/legacy/ModuleXbmcplugin.cpp new file mode 100644 index 0000000..0af97b7 --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcplugin.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ModuleXbmcplugin.h" + +#include "FileItem.h" +#include "filesystem/PluginDirectory.h" + +namespace XBMCAddon +{ + + namespace xbmcplugin + { + bool addDirectoryItem(int handle, const String& url, const xbmcgui::ListItem* listItem, + bool isFolder, int totalItems) + { + if (listItem == nullptr) + throw new XBMCAddon::WrongTypeException("None not allowed as argument for listitem"); + AddonClass::Ref<xbmcgui::ListItem> pListItem(listItem); + pListItem->item->SetPath(url); + pListItem->item->m_bIsFolder = isFolder; + + // call the directory class to add our item + return XFILE::CPluginDirectory::AddItem(handle, pListItem->item.get(), totalItems); + } + + bool addDirectoryItems(int handle, + const std::vector<Tuple<String,const XBMCAddon::xbmcgui::ListItem*,bool> >& items, + int totalItems) + { + CFileItemList fitems; + for (const auto& item : items) + { + const String& url = item.first(); + const XBMCAddon::xbmcgui::ListItem* pListItem = item.second(); + bool bIsFolder = item.GetNumValuesSet() > 2 ? item.third() : false; + pListItem->item->SetPath(url); + pListItem->item->m_bIsFolder = bIsFolder; + fitems.Add(pListItem->item); + } + + // call the directory class to add our items + return XFILE::CPluginDirectory::AddItems(handle, &fitems, totalItems); + } + + void endOfDirectory(int handle, bool succeeded, bool updateListing, + bool cacheToDisc) + { + // tell the directory class that we're done + XFILE::CPluginDirectory::EndOfDirectory(handle, succeeded, updateListing, cacheToDisc); + } + + void setResolvedUrl(int handle, bool succeeded, const xbmcgui::ListItem* listItem) + { + if (listItem == nullptr) + throw new XBMCAddon::WrongTypeException("None not allowed as argument for listitem"); + AddonClass::Ref<xbmcgui::ListItem> pListItem(listItem); + XFILE::CPluginDirectory::SetResolvedUrl(handle, succeeded, pListItem->item.get()); + } + + void addSortMethod(int handle, int sortMethod, const String& clabelMask, const String& clabel2Mask) + { + String labelMask; + if (sortMethod == SORT_METHOD_TRACKNUM) + labelMask = (clabelMask.empty() ? "[%N. ]%T" : clabelMask.c_str()); + else if (sortMethod == SORT_METHOD_EPISODE || sortMethod == SORT_METHOD_PRODUCTIONCODE) + labelMask = (clabelMask.empty() ? "%H. %T" : clabelMask.c_str()); + else + labelMask = (clabelMask.empty() ? "%T" : clabelMask.c_str()); + + String label2Mask; + label2Mask = (clabel2Mask.empty() ? "%D" : clabel2Mask.c_str()); + + // call the directory class to add the sort method. + if (sortMethod >= SORT_METHOD_NONE && sortMethod < SORT_METHOD_MAX) + XFILE::CPluginDirectory::AddSortMethod(handle, (SORT_METHOD)sortMethod, labelMask, label2Mask); + } + + String getSetting(int handle, const char* id) + { + return XFILE::CPluginDirectory::GetSetting(handle, id); + } + + void setSetting(int handle, const String& id, const String& value) + { + XFILE::CPluginDirectory::SetSetting(handle, id, value); + } + + void setContent(int handle, const char* content) + { + XFILE::CPluginDirectory::SetContent(handle, content); + } + + void setPluginCategory(int handle, const String& category) + { + XFILE::CPluginDirectory::SetProperty(handle, "plugincategory", category); + } + + void setPluginFanart(int handle, const char* image, + const char* color1, + const char* color2, + const char* color3) + { + if (image) + XFILE::CPluginDirectory::SetProperty(handle, "fanart_image", image); + if (color1) + XFILE::CPluginDirectory::SetProperty(handle, "fanart_color1", color1); + if (color2) + XFILE::CPluginDirectory::SetProperty(handle, "fanart_color2", color2); + if (color3) + XFILE::CPluginDirectory::SetProperty(handle, "fanart_color3", color3); + } + + void setProperty(int handle, const char* key, const String& value) + { + XFILE::CPluginDirectory::SetProperty(handle, key, value); + } + + } +} diff --git a/xbmc/interfaces/legacy/ModuleXbmcplugin.h b/xbmc/interfaces/legacy/ModuleXbmcplugin.h new file mode 100644 index 0000000..1aa458c --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcplugin.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonString.h" +#include "ListItem.h" +#include "Tuple.h" +#include "swighelper.h" + +#include <vector> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace XBMCAddon +{ + namespace xbmcplugin + { +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + + // + /// \defgroup python_xbmcplugin Library - xbmcplugin + /// @{ + /// @brief <b>Plugin functions on Kodi.</b> + /// + /// Offers classes and functions that allow a developer to present + /// information through Kodi's standard menu structure. While plugins don't + /// have the same flexibility as scripts, they boast significantly quicker + /// development time and a more consistent user experience. + // + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.addDirectoryItem(handle, url, listitem [,isFolder, totalItems]) } + /// Callback function to pass directory contents back to Kodi. + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param url string - url of the entry. would be + /// `plugin://` for another virtual directory + /// @param listitem ListItem - item to add. + /// @param isFolder [opt] bool - True=folder / False=not a + /// folder(default). + /// @param totalItems [opt] integer - total number of items + /// that will be passed.(used for progressbar) + /// @return Returns a bool for successful completion. + /// + /// @note You can use the above as keywords for arguments and skip certain + /// optional arguments. Once you use a keyword, all following arguments + /// require the keyword. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// if not xbmcplugin.addDirectoryItem(int(sys.argv[1]), 'F:\\Trailers\\300.mov', listitem, totalItems=50): break + /// .. + /// ~~~~~~~~~~~~~ + /// + addDirectoryItem(...); +#else + bool addDirectoryItem(int handle, const String& url, const XBMCAddon::xbmcgui::ListItem* listitem, + bool isFolder = false, int totalItems = 0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.addDirectoryItems(handle, items[, totalItems]) } + /// Callback function to pass directory contents back to Kodi as a list. + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param items List - list of (url, listitem[, isFolder]) + /// as a tuple to add. + /// @param totalItems [opt] integer - total number of items + /// that will be passed.(used for progressbar) + /// @return Returns a bool for successful completion. + /// + /// @remark Large lists benefit over using the standard addDirectoryItem(). + /// You may call this more than once to add items in chunks. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// if not xbmcplugin.addDirectoryItems(int(sys.argv[1]), [(url, listitem, False,)]: raise + /// .. + /// ~~~~~~~~~~~~~ + /// + addDirectoryItems(...); +#else + bool addDirectoryItems(int handle, + const std::vector<Tuple<String,const XBMCAddon::xbmcgui::ListItem*,bool> >& items, + int totalItems = 0); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.endOfDirectory(handle[, succeeded, updateListing, cacheToDisc]) } + /// Callback function to tell Kodi that the end of the directory listing in + /// a virtualPythonFolder module is reached. + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param succeeded [opt] bool - True=script completed + /// successfully(Default)/False=Script did not. + /// @param updateListing [opt] bool - True=this folder should + /// update the current listing/False=Folder + /// is a subfolder(Default). + /// @param cacheToDisc [opt] bool - True=Folder will cache if + /// extended time(default)/False=this folder + /// will never cache to disc. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.endOfDirectory(int(sys.argv[1]), cacheToDisc=False) + /// .. + /// ~~~~~~~~~~~~~ + /// + endOfDirectory(...); +#else + void endOfDirectory(int handle, bool succeeded = true, bool updateListing = false, + bool cacheToDisc = true); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setResolvedUrl(handle, succeeded, listitem) } + /// Callback function to tell Kodi that the file plugin has been resolved to + /// a url + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param succeeded bool - True=script completed + /// successfully/False=Script did not. + /// @param listitem ListItem - item the file plugin resolved + /// to for playback. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) + /// .. + /// ~~~~~~~~~~~~~ + /// + setResolvedUrl(...); +#else + void setResolvedUrl(int handle, bool succeeded, const XBMCAddon::xbmcgui::ListItem* listitem); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.addSortMethod(handle, sortMethod [,labelMask, label2Mask]) } + ///------------------------------------------------------------------------- + /// Adds a sorting method for the media list. + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param sortMethod integer - see available sort methods at + /// the bottom (or see \ref List_of_sort_methods "SortUtils"). + /// | Value | Description | + /// |----------------------------------------------|-----------------------| + /// | xbmcplugin.SORT_METHOD_NONE | Do not sort + /// | xbmcplugin.SORT_METHOD_LABEL | Sort by label + /// | xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE | Sort by the label and ignore "The" before + /// | xbmcplugin.SORT_METHOD_DATE | Sort by the date + /// | xbmcplugin.SORT_METHOD_SIZE | Sort by the size + /// | xbmcplugin.SORT_METHOD_FILE | Sort by the file + /// | xbmcplugin.SORT_METHOD_DRIVE_TYPE | Sort by the drive type + /// | xbmcplugin.SORT_METHOD_TRACKNUM | Sort by the track number + /// | xbmcplugin.SORT_METHOD_DURATION | Sort by the duration + /// | xbmcplugin.SORT_METHOD_TITLE | Sort by the title + /// | xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE | Sort by the title and ignore "The" before + /// | xbmcplugin.SORT_METHOD_ARTIST | Sort by the artist + /// | xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE | Sort by the artist and ignore "The" before + /// | xbmcplugin.SORT_METHOD_ALBUM | Sort by the album + /// | xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE | Sort by the album and ignore "The" before + /// | xbmcplugin.SORT_METHOD_GENRE | Sort by the genre + /// | xbmcplugin.SORT_SORT_METHOD_VIDEO_YEAR, xbmcplugin.SORT_METHOD_YEAR | Sort by the year + /// | xbmcplugin.SORT_METHOD_VIDEO_RATING | Sort by the video rating + /// | xbmcplugin.SORT_METHOD_PROGRAM_COUNT | Sort by the program count + /// | xbmcplugin.SORT_METHOD_PLAYLIST_ORDER | Sort by the playlist order + /// | xbmcplugin.SORT_METHOD_EPISODE | Sort by the episode + /// | xbmcplugin.SORT_METHOD_VIDEO_TITLE | Sort by the video title + /// | xbmcplugin.SORT_METHOD_VIDEO_SORT_TITLE | Sort by the video sort title + /// | xbmcplugin.SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE | Sort by the video sort title and ignore "The" before + /// | xbmcplugin.SORT_METHOD_PRODUCTIONCODE | Sort by the production code + /// | xbmcplugin.SORT_METHOD_SONG_RATING | Sort by the song rating + /// | xbmcplugin.SORT_METHOD_MPAA_RATING | Sort by the mpaa rating + /// | xbmcplugin.SORT_METHOD_VIDEO_RUNTIME | Sort by video runtime + /// | xbmcplugin.SORT_METHOD_STUDIO | Sort by the studio + /// | xbmcplugin.SORT_METHOD_STUDIO_IGNORE_THE | Sort by the studio and ignore "The" before + /// | xbmcplugin.SORT_METHOD_UNSORTED | Use list not sorted + /// | xbmcplugin.SORT_METHOD_BITRATE | Sort by the bitrate + /// | xbmcplugin.SORT_METHOD_LISTENERS | Sort by the listeners + /// | xbmcplugin.SORT_METHOD_COUNTRY | Sort by the country + /// | xbmcplugin.SORT_METHOD_DATEADDED | Sort by the added date + /// | xbmcplugin.SORT_METHOD_FULLPATH | Sort by the full path name + /// | xbmcplugin.SORT_METHOD_LABEL_IGNORE_FOLDERS | Sort by the label names and ignore related folder names + /// | xbmcplugin.SORT_METHOD_LASTPLAYED | Sort by last played date + /// | xbmcplugin.SORT_METHOD_PLAYCOUNT | Sort by the play count + /// | xbmcplugin.SORT_METHOD_CHANNEL | Sort by the channel + /// | xbmcplugin.SORT_METHOD_DATE_TAKEN | Sort by the taken date + /// | xbmcplugin.SORT_METHOD_VIDEO_USER_RATING | Sort by the rating of the user of video + /// | xbmcplugin.SORT_METHOD_SONG_USER_RATING | Sort by the rating of the user of song + /// @param labelMask [opt] string - the label mask to use for + /// the first label. + /// - applies to: + /// | sortMethod | labelMask | + /// |---------------------------------------|-----------------------------| + /// | SORT_METHOD_TRACKNUM | Defaults to `[%%N. ]%%T` | + /// | SORT_METHOD_EPISODE | Defaults to `%%H. %%T` | + /// | SORT_METHOD_PRODUCTIONCODE | Defaults to `%%H. %%T` | + /// | All other sort methods | Defaults to `%%T` | + /// + /// + /// @param label2Mask [opt] string - the label mask to use for + /// the second label. Defaults to `%%D` + /// - applies to: + /// | | | | + /// |-----------------------------------------|-----------------------------|-----------------------------------------| + /// | SORT_METHOD_NONE | SORT_METHOD_UNSORTED | SORT_METHOD_VIDEO_TITLE | + /// | SORT_METHOD_TRACKNUM | SORT_METHOD_FILE | SORT_METHOD_TITLE | + /// | SORT_METHOD_TITLE_IGNORE_THE | SORT_METHOD_LABEL | SORT_METHOD_LABEL_IGNORE_THE | + /// | SORT_METHOD_VIDEO_SORT_TITLE | SORT_METHOD_FULLPATH | SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE | + /// | SORT_METHOD_LABEL_IGNORE_FOLDERS | SORT_METHOD_CHANNEL | | + /// @note to add multiple sort methods just call the method multiple times. + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v13 Added new sort **SORT_METHOD_DATE_TAKEN**, **SORT_METHOD_COUNTRY**, + /// **SORT_METHOD_DATEADDED**, **SORT_METHOD_FULLPATH**, **SORT_METHOD_LABEL_IGNORE_FOLDERS**, + /// **SORT_METHOD_LASTPLAYED**, **SORT_METHOD_PLAYCOUNT**, **SORT_METHOD_CHANNEL**. + /// @python_v17 Added new sort **SORT_METHOD_VIDEO_USER_RATING**. + /// @python_v19 Added new option **labelMask**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORTMETHOD_DATEADDED) + /// .. + /// ~~~~~~~~~~~~~ + /// + addSortMethod(...); +#else + void addSortMethod(int handle, int sortMethod, const String& labelMask = emptyString, const String& label2Mask = emptyString); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.getSetting(handle, id) } + /// Returns the value of a setting as a string. + /// + /// @param handle integer - handle the plugin was started + /// with. + /// @param id string - id of the setting that the + /// module needs to access. + /// @return Setting value as string + /// + /// @note You can use the above as a keyword. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// apikey = xbmcplugin.getSetting(int(sys.argv[1]), 'apikey') + /// .. + /// ~~~~~~~~~~~~~ + /// + getSetting(...); +#else + String getSetting(int handle, const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setSetting(handle, id, value) } + /// Sets a plugin setting for the current running plugin. + /// + /// @param handle integer - handle the plugin was started with. + /// @param id string - id of the setting that the module needs to access. + /// @param value string or unicode - value of the setting. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setSetting(int(sys.argv[1]), id='username', value='teamxbmc') + /// .. + /// ~~~~~~~~~~~~~ + /// + setSetting(...); +#else + void setSetting(int handle, const String& id, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setContent(handle, content) } + /// Sets the plugins content. + /// + /// @param handle integer - handle the plugin was started with. + /// @param content string - content type (eg. movies) + /// + /// @par Available content strings + /// | | | | | + /// |:--------:|:--------:|:--------:|:-----------:| + /// | files | songs | artists | albums | + /// | movies | tvshows | episodes | musicvideos | + /// | videos | images | games | -- | + /// + /// @remark Use **videos** for all videos which do not apply to the + /// more specific mentioned ones like "movies", "episodes" etc. + /// A good example is youtube. + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v18 Added new **games** content + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setContent(int(sys.argv[1]), 'movies') + /// .. + /// ~~~~~~~~~~~~~ + /// + setContent(...); +#else + void setContent(int handle, const char* content); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setPluginCategory(handle, category) } + /// Sets the plugins name for skins to display. + /// + /// @param handle integer - handle the plugin was started with. + /// @param category string or unicode - plugins sub category. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setPluginCategory(int(sys.argv[1]), 'Comedy') + /// .. + /// ~~~~~~~~~~~~~ + /// + setPluginCategory(...); +#else + void setPluginCategory(int handle, const String& category); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setPluginFanart(handle, image, color1, color2, color3) } + /// Sets the plugins fanart and color for skins to display. + /// + /// @param handle integer - handle the plugin was started with. + /// @param image [opt] string - path to fanart image. + /// @param color1 [opt] hexstring - color1. (e.g. '0xFFFFFFFF') + /// @param color2 [opt] hexstring - color2. (e.g. '0xFFFF3300') + /// @param color3 [opt] hexstring - color3. (e.g. '0xFF000000') + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setPluginFanart(int(sys.argv[1]), 'special://home/addons/plugins/video/Apple movie trailers II/fanart.png', color2='0xFFFF3300') + /// .. + /// ~~~~~~~~~~~~~ + /// + setPluginFanart(...); +#else + void setPluginFanart(int handle, const char* image = NULL, + const char* color1 = NULL, + const char* color2 = NULL, + const char* color3 = NULL); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcplugin + /// @brief \python_func{ xbmcplugin.setProperty(handle, key, value) } + /// Sets a container property for this plugin. + /// + /// @param handle integer - handle the plugin was started with. + /// @param key string - property name. + /// @param value string or unicode - value of property. + /// + /// @note Key is NOT case sensitive. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcplugin.setProperty(int(sys.argv[1]), 'Emulator', 'M.A.M.E.') + /// .. + /// ~~~~~~~~~~~~~ + /// + setProperty(...); + ///@} +#else + void setProperty(int handle, const char* key, const String& value); +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + SWIG_CONSTANT(int, SORT_METHOD_NONE); + SWIG_CONSTANT(int, SORT_METHOD_LABEL); + SWIG_CONSTANT(int, SORT_METHOD_LABEL_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_DATE); + SWIG_CONSTANT(int, SORT_METHOD_SIZE); + SWIG_CONSTANT(int, SORT_METHOD_FILE); + SWIG_CONSTANT(int, SORT_METHOD_DRIVE_TYPE); + SWIG_CONSTANT(int, SORT_METHOD_TRACKNUM); + SWIG_CONSTANT(int, SORT_METHOD_DURATION); + SWIG_CONSTANT(int, SORT_METHOD_TITLE); + SWIG_CONSTANT(int, SORT_METHOD_TITLE_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_ARTIST); + SWIG_CONSTANT(int, SORT_METHOD_ARTIST_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_ALBUM); + SWIG_CONSTANT(int, SORT_METHOD_ALBUM_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_GENRE); + SWIG_CONSTANT2(int, SORT_METHOD_VIDEO_YEAR,SORT_METHOD_YEAR); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_RATING); + SWIG_CONSTANT(int, SORT_METHOD_PROGRAM_COUNT); + SWIG_CONSTANT(int, SORT_METHOD_PLAYLIST_ORDER); + SWIG_CONSTANT(int, SORT_METHOD_EPISODE); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_TITLE); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_SORT_TITLE); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_ORIGINAL_TITLE); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_ORIGINAL_TITLE_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_PRODUCTIONCODE); + SWIG_CONSTANT(int, SORT_METHOD_SONG_RATING); + SWIG_CONSTANT(int, SORT_METHOD_MPAA_RATING); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_RUNTIME); + SWIG_CONSTANT(int, SORT_METHOD_STUDIO); + SWIG_CONSTANT(int, SORT_METHOD_STUDIO_IGNORE_THE); + SWIG_CONSTANT(int, SORT_METHOD_UNSORTED); + SWIG_CONSTANT(int, SORT_METHOD_BITRATE); + SWIG_CONSTANT(int, SORT_METHOD_LISTENERS); + SWIG_CONSTANT(int, SORT_METHOD_COUNTRY); + SWIG_CONSTANT(int, SORT_METHOD_DATEADDED); + SWIG_CONSTANT(int, SORT_METHOD_FULLPATH); + SWIG_CONSTANT(int, SORT_METHOD_LABEL_IGNORE_FOLDERS); + SWIG_CONSTANT(int, SORT_METHOD_LASTPLAYED); + SWIG_CONSTANT(int, SORT_METHOD_PLAYCOUNT); + SWIG_CONSTANT(int, SORT_METHOD_CHANNEL); + SWIG_CONSTANT(int, SORT_METHOD_DATE_TAKEN); + SWIG_CONSTANT(int, SORT_METHOD_VIDEO_USER_RATING); + SWIG_CONSTANT(int, SORT_METHOD_SONG_USER_RATING); + } +} +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/xbmc/interfaces/legacy/ModuleXbmcvfs.cpp b/xbmc/interfaces/legacy/ModuleXbmcvfs.cpp new file mode 100644 index 0000000..c6858e3 --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcvfs.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "ModuleXbmcvfs.h" + +#include "FileItem.h" +#include "LanguageHook.h" +#include "URL.h" +#include "Util.h" +#include "filesystem/Directory.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "utils/FileUtils.h" +#include "utils/URIUtils.h" + +namespace XBMCAddon +{ + + namespace xbmcvfs + { + bool copy(const String& strSource, const String& strDestination) + { + DelayedCallGuard dg; + return XFILE::CFile::Copy(strSource, strDestination); + } + + // delete a file + bool deleteFile(const String& strSource) + { + DelayedCallGuard dg; + return XFILE::CFile::Delete(strSource); + } + + // rename a file + bool rename(const String& file, const String& newFile) + { + DelayedCallGuard dg; + return XFILE::CFile::Rename(file,newFile); + } + + // check for a file or folder existence, mimics Pythons os.path.exists() + bool exists(const String& path) + { + DelayedCallGuard dg; + if (URIUtils::HasSlashAtEnd(path, true)) + return XFILE::CDirectory::Exists(path, false); + return XFILE::CFile::Exists(path, false); + } + + // make legal file name + String makeLegalFilename(const String& filename) + { + XBMC_TRACE; + return CUtil::MakeLegalPath(filename); + } + + // translate path + String translatePath(const String& path) + { + XBMC_TRACE; + return CSpecialProtocol::TranslatePath(path); + } + + // validate path + String validatePath(const String& path) + { + XBMC_TRACE; + return CUtil::ValidatePath(path, true); + } + + // make a directory + bool mkdir(const String& path) + { + DelayedCallGuard dg; + return XFILE::CDirectory::Create(path); + } + + // make all directories along the path + bool mkdirs(const String& path) + { + DelayedCallGuard dg; + return CUtil::CreateDirectoryEx(path); + } + + bool rmdir(const String& path, bool force) + { + DelayedCallGuard dg; + + if (force) + return CFileUtils::DeleteItem(path); + else + return XFILE::CDirectory::Remove(path); + } + + Tuple<std::vector<String>, std::vector<String> > listdir(const String& path) + { + DelayedCallGuard dg; + CFileItemList items; + std::string strSource; + strSource = path; + XFILE::CDirectory::GetDirectory(strSource, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS); + + Tuple<std::vector<String>, std::vector<String> > ret; + // initialize the Tuple to two values + ret.second(); + + for (int i=0; i < items.Size(); i++) + { + std::string itemPath = items[i]->GetPath(); + + if (URIUtils::HasSlashAtEnd(itemPath)) // folder + { + URIUtils::RemoveSlashAtEnd(itemPath); + std::string strFileName = URIUtils::GetFileName(itemPath); + if (strFileName.empty()) + { + CURL url(itemPath); + strFileName = url.GetHostName(); + } + ret.first().push_back(strFileName); + } + else // file + { + std::string strFileName = URIUtils::GetFileName(itemPath); + ret.second().push_back(strFileName); + } + } + + return ret; + } + } +} diff --git a/xbmc/interfaces/legacy/ModuleXbmcvfs.h b/xbmc/interfaces/legacy/ModuleXbmcvfs.h new file mode 100644 index 0000000..30e768f --- /dev/null +++ b/xbmc/interfaces/legacy/ModuleXbmcvfs.h @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonString.h" +#include "Tuple.h" + +#include <vector> + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +namespace XBMCAddon +{ + namespace xbmcvfs + { +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ + + // + /// \defgroup python_xbmcvfs Library - xbmcvfs + /// @{ + /// @brief **Virtual file system functions on Kodi.** + /// + /// Offers classes and functions offers access to the Virtual File Server + /// (VFS) which you can use to manipulate files and folders. + // + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.copy(source, destination) } + /// Copy file to destination, returns true/false. + /// + /// @param source file to copy. + /// @param destination destination file + /// @return True if successed + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.copy(source, destination) + /// .. + /// ~~~~~~~~~~~~~ + /// + copy(...); +#else + bool copy(const String& strSource, const String& strDestination); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.delete(file) } + /// Delete a file + /// + /// @param file File to delete + /// @return True if successed + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// xbmcvfs.delete(file) + /// .. + /// ~~~~~~~~~~~~~ + /// + delete(...); +#else + bool deleteFile(const String& file); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.rename(file, newFileName) } + /// Rename a file + /// + /// @param file File to rename + /// @param newFileName New filename, including the full path + /// @return True if successed + /// + /// @note Moving files between different filesystem (eg. local to nfs://) is not possible on + /// all platforms. You may have to do it manually by using the copy and deleteFile functions. + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.rename(file,newFileName) + /// .. + /// ~~~~~~~~~~~~~ + /// + rename(...); +#else + bool rename(const String& file, const String& newFile); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.exists(path) } + /// Check for a file or folder existence + /// + /// @param path File or folder (folder must end with + /// slash or backslash) + /// @return True if successed + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.exists(path) + /// .. + /// ~~~~~~~~~~~~~ + /// + exists(...); +#else + bool exists(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.makeLegalFilename(filename) } + /// Returns a legal filename or path as a string. + /// + /// @param filename string - filename/path to make legal + /// @return Legal filename or path as a string + /// + /// + /// @note The returned value is platform-specific. This is due to the fact that + /// the chars that need to be replaced to make a path legal depend on the + /// underlying OS filesystem. This is useful, for example, if you want to create + /// a file or folder based on data over which you have no control (e.g. an external API). + /// + /// + ///------------------------------------------------------------------------- + /// @python_v19 New function added (replaces old **xbmc.makeLegalFilename**) + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # windows + /// >> xbmcvfs.makeLegalFilename('C://Trailers/Ice Age: The Meltdown.avi') + /// C:\Trailers\Ice Age_ The Meltdown.avi + /// # non-windows + /// >> xbmcvfs.makeLegalFilename("///\\jk???lj????.mpg") + /// /jk___lj____.mpg + /// .. + /// ~~~~~~~~~~~~~ + /// + makeLegalFilename(...); +#else + String makeLegalFilename(const String& filename); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.translatePath(path) } + /// Returns the translated path. + /// + /// @param path string - Path to format + /// @return Translated path + /// + /// @note Only useful if you are coding for both Linux and Windows. + /// e.g. Converts 'special://home' -> '/home/[username]/.kodi' + /// on Linux. + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v19 New function added (replaces old **xbmc.translatePath**) + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// fpath = xbmcvfs.translatePath('special://home') + /// .. + /// ~~~~~~~~~~~~~ + /// + translatePath(...); +#else + String translatePath(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.validatePath(path) } + /// Returns the validated path. + /// + /// @param path string - Path to format + /// @return Validated path + /// + /// @note The result is platform-specific. Only useful if you are coding + /// for multiple platfforms for fixing slash problems + /// (e.g. Corrects 'Z://something' -> 'Z:\something'). + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v19 New function added (replaces old **xbmc.validatePath**) + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// fpath = xbmcvfs.validatePath(somepath) + /// .. + /// ~~~~~~~~~~~~~ + /// + validatePath(...); +#else + String validatePath(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.mkdir(path) } + /// Create a folder. + /// + /// @param path Folder to create + /// @return True if successed + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.mkdir(path) + /// .. + /// ~~~~~~~~~~~~~ + /// + mkdir(...); +#else + bool mkdir(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.mkdirs(path) } + /// Make all directories along the path + /// + /// Create folder(s) - it will create all folders in the path. + /// + /// @param path Folders to create + /// @return True if successed + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.mkdirs(path) + /// .. + /// ~~~~~~~~~~~~~ + /// + mkdirs(...); +#else + bool mkdirs(const String& path); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.rmdir(path, [force]) } + /// Remove a folder. + /// + /// @param path string - Folder to remove + /// @param force [opt] bool - Force directory removal + /// (default False). This can be useful + /// if the directory is not empty. + /// @return bool - True if successful, False + /// otherwise + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// success = xbmcvfs.rmdir(path) + /// .. + /// ~~~~~~~~~~~~~ + /// + rmdir(...); +#else + bool rmdir(const String& path, bool force = false); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcvfs + /// @brief \python_func{ xbmcvfs.listdir(path) } + /// Lists content of a folder. + /// + /// @param path Folder to get list from + /// @return Directory content list + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dirs, files = xbmcvfs.listdir(path) + /// .. + /// ~~~~~~~~~~~~~ + /// + listdir(...); +#else + Tuple<std::vector<String>, std::vector<String> > listdir(const String& path); +#endif + //@} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + } +} +#endif /* DOXYGEN_SHOULD_SKIP_THIS */ diff --git a/xbmc/interfaces/legacy/Monitor.cpp b/xbmc/interfaces/legacy/Monitor.cpp new file mode 100644 index 0000000..34d0553 --- /dev/null +++ b/xbmc/interfaces/legacy/Monitor.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Monitor.h" + +#include "threads/SystemClock.h" + +#include <algorithm> +#include <math.h> + +using namespace std::chrono_literals; + +namespace XBMCAddon +{ + namespace xbmc + { + Monitor::Monitor(): abortEvent(true) + { + XBMC_TRACE; + if (languageHook) + { + Id = languageHook->GetAddonId(); + invokerId = languageHook->GetInvokerId(); + languageHook->RegisterMonitorCallback(this); + } + } + + void Monitor::AbortNotify() + { + XBMC_TRACE; + abortEvent.Set(); + } + + bool Monitor::waitForAbort(double timeout) + { + XBMC_TRACE; + int timeoutMS = ceil(timeout * 1000); + XbmcThreads::EndTime<> endTime{std::chrono::milliseconds(timeoutMS)}; + + if (timeoutMS <= 0) + endTime.SetInfinite(); + + while (!endTime.IsTimePast()) + { + { + DelayedCallGuard dg(languageHook); + auto timeout = std::min(endTime.GetTimeLeft(), 100ms); + if (abortEvent.Wait(timeout)) + return true; + } + if (languageHook) + languageHook->MakePendingCalls(); + } + return false; + } + + bool Monitor::abortRequested() + { + XBMC_TRACE; + return abortEvent.Signaled(); + } + + Monitor::~Monitor() + { + XBMC_TRACE; + deallocating(); + DelayedCallGuard dg(languageHook); + // we're shutting down so unregister me. + if (languageHook) + { + DelayedCallGuard dc; + languageHook->UnregisterMonitorCallback(this); + } + } + } +} + diff --git a/xbmc/interfaces/legacy/Monitor.h b/xbmc/interfaces/legacy/Monitor.h new file mode 100644 index 0000000..0d3d831 --- /dev/null +++ b/xbmc/interfaces/legacy/Monitor.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonCallback.h" +#include "AddonString.h" + +namespace XBMCAddon +{ + namespace xbmc + { + + /// + /// \ingroup python_xbmc + /// \defgroup python_monitor Monitor + /// @{ + /// @brief **Kodi's monitor class.** + /// + /// \python_class{ xbmc.Monitor() } + /// + /// Creates a new monitor to notify addon about changes. + /// + class Monitor : public AddonCallback + { + String Id; + long invokerId; + CEvent abortEvent; + public: + Monitor(); + +#ifndef SWIG + inline void OnSettingsChanged() { XBMC_TRACE; invokeCallback(new CallbackFunction<Monitor>(this,&Monitor::onSettingsChanged)); } + inline void OnScreensaverActivated() { XBMC_TRACE; invokeCallback(new CallbackFunction<Monitor>(this,&Monitor::onScreensaverActivated)); } + inline void OnScreensaverDeactivated() { XBMC_TRACE; invokeCallback(new CallbackFunction<Monitor>(this,&Monitor::onScreensaverDeactivated)); } + inline void OnDPMSActivated() { XBMC_TRACE; invokeCallback(new CallbackFunction<Monitor>(this,&Monitor::onDPMSActivated)); } + inline void OnDPMSDeactivated() { XBMC_TRACE; invokeCallback(new CallbackFunction<Monitor>(this,&Monitor::onDPMSDeactivated)); } + inline void OnScanStarted(const String &library) + { + XBMC_TRACE; + invokeCallback( + new CallbackFunction<Monitor, const String>(this, &Monitor::onScanStarted, library)); + } + inline void OnScanFinished(const String &library) + { + XBMC_TRACE; + invokeCallback( + new CallbackFunction<Monitor, const String>(this, &Monitor::onScanFinished, library)); + } + inline void OnCleanStarted(const String& library) + { + XBMC_TRACE; + invokeCallback( + new CallbackFunction<Monitor, const String>(this, &Monitor::onCleanStarted, library)); + } + inline void OnCleanFinished(const String& library) + { + XBMC_TRACE; + invokeCallback( + new CallbackFunction<Monitor, const String>(this, &Monitor::onCleanFinished, library)); + } + inline void OnNotification(const String& sender, const String& method, const String& data) + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Monitor, const String, const String, const String>( + this, &Monitor::onNotification, sender, method, data)); + } + + inline const String& GetId() { return Id; } + inline long GetInvokerId() { return invokerId; } + + /** + * Called from XBPython to notify registered monitors that a script is aborting/ending. + */ + void AbortNotify(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onSettingsChanged() } + /// onSettingsChanged method. + /// + /// Will be called when addon settings are changed + /// + onSettingsChanged(); +#else + virtual void onSettingsChanged() { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onScreensaverActivated() } + /// onScreensaverActivated method. + /// + /// Will be called when screensaver kicks in + /// + onScreensaverActivated(); +#else + virtual void onScreensaverActivated() { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onScreensaverDeactivated() } + /// onScreensaverDeactivated method. + /// + /// Will be called when screensaver goes off + /// + onScreensaverDeactivated(); +#else + virtual void onScreensaverDeactivated() { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onDPMSActivated() } + /// onDPMSActivated method. + /// + /// Will be called when energysaving/DPMS gets active + /// + onDPMSActivated(); +#else + virtual void onDPMSActivated() { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onDPMSDeactivated() } + /// onDPMSDeactivated method. + /// + /// Will be called when energysaving/DPMS is turned off + /// + onDPMSDeactivated(); +#else + virtual void onDPMSDeactivated() { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onScanStarted(library) } + /// onScanStarted method. + /// + /// @param library Video / music as string + /// + /// + /// @note Will be called when library clean has ended and return video or + /// music to indicate which library is being scanned + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + onScanStarted(...); +#else + virtual void onScanStarted(const String library) { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onScanFinished(library) } + /// onScanFinished method. + /// + /// @param library Video / music as string + /// + /// + /// @note Will be called when library clean has ended and return video or + /// music to indicate which library has been scanned + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + onScanFinished(...); +#else + virtual void onScanFinished(const String library) { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onCleanStarted(library) } + /// onCleanStarted method. + /// + /// @param library Video / music as string + /// + /// + /// @note Will be called when library clean has ended and return video or + /// music to indicate which library has been cleaned + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + onCleanStarted(...); +#else + virtual void onCleanStarted(const String library) { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onCleanFinished(library) } + /// onCleanFinished method. + /// + /// @param library Video / music as string + /// + /// + /// @note Will be called when library clean has ended and return video or + /// music to indicate which library has been finished + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + onCleanFinished(...); +#else + virtual void onCleanFinished(const String library) { XBMC_TRACE; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_monitor + /// @brief \python_func{ onNotification(sender, method, data) } + /// onNotification method. + /// + /// @param sender Sender of the notification + /// @param method Name of the notification + /// @param data JSON-encoded data of the notification + /// + /// @note Will be called when Kodi receives or sends a notification + /// + /// + ///----------------------------------------------------------------------- + /// @python_v13 New function added. + /// + onNotification(...); +#else + virtual void onNotification(const String sender, const String method, const String data) + { + XBMC_TRACE; + } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_monitor + /// @brief \python_func{ waitForAbort([timeout]) } + /// Wait for Abort + /// \anchor xbmc_Monitor_waitForAbort + /// + /// Block until abort is requested, or until timeout occurs. If an + /// abort requested have already been made, return immediately. + /// + /// @param timeout [opt] float - timeout in seconds. + /// Default: no timeout. + /// + /// @return True when abort have been requested, + /// False if a timeout is given and the + /// operation times out. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// monitor = xbmc.Monitor() + /// # do something + /// monitor.waitForAbort(10) # sleeps for 10 secs or returns early if kodi aborts + /// if monitor.abortRequested(): + /// # abort was requested to Kodi (e.g. shutdown), do your cleanup logic + /// .. + /// ~~~~~~~~~~~~~ + /// + waitForAbort(...); +#else + bool waitForAbort(double timeout = -1); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_monitor + /// @brief \python_func{ abortRequested() } + /// Returns True if abort has been requested. + /// + /// @return True if requested + /// + /// + ///----------------------------------------------------------------------- + /// @python_v14 New function added. + /// + abortRequested(); +#else + bool abortRequested(); +#endif + ~Monitor() override; + }; + /** @} */ + } +}; diff --git a/xbmc/interfaces/legacy/PlayList.cpp b/xbmc/interfaces/legacy/PlayList.cpp new file mode 100644 index 0000000..1117a81 --- /dev/null +++ b/xbmc/interfaces/legacy/PlayList.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PlayList.h" + +#include "PlayListPlayer.h" +#include "ServiceBroker.h" +#include "playlists/PlayListFactory.h" +#include "utils/URIUtils.h" + +namespace XBMCAddon +{ + namespace xbmc + { + //! @todo need a means to check for a valid construction + //! either by throwing an exception or by an "isValid" check + PlayList::PlayList(int playList) : + iPlayList(playList), pPlayList(NULL) + { + // we do not create our own playlist, just using the ones from playlistplayer + if (iPlayList != PLAYLIST::TYPE_MUSIC && iPlayList != PLAYLIST::TYPE_VIDEO) + throw PlayListException("PlayList does not exist"); + + pPlayList = &CServiceBroker::GetPlaylistPlayer().GetPlaylist(playList); + iPlayList = playList; + } + + PlayList::~PlayList() = default; + + void PlayList::add(const String& url, XBMCAddon::xbmcgui::ListItem* listitem, int index) + { + CFileItemList items; + + if (listitem != NULL) + { + // an optional listitem was passed + // set m_strPath to the passed url + listitem->item->SetPath(url); + + items.Add(listitem->item); + } + else + { + CFileItemPtr item(new CFileItem(url, false)); + item->SetLabel(url); + + items.Add(item); + } + + pPlayList->Insert(items, index); + } + + bool PlayList::load(const char* cFileName) + { + CFileItem item(cFileName); + item.SetPath(cFileName); + + if (item.IsPlayList()) + { + // load playlist and copy al items to existing playlist + + // load a playlist like .m3u, .pls + // first get correct factory to load playlist + std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(item)); + if (nullptr != pPlayList) + { + // load it + if (!pPlayList->Load(item.GetPath())) + //hmmm unable to load playlist? + return false; + + // clear current playlist + CServiceBroker::GetPlaylistPlayer().ClearPlaylist(this->iPlayList); + + // add each item of the playlist to the playlistplayer + for (int i=0; i < pPlayList->size(); ++i) + { + CFileItemPtr playListItem =(*pPlayList)[i]; + if (playListItem->GetLabel().empty()) + playListItem->SetLabel(URIUtils::GetFileName(playListItem->GetPath())); + + this->pPlayList->Add(playListItem); + } + } + } + else + // filename is not a valid playlist + throw PlayListException("Not a valid playlist"); + + return true; + } + + void PlayList::remove(const char* filename) + { + pPlayList->Remove(filename); + } + + void PlayList::clear() + { + pPlayList->Clear(); + } + + int PlayList::size() + { + return pPlayList->size(); + } + + void PlayList::shuffle() + { + pPlayList->Shuffle(); + } + + void PlayList::unshuffle() + { + pPlayList->UnShuffle(); + } + + int PlayList::getposition() + { + return CServiceBroker::GetPlaylistPlayer().GetCurrentSong(); + } + + XBMCAddon::xbmcgui::ListItem* PlayList::operator [](long i) + { + int iPlayListSize = size(); + + long pos = i; + if (pos < 0) pos += iPlayListSize; + + if (pos < 0 || pos >= iPlayListSize) + throw PlayListException("array out of bound"); + + CFileItemPtr ptr((*pPlayList)[pos]); + + return new XBMCAddon::xbmcgui::ListItem(ptr); + } + } +} + diff --git a/xbmc/interfaces/legacy/PlayList.h b/xbmc/interfaces/legacy/PlayList.h new file mode 100644 index 0000000..6162945 --- /dev/null +++ b/xbmc/interfaces/legacy/PlayList.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "Exception.h" +#include "ListItem.h" +#include "playlists/PlayList.h" + +namespace XBMCAddon +{ + namespace xbmc + { + XBMCCOMMONS_STANDARD_EXCEPTION(PlayListException); + + // + /// \defgroup python_PlayList PlayList + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's Play List class.** + /// + /// \python_class{ xbmc.PlayList(playList) } + /// + /// To create and edit a playlist which can be handled by the player. + /// + /// @param playList [integer] To define the stream type + /// | Value | Integer String | Description | + /// |:-----:|:--------------------|:---------------------------------------| + /// | 0 | xbmc.PLAYLIST_MUSIC | Playlist for music files or streams | + /// | 1 | xbmc.PLAYLIST_VIDEO | Playlist for video files or streams | + /// + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// play=xbmc.PlayList(xbmc.PLAYLIST_VIDEO) + /// ... + /// ~~~~~~~~~~~~~ + // + class PlayList : public AddonClass + { + int iPlayList; + PLAYLIST::CPlayList *pPlayList; + + public: + explicit PlayList(int playList); + ~PlayList() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ getPlayListId() } + /// Get the PlayList Identifier + /// + /// @return Id as an integer. + /// + getPlayListId(); +#else + inline int getPlayListId() const { return iPlayList; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ add(url[, listitem, index]) } + /// Adds a new file to the playlist. + /// + /// @param url string or unicode - filename or url to add. + /// @param listitem [opt] listitem - used with setInfo() to set different infolabels. + /// @param index [opt] integer - position to add playlist item. (default=end) + /// + /// @note You can use the above as keywords for arguments and skip certain optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) + /// video = 'F:\\movies\\Ironman.mov' + /// listitem = xbmcgui.ListItem('Ironman', thumbnailImage='F:\\movies\\Ironman.tbn') + /// listitem.setInfo('video', {'Title': 'Ironman', 'Genre': 'Science Fiction'}) + /// playlist.add(url=video, listitem=listitem, index=7) + /// .. + /// ~~~~~~~~~~~~~ + /// + add(...); +#else + void add(const String& url, XBMCAddon::xbmcgui::ListItem* listitem = NULL, int index = -1); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ load(filename) } + /// Load a playlist. + /// + /// Clear current playlist and copy items from the file to this Playlist + /// filename can be like .pls or .m3u ... + /// + /// @param filename File with list to play inside + /// @return False if unable to load playlist + /// + load(...); +#else + bool load(const char* filename); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ remove(filename) } + /// Remove an item with this filename from the playlist. + /// + /// @param filename The file to remove from list. + /// + remove(...); +#else + void remove(const char* filename); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ clear() } + /// Clear all items in the playlist. + /// + clear(); +#else + void clear(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ size() } + /// Returns the total number of PlayListItems in this playlist. + /// + /// @return Amount of playlist entries. + /// + size(); +#else + int size(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ shuffle() } + /// Shuffle the playlist. + /// + shuffle(); +#else + void shuffle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ unshuffle() } + /// Unshuffle the playlist. + /// + unshuffle(); +#else + void unshuffle(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ getposition() } + /// Returns the position of the current song in this playlist. + /// + /// @return Position of the current song + /// + getposition(); +#else + int getposition(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayList + /// @brief \python_func{ [] } + /// Retrieve the item at the given position. + /// + /// @param i Pointer in list + /// @return The selected item on list + /// + /// @note A negative index means + /// from the end rather than from the start. + /// + [](...); +#else + XBMCAddon::xbmcgui::ListItem* operator[](long i); +#endif + }; + /// @} + } +} + diff --git a/xbmc/interfaces/legacy/Player.cpp b/xbmc/interfaces/legacy/Player.cpp new file mode 100644 index 0000000..cf982ba --- /dev/null +++ b/xbmc/interfaces/legacy/Player.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Player.h" + +#include "AddonUtils.h" +#include "GUIInfoManager.h" +#include "GUIUserMessages.h" +#include "ListItem.h" +#include "PlayList.h" +#include "PlayListPlayer.h" +#include "ServiceBroker.h" +#include "application/Application.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationPlayer.h" +#include "cores/IPlayer.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "messaging/ApplicationMessenger.h" +#include "settings/MediaSettings.h" + +namespace +{ + +std::shared_ptr<const CApplicationPlayer> getAppPlayer() +{ + const auto& components = CServiceBroker::GetAppComponents(); + auto res = components.GetComponent<CApplicationPlayer>(); + return res; +} + +std::shared_ptr<CApplicationPlayer> getAppPlayerMut() +{ + auto& components = CServiceBroker::GetAppComponents(); + auto res = components.GetComponent<CApplicationPlayer>(); + return res; +} + +} // namespace + +namespace XBMCAddon +{ + namespace xbmc + { + PlayParameter Player::defaultPlayParameter; + + Player::Player() + { + iPlayList = PLAYLIST::TYPE_MUSIC; + + // now that we're done, register hook me into the system + if (languageHook) + { + DelayedCallGuard dc(languageHook); + languageHook->RegisterPlayerCallback(this); + } + } + + Player::~Player() + { + deallocating(); + + // we're shutting down so unregister me. + if (languageHook) + { + DelayedCallGuard dc(languageHook); + languageHook->UnregisterPlayerCallback(this); + } + } + + void Player::play(const Alternative<String, const PlayList* > & item, + const XBMCAddon::xbmcgui::ListItem* listitem, bool windowed, int startpos) + { + XBMC_TRACE; + + if (&item == &defaultPlayParameter) + playCurrent(windowed); + else if (item.which() == XBMCAddon::first) + playStream(item.former(), listitem, windowed); + else // item is a PlayListItem + playPlaylist(item.later(),windowed,startpos); + } + + void Player::playStream(const String& item, const xbmcgui::ListItem* plistitem, bool windowed) + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + if (!item.empty()) + { + // set fullscreen or windowed + CMediaSettings::GetInstance().SetMediaStartWindowed(windowed); + + const AddonClass::Ref<xbmcgui::ListItem> listitem(plistitem); + + if (listitem.isSet()) + { + // set m_strPath to the passed url + listitem->item->SetPath(item.c_str()); + CServiceBroker::GetAppMessenger()->PostMsg( + TMSG_MEDIA_PLAY, 0, 0, static_cast<void*>(new CFileItem(*listitem->item))); + } + else + { + CFileItemList *l = new CFileItemList; //don't delete, + l->Add(std::make_shared<CFileItem>(item, false)); + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, -1, -1, + static_cast<void*>(l)); + } + } + else + playCurrent(windowed); + } + + void Player::playCurrent(bool windowed) + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + // set fullscreen or windowed + CMediaSettings::GetInstance().SetMediaStartWindowed(windowed); + + // play current file in playlist + if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() != iPlayList) + CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlayList); + CServiceBroker::GetAppMessenger()->SendMsg( + TMSG_PLAYLISTPLAYER_PLAY, CServiceBroker::GetPlaylistPlayer().GetCurrentSong()); + } + + void Player::playPlaylist(const PlayList* playlist, bool windowed, int startpos) + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + if (playlist != NULL) + { + // set fullscreen or windowed + CMediaSettings::GetInstance().SetMediaStartWindowed(windowed); + + // play a python playlist (a playlist from playlistplayer.cpp) + iPlayList = playlist->getPlayListId(); + CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlayList); + if (startpos > -1) + CServiceBroker::GetPlaylistPlayer().SetCurrentSong(startpos); + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PLAY, startpos); + } + else + playCurrent(windowed); + } + + void Player::stop() + { + XBMC_TRACE; + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_STOP); + } + + void Player::pause() + { + XBMC_TRACE; + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_MEDIA_PAUSE); + } + + void Player::playnext() + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_NEXT); + } + + void Player::playprevious() + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PREV); + } + + void Player::playselected(int selected) + { + XBMC_TRACE; + DelayedCallGuard dc(languageHook); + + if (CServiceBroker::GetPlaylistPlayer().GetCurrentPlaylist() != iPlayList) + { + CServiceBroker::GetPlaylistPlayer().SetCurrentPlaylist(iPlayList); + } + CServiceBroker::GetPlaylistPlayer().SetCurrentSong(selected); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_PLAYLISTPLAYER_PLAY, selected); + //CServiceBroker::GetPlaylistPlayer().Play(selected); + //CLog::Log(LOGINFO, "Current Song After Play: {}", CServiceBroker::GetPlaylistPlayer().GetCurrentSong()); + } + + void Player::OnPlayBackStarted(const CFileItem &file) + { + // We only have fileItem due to us having to + // implement the interface, we can't send it to python + // as we're not able to serialize it. + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this, &Player::onPlayBackStarted)); + } + + void Player::OnAVStarted(const CFileItem &file) + { + // We only have fileItem due to us having to + // implement the interface, we can't send it to python + // as we're not able to serialize it. + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this, &Player::onAVStarted)); + } + + void Player::OnAVChange() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this, &Player::onAVChange)); + } + + void Player::OnPlayBackEnded() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onPlayBackEnded)); + } + + void Player::OnPlayBackStopped() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onPlayBackStopped)); + } + + void Player::OnPlayBackError() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onPlayBackError)); + } + + void Player::OnPlayBackPaused() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onPlayBackPaused)); + } + + void Player::OnPlayBackResumed() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onPlayBackResumed)); + } + + void Player::OnQueueNextItem() + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player>(this,&Player::onQueueNextItem)); + } + + void Player::OnPlayBackSpeedChanged(int speed) + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player,int>(this,&Player::onPlayBackSpeedChanged,speed)); + } + + void Player::OnPlayBackSeek(int64_t time, int64_t seekOffset) + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player,int,int>(this,&Player::onPlayBackSeek,static_cast<int>(time),static_cast<int>(seekOffset))); + } + + void Player::OnPlayBackSeekChapter(int chapter) + { + XBMC_TRACE; + invokeCallback(new CallbackFunction<Player,int>(this,&Player::onPlayBackSeekChapter,chapter)); + } + + void Player::onPlayBackStarted() + { + XBMC_TRACE; + } + + void Player::onAVStarted() + { + XBMC_TRACE; + } + + void Player::onAVChange() + { + XBMC_TRACE; + } + + void Player::onPlayBackEnded() + { + XBMC_TRACE; + } + + void Player::onPlayBackStopped() + { + XBMC_TRACE; + } + + void Player::onPlayBackError() + { + XBMC_TRACE; + } + + void Player::onPlayBackPaused() + { + XBMC_TRACE; + } + + void Player::onPlayBackResumed() + { + XBMC_TRACE; + } + + void Player::onQueueNextItem() + { + XBMC_TRACE; + } + + void Player::onPlayBackSpeedChanged(int speed) + { + XBMC_TRACE; + } + + void Player::onPlayBackSeek(int time, int seekOffset) + { + XBMC_TRACE; + } + + void Player::onPlayBackSeekChapter(int chapter) + { + XBMC_TRACE; + } + + bool Player::isPlaying() + { + XBMC_TRACE; + return getAppPlayer()->IsPlaying(); + } + + bool Player::isPlayingAudio() + { + XBMC_TRACE; + return getAppPlayer()->IsPlayingAudio(); + } + + bool Player::isPlayingVideo() + { + XBMC_TRACE; + return getAppPlayer()->IsPlayingVideo(); + } + + bool Player::isPlayingRDS() + { + XBMC_TRACE; + return getAppPlayer()->IsPlayingRDS(); + } + + bool Player::isPlayingGame() + { + XBMC_TRACE; + return getAppPlayer()->IsPlayingGame(); + } + + bool Player::isExternalPlayer() + { + XBMC_TRACE; + return getAppPlayer()->IsExternalPlaying(); + } + + String Player::getPlayingFile() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any file"); + + return g_application.CurrentFileItem().GetDynPath(); + } + + XBMCAddon::xbmcgui::ListItem* Player::getPlayingItem() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any item"); + + CFileItemPtr itemPtr = std::make_shared<CFileItem>(g_application.CurrentFileItem()); + return new XBMCAddon::xbmcgui::ListItem(itemPtr); + } + + InfoTagVideo* Player::getVideoInfoTag() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlayingVideo()) + throw PlayerException("Kodi is not playing any videofile"); + + const CVideoInfoTag* movie = CServiceBroker::GetGUI()->GetInfoManager().GetCurrentMovieTag(); + if (movie) + return new InfoTagVideo(movie); + + return new InfoTagVideo(true); + } + + InfoTagMusic* Player::getMusicInfoTag() + { + XBMC_TRACE; + if (getAppPlayer()->IsPlayingVideo() || !getAppPlayer()->IsPlayingAudio()) + throw PlayerException("Kodi is not playing any music file"); + + const MUSIC_INFO::CMusicInfoTag* tag = CServiceBroker::GetGUI()->GetInfoManager().GetCurrentSongTag(); + if (tag) + return new InfoTagMusic(tag); + + return new InfoTagMusic(true); + } + + void Player::updateInfoTag(const XBMCAddon::xbmcgui::ListItem* item) + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any file"); + + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, item->item); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + } + + InfoTagGame* Player::getGameInfoTag() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlayingGame()) + throw PlayerException("Kodi is not playing any game file"); + + const KODI::GAME::CGameInfoTag* game = + CServiceBroker::GetGUI()->GetInfoManager().GetCurrentGameTag(); + if (game) + return new InfoTagGame(game); + + return new InfoTagGame(); + } + + InfoTagRadioRDS* Player::getRadioRDSInfoTag() + { + XBMC_TRACE; + if (getAppPlayer()->IsPlayingVideo() || !getAppPlayer()->IsPlayingRDS()) + throw PlayerException("Kodi is not playing any music file with RDS"); + + std::shared_ptr<CFileItem> item = g_application.CurrentFileItemPtr(); + if (item && item->HasPVRChannelInfoTag()) + return new InfoTagRadioRDS(item->GetPVRChannelInfoTag()); + + return new InfoTagRadioRDS(); + } + + double Player::getTotalTime() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any media file"); + + return g_application.GetTotalTime(); + } + + double Player::getTime() + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any media file"); + + return g_application.GetTime(); + } + + void Player::seekTime(double pTime) + { + XBMC_TRACE; + if (!getAppPlayer()->IsPlaying()) + throw PlayerException("Kodi is not playing any media file"); + + g_application.SeekTime( pTime ); + } + + void Player::setSubtitles(const char* cLine) + { + XBMC_TRACE; + if (getAppPlayer()->HasPlayer()) + { + getAppPlayerMut()->AddSubtitle(cLine); + } + } + + void Player::showSubtitles(bool bVisible) + { + XBMC_TRACE; + if (getAppPlayer()->HasPlayer()) + { + getAppPlayerMut()->SetSubtitleVisible(bVisible != 0); + } + } + + String Player::getSubtitles() + { + XBMC_TRACE; + if (getAppPlayer()->HasPlayer()) + { + SubtitleStreamInfo info; + getAppPlayerMut()->GetSubtitleStreamInfo(CURRENT_STREAM, info); + + if (info.language.length() > 0) + return info.language; + else + return info.name; + } + + return ""; + } + + std::vector<String> Player::getAvailableSubtitleStreams() + { + if (getAppPlayer()->HasPlayer()) + { + int subtitleCount = getAppPlayer()->GetSubtitleCount(); + std::vector<String> ret(subtitleCount); + for (int iStream=0; iStream < subtitleCount; iStream++) + { + SubtitleStreamInfo info; + getAppPlayer()->GetSubtitleStreamInfo(iStream, info); + + if (info.language.length() > 0) + ret[iStream] = info.language; + else + ret[iStream] = info.name; + } + return ret; + } + + return std::vector<String>(); + } + + void Player::setSubtitleStream(int iStream) + { + if (getAppPlayer()->HasPlayer()) + { + int streamCount = getAppPlayer()->GetSubtitleCount(); + if(iStream < streamCount) + { + getAppPlayerMut()->SetSubtitle(iStream); + getAppPlayerMut()->SetSubtitleVisible(true); + } + } + } + + std::vector<String> Player::getAvailableAudioStreams() + { + if (getAppPlayer()->HasPlayer()) + { + int streamCount = getAppPlayer()->GetAudioStreamCount(); + std::vector<String> ret(streamCount); + for (int iStream=0; iStream < streamCount; iStream++) + { + AudioStreamInfo info; + getAppPlayerMut()->GetAudioStreamInfo(iStream, info); + + if (info.language.length() > 0) + ret[iStream] = info.language; + else + ret[iStream] = info.name; + } + return ret; + } + + return std::vector<String>(); + } + + void Player::setAudioStream(int iStream) + { + if (getAppPlayer()->HasPlayer()) + { + int streamCount = getAppPlayer()->GetAudioStreamCount(); + if (iStream < streamCount) + getAppPlayerMut()->SetAudioStream(iStream); + } + } + + std::vector<String> Player::getAvailableVideoStreams() + { + int streamCount = getAppPlayer()->GetVideoStreamCount(); + std::vector<String> ret(streamCount); + for (int iStream = 0; iStream < streamCount; ++iStream) + { + VideoStreamInfo info; + getAppPlayer()->GetVideoStreamInfo(iStream, info); + + if (info.language.length() > 0) + ret[iStream] = info.language; + else + ret[iStream] = info.name; + } + return ret; + } + + void Player::setVideoStream(int iStream) + { + int streamCount = getAppPlayer()->GetVideoStreamCount(); + if (iStream < streamCount) + getAppPlayerMut()->SetVideoStream(iStream); + } + } +} + diff --git a/xbmc/interfaces/legacy/Player.h b/xbmc/interfaces/legacy/Player.h new file mode 100644 index 0000000..b700597 --- /dev/null +++ b/xbmc/interfaces/legacy/Player.h @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonCallback.h" +#include "AddonString.h" +#include "Alternative.h" +#include "Exception.h" +#include "InfoTagGame.h" +#include "InfoTagMusic.h" +#include "InfoTagRadioRDS.h" +#include "InfoTagVideo.h" +#include "ListItem.h" +#include "PlayList.h" +#include "cores/IPlayerCallback.h" +#include "swighelper.h" + +#include <vector> + +namespace XBMCAddon +{ + namespace xbmc + { + XBMCCOMMONS_STANDARD_EXCEPTION(PlayerException); + + typedef Alternative<String, const PlayList* > PlayParameter; + + // This class is a merge of what was previously in xbmcmodule/player.h + // and xbmcmodule/PythonPlayer.h without the python references. The + // queuing and handling of asynchronous callbacks is done internal to + // this class. + + // + /// \defgroup python_Player Player + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's player.** + /// + /// \python_class{ xbmc.Player() } + /// + /// To become and create the class to play something. + /// + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// xbmc.Player().play(url, listitem, windowed) + /// ... + /// ~~~~~~~~~~~~~ + // + class Player : public AddonCallback, public IPlayerCallback + { + private: + int iPlayList; + + void playStream(const String& item = emptyString, const XBMCAddon::xbmcgui::ListItem* listitem = NULL, bool windowed = false); + void playPlaylist(const PlayList* playlist = NULL, + bool windowed = false, int startpos=-1); + void playCurrent(bool windowed = false); + + public: +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + static PlayParameter defaultPlayParameter; +#endif + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + // Construct a Player proxying the given generated binding. The + // construction of a Player needs to identify whether or not any + // callbacks will be executed asynchronously or not. + explicit Player(); + ~Player(void) override; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ play([item, listitem, windowed, startpos]) } + /// Play an item. + /// + /// @param item [opt] string - filename, url or playlist + /// @param listitem [opt] listitem - used with setInfo() to set + /// different infolabels. + /// @param windowed [opt] bool - true=play video windowed, + /// false=play users preference.(default) + /// @param startpos [opt] int - starting position when playing + /// a playlist. Default = -1 + /// + /// @note If item is not given then the Player will try to play the + /// current item in the current playlist.\n + /// \n + /// You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword. + /// + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// listitem = xbmcgui.ListItem('Ironman') + /// listitem.setInfo('video', {'Title': 'Ironman', 'Genre': 'Science Fiction'}) + /// xbmc.Player().play(url, listitem, windowed) + /// xbmc.Player().play(playlist, listitem, windowed, startpos) + /// ... + /// ~~~~~~~~~~~~~ + /// + play(...); +#else + void play(const PlayParameter& item = Player::defaultPlayParameter, + const XBMCAddon::xbmcgui::ListItem* listitem = NULL, bool windowed = false, int startpos = -1); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ stop() } + /// Stop playing. + /// + stop(); +#else + void stop(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ pause() } + /// Pause or resume playing if already paused. + /// + pause(); +#else + void pause(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ playnext() } + /// Play next item in playlist. + /// + playnext(); +#else + void playnext(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ playprevious() } + /// Play previous item in playlist. + /// + playprevious(); +#else + void playprevious(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ playselected(selected) } + /// Play a certain item from the current playlist. + /// + /// @param selected Integer - Item to select + /// + playselected(...); +#else + void playselected(int selected); +#endif + + // + /// @defgroup python_PlayerCB Callback functions from Kodi to Add-On + /// \ingroup python_Player + /// @{ + /// @brief **Callback functions.** + /// + /// Functions to handle control callbacks from Kodi to Add-On. + /// + /// ---------------------------------------------------------------------- + /// + /// @link python_Player Go back to normal functions from player@endlink + // + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackStarted() } + /// onPlayBackStarted method. + /// + /// Will be called when Kodi player starts. Video or audio might not be available at this point. + /// + ///------------------------------------------------------------------------ + /// @python_v18 Use onAVStarted() instead if you need to detect if Kodi is actually playing a media file + /// (i.e, if a stream is available) + /// + onPlayBackStarted(); +#else + virtual void onPlayBackStarted(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onAVStarted() } + /// onAVStarted method. + /// + /// Will be called when Kodi has a video or audiostream. + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + onAVStarted(); +#else + virtual void onAVStarted(); +#endif + + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onAVChange() } + /// onAVChange method. + /// + /// Will be called when Kodi has a video, audio or subtitle stream. Also happens when the stream changes. + /// + ///------------------------------------------------------------------------ + /// @python_v18 New function added. + /// + onAVChange(); +#else + virtual void onAVChange(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackEnded() } + /// onPlayBackEnded method. + /// + /// Will be called when Kodi stops playing a file. + /// + onPlayBackEnded(); +#else + virtual void onPlayBackEnded(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackStopped() } + /// onPlayBackStopped method. + /// + /// Will be called when user stops Kodi playing a file. + /// + onPlayBackStopped(); +#else + virtual void onPlayBackStopped(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackError() } + /// onPlayBackError method. + /// + /// Will be called when playback stops due to an error. + /// + onPlayBackError(); +#else + virtual void onPlayBackError(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackPaused() } + /// onPlayBackPaused method. + /// + /// Will be called when user pauses a playing file. + /// + onPlayBackPaused(); +#else + virtual void onPlayBackPaused(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackResumed() } + /// onPlayBackResumed method. + /// + /// Will be called when user resumes a paused file. + /// + onPlayBackResumed(); +#else + virtual void onPlayBackResumed(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onQueueNextItem() } + /// onQueueNextItem method. + /// + /// Will be called when user queues the next item. + /// + onQueueNextItem(); +#else + virtual void onQueueNextItem(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackSpeedChanged(speed) } + /// onPlayBackSpeedChanged method. + /// + /// Will be called when players speed changes (eg. user FF/RW). + /// + /// @param speed [integer] Current speed of player + /// + /// @note Negative speed means player is rewinding, 1 is normal playback + /// speed. + /// + onPlayBackSpeedChanged(int speed); +#else + virtual void onPlayBackSpeedChanged(int speed); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackSeek(time, seekOffset) } + /// onPlayBackSeek method. + /// + /// Will be called when user seeks to a time. + /// + /// @param time [integer] Time to seek to + /// @param seekOffset [integer] ? + /// + onPlayBackSeek(...); +#else + virtual void onPlayBackSeek(int time, int seekOffset); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_PlayerCB + /// @brief \python_func{ onPlayBackSeekChapter(chapter) } + /// onPlayBackSeekChapter method. + /// + /// Will be called when user performs a chapter seek. + /// + /// @param chapter [integer] Chapter to seek to + /// + onPlayBackSeekChapter(...); +#else + virtual void onPlayBackSeekChapter(int chapter); +#endif + /// @} + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isPlaying() } + /// Check Kodi is playing something. + /// + /// @return True if Kodi is playing a file. + /// + isPlaying(); +#else + bool isPlaying(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isPlayingAudio() } + /// Check for playing audio. + /// + /// @return True if Kodi is playing an audio file. + /// + isPlayingAudio(); +#else + bool isPlayingAudio(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isPlayingVideo() } + /// Check for playing video. + /// + /// @return True if Kodi is playing a video. + /// + isPlayingVideo(); +#else + bool isPlayingVideo(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isPlayingRDS() } + /// Check for playing radio data system (RDS). + /// + /// @return True if kodi is playing a radio data + /// system (RDS). + /// + isPlayingRDS(); +#else + bool isPlayingRDS(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isPlayingGame() } + /// Check for playing game. + /// + /// @return True if kodi is playing a game + /// + ///------------------------------------------------------------------------ + /// @python_v20 New function added. + /// + isPlayingGame(); +#else + bool isPlayingGame(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ isExternalPlayer() } + /// Check for external player. + /// + /// @return True if kodi is playing using an + /// external player. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v18 New function added. + /// + isExternalPlayer(); +#else + bool isExternalPlayer(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getPlayingFile() } + /// Returns the current playing file as a string. + /// + /// @note For LiveTV, returns a __pvr://__ url which is not translatable + /// to an OS specific file or external url. + /// + /// @return Playing filename + /// @throws Exception If player is not playing a file. + /// + getPlayingFile(); +#else + String getPlayingFile(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getPlayingItem() } + /// Returns the current playing item. + /// + /// @return Playing item + /// @throws Exception If player is not playing a file. + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + getPlayingItem(); +#else + XBMCAddon::xbmcgui::ListItem* getPlayingItem(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getTime() } + /// Get playing time. + /// + /// Returns the current time of the current playing media as fractional + /// seconds. + /// + /// @return Current time as fractional seconds + /// @throws Exception If player is not playing a file. + /// + getTime(); +#else + double getTime(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ seekTime(seekTime) } + /// Seek time. + /// + /// Seeks the specified amount of time as fractional seconds. + /// The time specified is relative to the beginning of the currently. + /// playing media file. + /// + /// @param seekTime Time to seek as fractional seconds + /// @throws Exception If player is not playing a file. + /// + seekTime(...); +#else + void seekTime(double seekTime); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ setSubtitles(subtitleFile) } + /// Set subtitle file and enable subtitles. + /// + /// @param subtitleFile File to use as source ofsubtitles + /// + setSubtitles(...); +#else + void setSubtitles(const char* subtitleFile); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ showSubtitles(visible) } + /// Enable / disable subtitles. + /// + /// @param visible [boolean] True for visible subtitles. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// xbmc.Player().showSubtitles(True) + /// ... + /// ~~~~~~~~~~~~~ + /// + showSubtitles(...); +#else + void showSubtitles(bool bVisible); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getSubtitles() } + /// Get subtitle stream name. + /// + /// @return Stream name + /// + getSubtitles(); +#else + String getSubtitles(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getAvailableSubtitleStreams() } + /// Get Subtitle stream names. + /// + /// @return List of subtitle streams as name + /// + getAvailableSubtitleStreams(); +#else + std::vector<String> getAvailableSubtitleStreams(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ setSubtitleStream(stream) } + /// Set Subtitle Stream. + /// + /// @param iStream [int] Subtitle stream to select for play + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// xbmc.Player().setSubtitleStream(1) + /// ... + /// ~~~~~~~~~~~~~ + /// + setSubtitleStream(...); +#else + void setSubtitleStream(int iStream); +#endif + + // Player_UpdateInfoTag +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ updateInfoTag(item) } + /// Update info labels for currently playing item. + /// + /// @param item ListItem with new info + /// + /// @throws Exception If player is not playing a file + /// + /// @python_v18 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// item = xbmcgui.ListItem() + /// item.setPath(xbmc.Player().getPlayingFile()) + /// item.setInfo('music', {'title' : 'foo', 'artist' : 'bar'}) + /// xbmc.Player().updateInfoTag(item) + /// ... + /// ~~~~~~~~~~~~~ + /// + updateInfoTag(); +#else + void updateInfoTag(const XBMCAddon::xbmcgui::ListItem* item); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getGameInfoTag() } + /// To get game info tag. + /// + /// Returns the GameInfoTag of the current playing game. + /// + /// @return Game info tag + /// @throws Exception If player is not playing a file or current + /// file is not a game file. + /// + ///------------------------------------------------------------------------ + /// @python_v20 New function added. + /// + getGameInfoTag(); +#else + InfoTagGame* getGameInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getVideoInfoTag() } + /// To get video info tag. + /// + /// Returns the VideoInfoTag of the current playing Movie. + /// + /// @return Video info tag + /// @throws Exception If player is not playing a file or current + /// file is not a movie file. + /// + getVideoInfoTag(); +#else + InfoTagVideo* getVideoInfoTag(); +#endif + + // Player_GetMusicInfoTag +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getMusicInfoTag() } + /// To get music info tag. + /// + /// Returns the MusicInfoTag of the current playing 'Song'. + /// + /// @return Music info tag + /// @throws Exception If player is not playing a file or current + /// file is not a music file. + /// + getMusicInfoTag(); +#else + InfoTagMusic* getMusicInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getRadioRDSInfoTag() } + /// To get Radio RDS info tag + /// + /// Returns the RadioRDSInfoTag of the current playing 'Radio Song if. + /// present'. + /// + /// @return Radio RDS info tag + /// @throws Exception If player is not playing a file or current + /// file is not a rds file. + /// + getRadioRDSInfoTag(); +#else + InfoTagRadioRDS* getRadioRDSInfoTag(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getTotalTime() } + /// To get total playing time. + /// + /// Returns the total time of the current playing media in seconds. + /// This is only accurate to the full second. + /// + /// @return Total time of the current playing media + /// @throws Exception If player is not playing a file. + /// + getTotalTime(); +#else + double getTotalTime(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getAvailableAudioStreams() } + /// Get Audio stream names + /// + /// @return List of audio streams as name + /// + getAvailableAudioStreams(); +#else + std::vector<String> getAvailableAudioStreams(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ setAudioStream(stream) } + /// Set Audio Stream. + /// + /// @param iStream [int] Audio stream to select for play + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// xbmc.Player().setAudioStream(1) + /// ... + /// ~~~~~~~~~~~~~ + /// + setAudioStream(...); +#else + void setAudioStream(int iStream); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ getAvailableVideoStreams() } + /// Get Video stream names + /// + /// @return List of video streams as name + /// + getAvailableVideoStreams(); +#else + std::vector<String> getAvailableVideoStreams(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_Player + /// @brief \python_func{ setVideoStream(stream) } + /// Set Video Stream. + /// + /// @param iStream [int] Video stream to select for play + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// ... + /// xbmc.Player().setVideoStream(1) + /// ... + /// ~~~~~~~~~~~~~ + /// + setVideoStream(...); +#else + void setVideoStream(int iStream); +#endif + +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + void OnPlayBackStarted(const CFileItem& file) override; + void OnAVStarted(const CFileItem& file) override; + void OnAVChange() override; + void OnPlayBackEnded() override; + void OnPlayBackStopped() override; + void OnPlayBackError() override; + void OnPlayBackPaused() override; + void OnPlayBackResumed() override; + void OnQueueNextItem() override; + void OnPlayBackSpeedChanged(int iSpeed) override; + void OnPlayBackSeek(int64_t iTime, int64_t seekOffset) override; + void OnPlayBackSeekChapter(int iChapter) override; +#endif + + protected: + }; + } +} + diff --git a/xbmc/interfaces/legacy/RenderCapture.h b/xbmc/interfaces/legacy/RenderCapture.h new file mode 100644 index 0000000..cf1b931 --- /dev/null +++ b/xbmc/interfaces/legacy/RenderCapture.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "Exception.h" +#include "ServiceBroker.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationPlayer.h" +#include "commons/Buffer.h" + +#include <climits> + +namespace XBMCAddon +{ + namespace xbmc + { + XBMCCOMMONS_STANDARD_EXCEPTION(RenderCaptureException); + + // + /// \defgroup python_xbmc_RenderCapture RenderCapture + /// \ingroup python_xbmc + /// @{ + /// @brief **Kodi's render capture.** + /// + /// \python_class{ RenderCapture() } + /// + /// + ///-------------------------------------------------------------------------- + /// + // + class RenderCapture : public AddonClass + { + unsigned int m_captureId; + unsigned int m_width; + unsigned int m_height; + uint8_t *m_buffer; + + public: + inline RenderCapture() + { + m_captureId = UINT_MAX; + m_buffer = nullptr; + m_width = 0; + m_height = 0; + } + inline ~RenderCapture() override + { + auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + appPlayer->RenderCaptureRelease(m_captureId); + delete [] m_buffer; + } + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ getWidth() } + /// Get width + /// + /// To get width of captured image as set during RenderCapture.capture(). + /// Returns 0 prior to calling capture. + /// + /// @return Width or 0 prior to calling capture + /// + getWidth(); +#else + inline int getWidth() { return m_width; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ getHeight() } + /// Get height + /// + /// To get height of captured image as set during RenderCapture.capture(). + /// Returns 0 prior to calling capture. + /// + /// @return height or 0 prior to calling capture + getHeight(); +#else + inline int getHeight() { return m_height; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ getAspectRatio() } + /// Get aspect ratio of currently displayed video. + /// + /// @return Aspect ratio + /// @warning This may be called prior to calling RenderCapture.capture(). + /// + getAspectRatio(); +#else + inline float getAspectRatio() + { + const auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + return appPlayer->GetRenderAspectRatio(); + } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ getImageFormat() } + /// Get image format + /// + /// @return Format of captured image: 'BGRA' + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 Image will now always be returned in BGRA + /// + getImageFormat() +#else + inline const char* getImageFormat() +#endif + { + return "BGRA"; + } + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ getImage([msecs]) } + /// Returns captured image as a bytearray. + /// + /// @param msecs [opt] Milliseconds to wait. Waits + /// 1000ms if not specified + /// @return Captured image as a bytearray + /// + /// @note The size of the image is m_width * m_height * 4 + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 Added the option to specify wait time in msec. + /// + getImage(...) +#else + inline XbmcCommons::Buffer getImage(unsigned int msecs = 0) +#endif + { + if (!GetPixels(msecs)) + return XbmcCommons::Buffer(0); + + size_t size = m_width * m_height * 4; + return XbmcCommons::Buffer(m_buffer, size); + } + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmc_RenderCapture + /// @brief \python_func{ capture(width, height) } + /// Issue capture request. + /// + /// @param width Width capture image should be rendered to + /// @param height Height capture image should should be rendered to + /// + /// + ///----------------------------------------------------------------------- + /// @python_v17 Removed the option to pass **flags** + /// + capture(...) +#else + inline void capture(int width, int height) +#endif + { + auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + + if (m_buffer) + { + appPlayer->RenderCaptureRelease(m_captureId); + delete [] m_buffer; + } + m_captureId = appPlayer->RenderCaptureAlloc(); + m_width = width; + m_height = height; + m_buffer = new uint8_t[m_width*m_height*4]; + appPlayer->RenderCapture(m_captureId, m_width, m_height, CAPTUREFLAG_CONTINUOUS); + } + +// hide these from swig +#ifndef SWIG + inline bool GetPixels(unsigned int msec) + { + auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + return appPlayer->RenderCaptureGetPixels(m_captureId, msec, m_buffer, + m_width * m_height * 4); + } +#endif + + }; + //@} + } +} diff --git a/xbmc/interfaces/legacy/Settings.cpp b/xbmc/interfaces/legacy/Settings.cpp new file mode 100644 index 0000000..0ba77c3 --- /dev/null +++ b/xbmc/interfaces/legacy/Settings.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2017-2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Settings.h" + +#include "settings/SettingsBase.h" +#include "settings/lib/Setting.h" + +#include <algorithm> +#include <functional> +#include <utility> + +namespace XBMCAddon +{ +namespace xbmcaddon +{ + +template<class TSetting> +bool GetSettingValue(const std::shared_ptr<CSettingsBase>& settings, + const std::string& key, + typename TSetting::Value& value) +{ + if (key.empty() || !settings->IsLoaded()) + return false; + + auto setting = settings->GetSetting(key); + if (setting == nullptr || setting->GetType() != TSetting::Type()) + return false; + + value = std::static_pointer_cast<TSetting>(setting)->GetValue(); + return true; +} + +template<class TSetting> +bool GetSettingValueList(const std::shared_ptr<CSettingsBase>& settings, + const std::string& key, + std::function<typename TSetting::Value(CVariant)> transform, + std::vector<typename TSetting::Value>& values) +{ + if (key.empty() || !settings->IsLoaded()) + return false; + + auto setting = settings->GetSetting(key); + if (setting == nullptr || setting->GetType() != SettingType::List || + std::static_pointer_cast<CSettingList>(setting)->GetElementType() != TSetting::Type()) + return false; + + const auto variantValues = settings->GetList(key); + std::transform(variantValues.begin(), variantValues.end(), std::back_inserter(values), transform); + return true; +} + +template<class TSetting> +bool SetSettingValue(const std::shared_ptr<CSettingsBase>& settings, + const std::string& key, + typename TSetting::Value value) +{ + if (key.empty() || !settings->IsLoaded()) + return false; + + // try to get the setting + auto setting = settings->GetSetting(key); + if (setting == nullptr || setting->GetType() != TSetting::Type()) + return false; + + return std::static_pointer_cast<TSetting>(setting)->SetValue(value); +} + +template<class TSetting> +bool SetSettingValueList(const std::shared_ptr<CSettingsBase>& settings, + const std::string& key, + const std::vector<typename TSetting::Value>& values) +{ + if (key.empty() || !settings->IsLoaded()) + return false; + + // try to get the setting + auto setting = settings->GetSetting(key); + if (setting == nullptr || setting->GetType() != SettingType::List || + std::static_pointer_cast<CSettingList>(setting)->GetElementType() != TSetting::Type()) + return false; + + std::vector<CVariant> variantValues; + std::transform(values.begin(), values.end(), std::back_inserter(variantValues), + [](typename TSetting::Value value) { return CVariant(value); }); + + return settings->SetList(key, variantValues); +} + +Settings::Settings(std::shared_ptr<CSettingsBase> settings) : settings(std::move(settings)) +{ +} + +bool Settings::getBool(const char* id) +{ + bool value = false; + if (!GetSettingValue<CSettingBool>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"boolean\" for \"%s\"", id); + + return value; +} + +int Settings::getInt(const char* id) +{ + int value = 0; + if (!GetSettingValue<CSettingInt>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"integer\" for \"%s\"", id); + + return value; +} + +double Settings::getNumber(const char* id) +{ + double value = 0.0; + if (!GetSettingValue<CSettingNumber>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"number\" for \"%s\"", id); + + return value; +} + +String Settings::getString(const char* id) +{ + std::string value; + if (!GetSettingValue<CSettingString>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"string\" for \"%s\"", id); + + return value; +} + +std::vector<bool> Settings::getBoolList(const char* id) +{ + const auto transform = [](const CVariant& value) { return value.asBoolean(); }; + std::vector<bool> values; + if (!GetSettingValueList<CSettingBool>(settings, id, transform, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[boolean]\" for \"%s\"", id); + + return values; +} + +std::vector<int> Settings::getIntList(const char* id) +{ + const auto transform = [](const CVariant& value) { return value.asInteger32(); }; + std::vector<int> values; + if (!GetSettingValueList<CSettingInt>(settings, id, transform, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[integer]\" for \"%s\"", id); + + return values; +} + +std::vector<double> Settings::getNumberList(const char* id) +{ + const auto transform = [](const CVariant& value) { return value.asDouble(); }; + std::vector<double> values; + if (!GetSettingValueList<CSettingNumber>(settings, id, transform, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[number]\" for \"%s\"", id); + + return values; +} + +std::vector<String> Settings::getStringList(const char* id) +{ + const auto transform = [](const CVariant& value) { return value.asString(); }; + std::vector<std::string> values; + if (!GetSettingValueList<CSettingString>(settings, id, transform, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[string]\" for \"%s\"", id); + + return values; +} + +void Settings::setBool(const char* id, bool value) +{ + if (!SetSettingValue<CSettingBool>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"boolean\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setInt(const char* id, int value) +{ + if (!SetSettingValue<CSettingInt>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"integer\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setNumber(const char* id, double value) +{ + if (!SetSettingValue<CSettingNumber>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"number\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setString(const char* id, const String& value) +{ + if (!SetSettingValue<CSettingString>(settings, id, value)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"string\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setBoolList(const char* id, const std::vector<bool>& values) +{ + if (!SetSettingValueList<CSettingBool>(settings, id, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[boolean]\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setIntList(const char* id, const std::vector<int>& values) +{ + if (!SetSettingValueList<CSettingInt>(settings, id, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[integer]\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setNumberList(const char* id, const std::vector<double>& values) +{ + if (!SetSettingValueList<CSettingNumber>(settings, id, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[number]\" for \"%s\"", id); + settings->Save(); +} + +void Settings::setStringList(const char* id, const std::vector<String>& values) +{ + if (!SetSettingValueList<CSettingString>(settings, id, values)) + throw XBMCAddon::WrongTypeException("Invalid setting type \"list[string]\" for \"%s\"", id); + settings->Save(); +} + +} // namespace xbmcaddon +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/Settings.h b/xbmc/interfaces/legacy/Settings.h new file mode 100644 index 0000000..8b8bb0e --- /dev/null +++ b/xbmc/interfaces/legacy/Settings.h @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2017-2021 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "commons/Exception.h" +#include "interfaces/legacy/AddonClass.h" +#include "interfaces/legacy/AddonString.h" +#include "interfaces/legacy/Exception.h" +#include "interfaces/legacy/Tuple.h" +#include "settings/lib/SettingDefinitions.h" + +#include <memory> +#include <string> +#include <vector> + +class CSettingsBase; + +namespace XBMCAddon +{ +namespace xbmcaddon +{ + +XBMCCOMMONS_STANDARD_EXCEPTION(SettingCallbacksNotSupportedException); + +// +/// \defgroup python_settings Settings +/// \ingroup python_xbmcaddon +/// @{ +/// @brief **Add-on settings** +/// +/// \python_class{ Settings() } +/// +/// This wrapper provides access to the settings specific to an add-on. +/// It supports reading and writing specific setting values. +/// +///----------------------------------------------------------------------- +/// @python_v20 New class added. +/// +/// **Example:** +/// ~~~~~~~~~~~~~{.py} +/// ... +/// settings = xbmcaddon.Addon('id').getSettings() +/// ... +/// ~~~~~~~~~~~~~ +// +class Settings : public AddonClass +{ +public: +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + std::shared_ptr<CSettingsBase> settings; +#endif + +#ifndef SWIG + Settings(std::shared_ptr<CSettingsBase> settings); +#endif + + virtual ~Settings() = default; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getBool(id) } + /// Returns the value of a setting as a boolean. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return bool - Setting as a boolean + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// enabled = settings.getBool('enabled') + /// .. + /// ~~~~~~~~~~~~~ + /// + getBool(...); +#else + bool getBool(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getInt(id) } + /// Returns the value of a setting as an integer. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return integer - Setting as an integer + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// max = settings.getInt('max') + /// .. + /// ~~~~~~~~~~~~~ + /// + getInt(...); +#else + int getInt(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getNumber(id) } + /// Returns the value of a setting as a floating point number. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return float - Setting as a floating point number + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// max = settings.getNumber('max') + /// .. + /// ~~~~~~~~~~~~~ + /// + getNumber(...); +#else + double getNumber(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getString(id) } + /// Returns the value of a setting as a unicode string. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return string - Setting as a unicode string + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// apikey = settings.getString('apikey') + /// .. + /// ~~~~~~~~~~~~~ + /// + getString(...); +#else + String getString(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getBoolList(id) } + /// Returns the value of a setting as a list of booleans. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return list - Setting as a list of booleans + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// enabled = settings.getBoolList('enabled') + /// .. + /// ~~~~~~~~~~~~~ + /// + getBoolList(...); +#else + std::vector<bool> getBoolList(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getIntList(id) } + /// Returns the value of a setting as a list of integers. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return list - Setting as a list of integers + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// ids = settings.getIntList('ids') + /// .. + /// ~~~~~~~~~~~~~ + /// + getIntList(...); +#else + std::vector<int> getIntList(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getNumberList(id) } + /// Returns the value of a setting as a list of floating point numbers. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return list - Setting as a list of floating point numbers + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// max = settings.getNumberList('max') + /// .. + /// ~~~~~~~~~~~~~ + /// + getNumberList(...); +#else + std::vector<double> getNumberList(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ getStringList(id) } + /// Returns the value of a setting as a list of unicode strings. + /// + /// @param id string - id of the setting that the module needs to access. + /// @return list - Setting as a list of unicode strings + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// views = settings.getStringList('views') + /// .. + /// ~~~~~~~~~~~~~ + /// + getStringList(...); +#else + std::vector<String> getStringList(const char* id); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setBool(id, value) } + /// Sets the value of a setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value bool - value of the setting. + /// @return bool - True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setBool(id='enabled', value=True) + /// .. + /// ~~~~~~~~~~~~~ + /// + setBool(...); +#else + void setBool(const char* id, bool value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setInt(id, value) } + /// Sets the value of a setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value integer - value of the setting. + /// @return bool - True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setInt(id='max', value=5) + /// .. + /// ~~~~~~~~~~~~~ + /// + setInt(...); +#else + void setInt(const char* id, int value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setNumber(id, value) } + /// Sets the value of a setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value float - value of the setting. + /// @return bool - True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setNumber(id='max', value=5.5) + /// .. + /// ~~~~~~~~~~~~~ + /// + setNumber(...); +#else + void setNumber(const char* id, double value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setString(id, value) } + /// Sets the value of a setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param value string or unicode - value of the setting. + /// @return bool - True if the value of the setting was set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setString(id='username', value='teamkodi') + /// .. + /// ~~~~~~~~~~~~~ + /// + setString(...); +#else + void setString(const char* id, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setBoolList(id, values) } + /// Sets the boolean values of a list setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param values list of boolean - values of the setting. + /// @return bool - True if the values of the setting were set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setBoolList(id='enabled', values=[ True, False ]) + /// .. + /// ~~~~~~~~~~~~~ + /// + setBoolList(...); +#else + void setBoolList(const char* id, const std::vector<bool>& values); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setIntList(id, value) } + /// Sets the integer values of a list setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param values list of int - values of the setting. + /// @return bool - True if the values of the setting were set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setIntList(id='max', values=[ 5, 23 ]) + /// .. + /// ~~~~~~~~~~~~~ + /// + setIntList(...); +#else + void setIntList(const char* id, const std::vector<int>& values); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setNumberList(id, value) } + /// Sets the floating point values of a list setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param values list of float - values of the setting. + /// @return bool - True if the values of the setting were set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setNumberList(id='max', values=[ 5.5, 5.8 ]) + /// .. + /// ~~~~~~~~~~~~~ + /// + setNumberList(...); +#else + void setNumberList(const char* id, const std::vector<double>& values); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_settings + /// @brief \python_func{ setStringList(id, value) } + /// Sets the string values of a list setting. + /// + /// @param id string - id of the setting that the module needs to access. + /// @param values list of string or unicode - values of the setting. + /// @return bool - True if the values of the setting were set, false otherwise + /// + /// + /// @note You can use the above as keywords for arguments. + /// + /// + ///----------------------------------------------------------------------- + /// @python_v20 New function added. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// settings.setStringList(id='username', values=[ 'team', 'kodi' ]) + /// .. + /// ~~~~~~~~~~~~~ + /// + setStringList(...); +#else + void setStringList(const char* id, const std::vector<String>& values); +#endif +}; +//@} + +} // namespace xbmcaddon +} // namespace XBMCAddon diff --git a/xbmc/interfaces/legacy/Stat.h b/xbmc/interfaces/legacy/Stat.h new file mode 100644 index 0000000..90a3843 --- /dev/null +++ b/xbmc/interfaces/legacy/Stat.h @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonClass.h" +#include "LanguageHook.h" +#include "filesystem/File.h" + +namespace XBMCAddon +{ + namespace xbmcvfs + { + // + /// \defgroup python_stat Stat + /// \ingroup python_xbmcvfs + /// @{ + /// @brief **Get file or file system status.** + /// + /// \python_class{ xbmcvfs.Stat(path) } + /// + /// These class return information about a file. Execute (search) permission + /// is required on all of the directories in path that lead to the file. + /// + /// @param path [string] file or folder + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v12 New function added + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// st = xbmcvfs.Stat(path) + /// modified = st.st_mtime() + /// .. + /// ~~~~~~~~~~~~~ + // + class Stat : public AddonClass + { + struct __stat64 st; + + public: + Stat(const String& path) + { + DelayedCallGuard dg; + XFILE::CFile::Stat(path, &st); + } + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_mode() } + /// To get file protection. + /// + /// @return st_mode + /// + st_mode(); +#else + inline long long st_mode() { return st.st_mode; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_ino() } + /// To get inode number. + /// + /// @return st_ino + /// + st_ino(); +#else + inline long long st_ino() { return st.st_ino; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_dev() } + /// To get ID of device containing file. + /// + /// The st_dev field describes the device on which this file resides. + /// + /// @return st_dev + /// + st_dev(); +#else + inline long long st_dev() { return st.st_dev; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_nlink() } + /// To get number of hard links. + /// + /// @return st_nlink + /// + st_nlink(); +#else + inline long long st_nlink() { return st.st_nlink; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_uid() } + /// To get user ID of owner. + /// + /// @return st_uid + /// + st_uid(); +#else + inline long long st_uid() { return st.st_uid; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_gid() } + /// To get group ID of owner. + /// + /// @return st_gid + /// + st_gid(); +#else + inline long long st_gid() { return st.st_gid; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_size() } + /// To get total size, in bytes. + /// + /// The st_size field gives the size of the file (if it is a regular file + /// or a symbolic link) in bytes. The size of a symbolic link (only on + /// Linux and Mac OS X) is the length of the pathname it contains, without + /// a terminating null byte. + /// + /// @return st_size + /// + st_size(); +#else + inline long long st_size() { return st.st_size; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_atime() } + /// To get time of last access. + /// + /// @return st_atime + /// + st_atime(); +#else + inline long long atime() { return st.st_atime; }; //names st_atime/st_mtime/st_ctime are used by sys/stat.h +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_mtime() } + /// To get time of last modification. + /// + /// @return st_mtime + /// + st_mtime(); +#else + inline long long mtime() { return st.st_mtime; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_stat + /// @brief \python_func{ st_ctime() } + /// To get time of last status change. + /// + /// @return st_ctime + /// + st_ctime(); +#else + inline long long ctime() { return st.st_ctime; } +#endif + }; + /// @} + } +} + diff --git a/xbmc/interfaces/legacy/String.cpp b/xbmc/interfaces/legacy/String.cpp new file mode 100644 index 0000000..cb172a2 --- /dev/null +++ b/xbmc/interfaces/legacy/String.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "AddonString.h" + +namespace XBMCAddon +{ + String emptyString; +} diff --git a/xbmc/interfaces/legacy/Tuple.h b/xbmc/interfaces/legacy/Tuple.h new file mode 100644 index 0000000..116f307 --- /dev/null +++ b/xbmc/interfaces/legacy/Tuple.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +/** + * This file contains a few templates to define various length + * Tuples. + */ +namespace XBMCAddon +{ + struct tuple_null_type { }; + + class TupleBase + { + protected: + int numValuesSet; + explicit inline TupleBase(int pnumValuesSet) : numValuesSet(pnumValuesSet) {} + inline void nvs(int newSize) { if(numValuesSet < newSize) numValuesSet = newSize; } + public: + inline int GetNumValuesSet() const { return numValuesSet; } + }; + + // stub type template to be partial specialized + template<typename T1 = tuple_null_type, typename T2 = tuple_null_type, + typename T3 = tuple_null_type, typename T4 = tuple_null_type, + typename Extraneous = tuple_null_type> class Tuple {}; + + // Tuple that holds a single value + template<typename T1> class Tuple<T1, tuple_null_type, tuple_null_type, tuple_null_type, tuple_null_type> : public TupleBase + { + private: + T1 v1; + public: + explicit inline Tuple(T1 p1) : TupleBase(1), v1(p1) {} + inline Tuple() : TupleBase(0) {} + inline Tuple(const Tuple<T1>& o) : TupleBase(o), v1(o.v1) {} + Tuple<T1>& operator=(const Tuple<T1>& other) = default; + + inline T1& first() { TupleBase::nvs(1); return v1; } + inline const T1& first() const { return v1; } + }; + + // Tuple that holds two values + template<typename T1, typename T2> class Tuple<T1, T2, tuple_null_type, tuple_null_type, tuple_null_type> : public Tuple<T1> + { + protected: + T2 v2; + + public: + inline Tuple(T1 p1, T2 p2) : Tuple<T1>(p1), v2(p2) { TupleBase::nvs(2); } + explicit inline Tuple(T1 p1) : Tuple<T1>(p1) {} + inline Tuple() = default; + Tuple<T1, T2>& operator=(const Tuple<T1, T2>& other) = default; + inline Tuple(const Tuple<T1,T2>& o) : Tuple<T1>(o), v2(o.v2) {} + + inline T2& second() { TupleBase::nvs(2); return v2; } + inline const T2& second() const { return v2; } + }; + + // Tuple that holds three values + template<typename T1, typename T2, typename T3> class Tuple<T1, T2, T3, tuple_null_type, tuple_null_type> : public Tuple<T1,T2> + { + private: + T3 v3; + public: + inline Tuple(T1 p1, T2 p2, T3 p3) : Tuple<T1,T2>(p1,p2), v3(p3) { TupleBase::nvs(3); } + inline Tuple(T1 p1, T2 p2) : Tuple<T1,T2>(p1,p2) {} + explicit inline Tuple(T1 p1) : Tuple<T1,T2>(p1) {} + inline Tuple() = default; + inline Tuple(const Tuple<T1,T2,T3>& o) : Tuple<T1,T2>(o), v3(o.v3) {} + + inline T3& third() { TupleBase::nvs(3); return v3; } + inline const T3& third() const { return v3; } + }; + + // Tuple that holds four values + template<typename T1, typename T2, typename T3, typename T4> class Tuple<T1, T2, T3, T4, tuple_null_type> : public Tuple<T1,T2,T3> + { + private: + T4 v4; + public: + inline Tuple(T1 p1, T2 p2, T3 p3, T4 p4) : Tuple<T1,T2,T3>(p1,p2,p3), v4(p4) { TupleBase::nvs(4); } + inline Tuple(T1 p1, T2 p2, T3 p3) : Tuple<T1,T2,T3>(p1,p2,p3) {} + inline Tuple(T1 p1, T2 p2) : Tuple<T1,T2,T3>(p1,p2) {} + explicit inline Tuple(T1 p1) : Tuple<T1,T2,T3>(p1) {} + inline Tuple() = default; + inline Tuple(const Tuple<T1,T2,T3,T4>& o) : Tuple<T1,T2,T3>(o), v4(o.v4) {} + + inline T4& fourth() { TupleBase::nvs(4); return v4; } + inline const T4& fourth() const { return v4; } + }; +} diff --git a/xbmc/interfaces/legacy/Window.cpp b/xbmc/interfaces/legacy/Window.cpp new file mode 100644 index 0000000..100803e --- /dev/null +++ b/xbmc/interfaces/legacy/Window.cpp @@ -0,0 +1,767 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "Window.h" + +#include "ServiceBroker.h" +#include "WindowException.h" +#include "WindowInterceptor.h" +#include "application/Application.h" +#include "guilib/GUIButtonControl.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUIWindowManager.h" +#include "messaging/ApplicationMessenger.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +#define ACTIVE_WINDOW CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() + +namespace XBMCAddon +{ + namespace xbmcgui + { + thread_local ref* InterceptorBase::upcallTls; + + /** + * Used in add/remove control. It only locks if it's given a + * non-NULL CCriticalSection. It's given a NULL CCriticalSection + * when a function higher in the call stack already has a + */ + class MaybeLock + { + CCriticalSection* lock; + public: + inline explicit MaybeLock(CCriticalSection* p_lock) : lock(p_lock) { if (lock) lock->lock(); } + inline ~MaybeLock() { if (lock) lock->unlock(); } + }; + + class SingleLockWithDelayGuard + { + DelayedCallGuard dcg; + CCriticalSection& lock; + public: + inline SingleLockWithDelayGuard(CCriticalSection& ccrit, LanguageHook* lh) : dcg(lh), lock(ccrit) { lock.lock(); } + inline ~SingleLockWithDelayGuard() { lock.unlock(); } + }; + + /** + * Explicit template instantiation + */ + template class Interceptor<CGUIWindow>; + + /** + * This interceptor is a simple, non-callbackable (is that a word?) + * Interceptor to satisfy the Window requirements for upcalling + * for the purposes of instantiating a Window instance from + * an already existing window. + */ + class ProxyExistingWindowInterceptor : public InterceptorBase + { + CGUIWindow* cguiwindow; + + public: + inline ProxyExistingWindowInterceptor(CGUIWindow* window) : + cguiwindow(window) { XBMC_TRACE; } + + CGUIWindow* get() override; + }; + + CGUIWindow* ProxyExistingWindowInterceptor::get() { XBMC_TRACE; return cguiwindow; } + + Window::Window(bool discrim): + window(NULL), + m_actionEvent(true), + canPulse(true), existingWindow(false) + { + XBMC_TRACE; + } + + /** + * This just creates a default window. + */ + Window::Window(int existingWindowId) : + window(NULL), + m_actionEvent(true) + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + + if (existingWindowId == -1) + { + // in this case just do the other constructor. + canPulse = true; + existingWindow = false; + + setWindow(new Interceptor<CGUIWindow>("CGUIWindow",this,getNextAvailableWindowId())); + } + else + { + // user specified window id, use this one if it exists + // It is not possible to capture key presses or button presses + CGUIWindow* pWindow = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(existingWindowId); + if (!pWindow) + throw WindowException("Window id does not exist"); + + setWindow(new ProxyExistingWindowInterceptor(pWindow)); + } + } + + Window::~Window() + { + XBMC_TRACE; + + deallocating(); + } + + void Window::deallocating() + { + AddonCallback::deallocating(); + + dispose(); + } + + void Window::dispose() + { + XBMC_TRACE; + + //! @todo rework locking + // Python GIL and CServiceBroker::GetWinSystem()->GetGfxContext() are deadlock happy + // dispose is called from GUIWindowManager and in this case DelayGuard must not be used. + if (!CServiceBroker::GetAppMessenger()->IsProcessThread()) + { + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook); + } + + if (!isDisposed) + { + isDisposed = true; + + // no callbacks are possible any longer + // - this will be handled by the parent constructor + + // first change to an existing window + if (!existingWindow) + { + if (ACTIVE_WINDOW == iWindowId && !g_application.m_bStop) + { + if(CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iOldWindowId)) + { + CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(iOldWindowId); + } + // old window does not exist anymore, switch to home + else CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME); + } + + } + else + { + //! @bug + //! This is an existing window, so no resources are free'd. Note that + //! THIS WILL FAIL for any controls newly created by python - they will + //! remain after the script ends. Ideally this would be remedied by + //! a flag in Control that specifies that it was python created - any python + //! created controls could then be removed + free'd from the window. + //! how this works with controlgroups though could be a bit tricky. + } + + // and free our list of controls + std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin(); + while (it != vecControls.end()) + { + AddonClass::Ref<Control> pControl = *it; + // initialize control to zero + pControl->pGUIControl = NULL; + pControl->iControlId = 0; + pControl->iParentId = 0; + ++it; + } + + if (!existingWindow) + { + if (window) + { + if (CServiceBroker::GetGUI()->GetWindowManager().IsWindowVisible(ref(window)->GetID())) + { + destroyAfterDeInit = true; + close(); + } + else + CServiceBroker::GetGUI()->GetWindowManager().Delete(ref(window)->GetID()); + } + } + + vecControls.clear(); + } + } + + void Window::setWindow(InterceptorBase* _window) + { + XBMC_TRACE; + window = _window; + iWindowId = _window->get()->GetID(); + + if (!existingWindow) + CServiceBroker::GetGUI()->GetWindowManager().Add(window->get()); + } + + int Window::getNextAvailableWindowId() + { + XBMC_TRACE; + // window id's 13000 - 13100 are reserved for python + // get first window id that is not in use + int id = WINDOW_PYTHON_START; + // if window 13099 is in use it means python can't create more windows + if (CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_PYTHON_END)) + throw WindowException("maximum number of windows reached"); + + while(id < WINDOW_PYTHON_END && CServiceBroker::GetGUI()->GetWindowManager().GetWindow(id) != NULL) id++; + return id; + } + + void Window::popActiveWindowId() + { + XBMC_TRACE; + if (iOldWindowId != iWindowId && + iWindowId != ACTIVE_WINDOW) + iOldWindowId = ACTIVE_WINDOW; + } + + // Internal helper method + /* Searches for a control in Window->vecControls + * If we can't find any but the window has the controlId (in case of a not python window) + * we create a new control with basic functionality + */ + Control* Window::GetControlById(int iControlId, CCriticalSection* gc) + { + XBMC_TRACE; + + // find in window vector first!!! + // this saves us from creating a complete new control + std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin(); + while (it != vecControls.end()) + { + AddonClass::Ref<Control> control = (*it); + if (control->iControlId == iControlId) + { + return control.get(); + } else ++it; + } + + // lock xbmc GUI before accessing data from it + MaybeLock lock(gc); + + // check if control exists + CGUIControl* pGUIControl = ref(window)->GetControl(iControlId); + if (!pGUIControl) + { + // control does not exist. + throw WindowException("Non-Existent Control %d",iControlId); + } + + // allocate a new control with a new reference + CLabelInfo li; + + Control* pControl = NULL; + + //! @todo Yuck! Should probably be done with a Factory pattern + switch(pGUIControl->GetControlType()) + { + case CGUIControl::GUICONTROL_BUTTON: + pControl = new ControlButton(); + + li = ((CGUIButtonControl *)pGUIControl)->GetLabelInfo(); + + // note: conversion from infocolors -> plain colors here + ((ControlButton*)pControl)->disabledColor = li.disabledColor; + ((ControlButton*)pControl)->focusedColor = li.focusedColor; + ((ControlButton*)pControl)->textColor = li.textColor; + ((ControlButton*)pControl)->shadowColor = li.shadowColor; + if (li.font) ((ControlButton*)pControl)->strFont = li.font->GetFontName(); + ((ControlButton*)pControl)->align = li.align; + break; + case CGUIControl::GUICONTROL_LABEL: + pControl = new ControlLabel(); + break; + case CGUIControl::GUICONTROL_SPIN: + pControl = new ControlSpin(); + break; + case CGUIControl::GUICONTROL_FADELABEL: + pControl = new ControlFadeLabel(); + break; + case CGUIControl::GUICONTROL_TEXTBOX: + pControl = new ControlTextBox(); + break; + case CGUIControl::GUICONTROL_IMAGE: + case CGUIControl::GUICONTROL_BORDEREDIMAGE: + pControl = new ControlImage(); + break; + case CGUIControl::GUICONTROL_PROGRESS: + pControl = new ControlProgress(); + break; + case CGUIControl::GUICONTROL_SLIDER: + pControl = new ControlSlider(); + break; + case CGUIControl::GUICONTAINER_LIST: + case CGUIControl::GUICONTAINER_WRAPLIST: + case CGUIControl::GUICONTAINER_FIXEDLIST: + case CGUIControl::GUICONTAINER_PANEL: + pControl = new ControlList(); + // create a python spin control + ((ControlList*)pControl)->pControlSpin = new ControlSpin(); + break; + case CGUIControl::GUICONTROL_GROUP: + pControl = new ControlGroup(); + break; + case CGUIControl::GUICONTROL_RADIO: + pControl = new ControlRadioButton(); + + li = ((CGUIRadioButtonControl *)pGUIControl)->GetLabelInfo(); + + // note: conversion from infocolors -> plain colors here + ((ControlRadioButton*)pControl)->disabledColor = li.disabledColor; + ((ControlRadioButton*)pControl)->focusedColor = li.focusedColor; + ((ControlRadioButton*)pControl)->textColor = li.textColor; + ((ControlRadioButton*)pControl)->shadowColor = li.shadowColor; + if (li.font) ((ControlRadioButton*)pControl)->strFont = li.font->GetFontName(); + ((ControlRadioButton*)pControl)->align = li.align; + break; + case CGUIControl::GUICONTROL_EDIT: + pControl = new ControlEdit(); + + li = ((CGUIEditControl *)pGUIControl)->GetLabelInfo(); + + // note: conversion from infocolors -> plain colors here + ((ControlEdit*)pControl)->disabledColor = li.disabledColor; + ((ControlEdit*)pControl)->textColor = li.textColor; + if (li.font) ((ControlEdit*)pControl)->strFont = li.font->GetFontName(); + ((ControlButton*)pControl)->align = li.align; + break; + default: + break; + } + + if (!pControl) + // throw an exception + throw WindowException("Unknown control type for python"); + + // we have a valid control here, fill in all the 'Control' data + pControl->pGUIControl = pGUIControl; + pControl->iControlId = pGUIControl->GetID(); + pControl->iParentId = iWindowId; + pControl->dwHeight = (int)pGUIControl->GetHeight(); + pControl->dwWidth = (int)pGUIControl->GetWidth(); + pControl->dwPosX = (int)pGUIControl->GetXPosition(); + pControl->dwPosY = (int)pGUIControl->GetYPosition(); + pControl->iControlUp = pGUIControl->GetAction(ACTION_MOVE_UP).GetNavigation(); + pControl->iControlDown = pGUIControl->GetAction(ACTION_MOVE_DOWN).GetNavigation(); + pControl->iControlLeft = pGUIControl->GetAction(ACTION_MOVE_LEFT).GetNavigation(); + pControl->iControlRight = pGUIControl->GetAction(ACTION_MOVE_RIGHT).GetNavigation(); + + // It got this far so means the control isn't actually in the vector of controls + // so lets add it to save doing all that next time + vecControls.emplace_back(pControl); + + // return the control with increased reference (+1) + return pControl; + } + + void Window::PulseActionEvent() + { + XBMC_TRACE; + if (canPulse) + m_actionEvent.Set(); + } + + bool Window::WaitForActionEvent(unsigned int milliseconds) + { + XBMC_TRACE; + // DO NOT MAKE THIS A DELAYED CALL!!!! + bool ret = languageHook == NULL ? m_actionEvent.Wait(std::chrono::milliseconds(milliseconds)) + : languageHook->WaitForEvent(m_actionEvent, milliseconds); + if (ret) + m_actionEvent.Reset(); + return ret; + } + + bool Window::OnAction(const CAction &action) + { + XBMC_TRACE; + // do the base class window first, and the call to python after this + bool ret = ref(window)->OnAction(action); + + // workaround - for scripts which try to access the active control (focused) when there is none. + // for example - the case when the mouse enters the screen. + CGUIControl *pControl = ref(window)->GetFocusedControl(); + if (action.IsMouse() && !pControl) + return ret; + + AddonClass::Ref<Action> inf(new Action(action)); + invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Action> >(this,&Window::onAction,inf.get())); + PulseActionEvent(); + + return ret; + } + + bool Window::OnBack(int actionID) + { + // we are always a Python window ... keep that in mind when reviewing the old code + return true; + } + + void Window::OnDeinitWindow(int nextWindowID /*= 0*/) + { + // NOTE!: This handle child classes correctly. XML windows will call + // the OnDeinitWindow from CGUIMediaWindow while non-XML classes will + // call the OnDeinitWindow on CGUIWindow + ref(window)->OnDeinitWindow(nextWindowID); + if (destroyAfterDeInit) + CServiceBroker::GetGUI()->GetWindowManager().Delete(window->get()->GetID()); + } + + void Window::onAction(Action* action) + { + XBMC_TRACE; + // default onAction behavior + if(action->id == ACTION_PREVIOUS_MENU || action->id == ACTION_NAV_BACK) + close(); + } + + bool Window::OnMessage(CGUIMessage& message) + { + XBMC_TRACE; + switch (message.GetMessage()) + { + case GUI_MSG_CLICKED: + { + int iControl=message.GetSenderId(); + AddonClass::Ref<Control> inf; + // find python control object with same iControl + std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin(); + while (it != vecControls.end()) + { + AddonClass::Ref<Control> pControl = (*it); + if (pControl->iControlId == iControl) + { + inf = pControl.get(); + break; + } + ++it; + } + + // did we find our control? + if (inf.isNotNull()) + { + // currently we only accept messages from a button or controllist with a select action + if (inf->canAcceptMessages(message.GetParam1())) + { + invokeCallback(new CallbackFunction<Window,AddonClass::Ref<Control> >(this,&Window::onControl,inf.get())); + PulseActionEvent(); + + // return true here as we are handling the event + return true; + } + } + // if we get here, we didn't add the action + } + break; + } + + return ref(window)->OnMessage(message); + } + + void Window::onControl(Control* action) { XBMC_TRACE; /* do nothing by default */ } + void Window::onClick(int controlId) { XBMC_TRACE; /* do nothing by default */ } + void Window::onDoubleClick(int controlId) { XBMC_TRACE; /* do nothing by default */ } + void Window::onFocus(int controlId) { XBMC_TRACE; /* do nothing by default */ } + void Window::onInit() { XBMC_TRACE; /* do nothing by default */ } + + void Window::show() + { + XBMC_TRACE; + DelayedCallGuard dcguard(languageHook); + popActiveWindowId(); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTIVATE_WINDOW, iWindowId, 0); + } + + void Window::setFocus(Control* pControl) + { + XBMC_TRACE; + if(pControl == NULL) + throw WindowException("Object should be of type Control"); + + CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,pControl->iParentId, pControl->iControlId); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, pControl->iParentId); + } + + void Window::setFocusId(int iControlId) + { + XBMC_TRACE; + CGUIMessage msg = CGUIMessage(GUI_MSG_SETFOCUS,iWindowId,iControlId); + CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(msg, iWindowId); + } + + Control* Window::getFocus() + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + + int iControlId = ref(window)->GetFocusedControlID(); + if(iControlId == -1) + throw WindowException("No control in this window has focus"); + // Sine I'm already holding the lock theres no reason to give it to GetFocusedControlID + return GetControlById(iControlId,NULL); + } + + long Window::getFocusId() + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + int iControlId = ref(window)->GetFocusedControlID(); + if(iControlId == -1) + throw WindowException("No control in this window has focus"); + return (long)iControlId; + } + + void Window::removeControl(Control* pControl) + { + XBMC_TRACE; + DelayedCallGuard dg(languageHook); + doRemoveControl(pControl,&CServiceBroker::GetWinSystem()->GetGfxContext(),true); + } + + void Window::doRemoveControl(Control* pControl, CCriticalSection* gcontext, bool wait) + { + XBMC_TRACE; + // type checking, object should be of type Control + if(pControl == NULL) + throw WindowException("Object should be of type Control"); + + { + MaybeLock mlock(gcontext); + if(!ref(window)->GetControl(pControl->iControlId)) + throw WindowException("Control does not exist in window"); + } + + // delete control from vecControls in window object + std::vector<AddonClass::Ref<Control> >::iterator it = vecControls.begin(); + while (it != vecControls.end()) + { + AddonClass::Ref<Control> control = (*it); + if (control->iControlId == pControl->iControlId) + { + it = vecControls.erase(it); + } else ++it; + } + + CGUIMessage msg(GUI_MSG_REMOVE_CONTROL, 0, 0); + msg.SetPointer(pControl->pGUIControl); + CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait); + + // initialize control to zero + pControl->pGUIControl = NULL; + pControl->iControlId = 0; + pControl->iParentId = 0; + } + + void Window::removeControls(std::vector<Control*> pControls) + { + XBMC_TRACE; + DelayedCallGuard dg(languageHook); + int count = 1; int size = pControls.size(); + for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, ++iter) + doRemoveControl(*iter,NULL, count == size); + } + + long Window::getHeight() + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook); + RESOLUTION_INFO resInfo = ref(window)->GetCoordsRes(); + return resInfo.iHeight; + } + + long Window::getWidth() + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(), languageHook); + RESOLUTION_INFO resInfo = ref(window)->GetCoordsRes(); + return resInfo.iWidth; + } + + void Window::setProperty(const char* key, const String& value) + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + + ref(window)->SetProperty(lowerKey, value); + } + + String Window::getProperty(const char* key) + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + std::string value = ref(window)->GetProperty(lowerKey).asString(); + return value; + } + + void Window::clearProperty(const char* key) + { + XBMC_TRACE; + if (!key) return; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + + std::string lowerKey = key; + StringUtils::ToLower(lowerKey); + ref(window)->SetProperty(lowerKey, ""); + } + + void Window::clearProperties() + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + ref(window)->ClearProperties(); + } + + void Window::close() + { + XBMC_TRACE; + bModal = false; + + if (!existingWindow) + PulseActionEvent(); + + { + DelayedCallGuard dcguard(languageHook); + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_PREVIOUS_WINDOW, iOldWindowId, 0); + } + + iOldWindowId = 0; + } + + void Window::doModal() + { + XBMC_TRACE; + if (!existingWindow) + { + bModal = true; + + if(iWindowId != ACTIVE_WINDOW) + show(); + + while (bModal && !g_application.m_bStop) + { +//! @todo garbear added this code to the python window.cpp class and +//! commented in XBPyThread.cpp. I'm not sure how to handle this +//! in this native implementation. +// // Check if XBPyThread::stop() raised a SystemExit exception +// if (PyThreadState_Get()->async_exc == PyExc_SystemExit) +// { +// CLog::Log(LOGDEBUG, "PYTHON: doModal() encountered a SystemExit exception, closing window and returning"); +// Window_Close(self, NULL); +// break; +// } + languageHook->MakePendingCalls(); // MakePendingCalls + + bool stillWaiting; + do + { + { + DelayedCallGuard dcguard(languageHook); + stillWaiting = WaitForActionEvent(100) ? false : true; + } + languageHook->MakePendingCalls(); + } while (stillWaiting); + } + } + } + + void Window::addControl(Control* pControl) + { + XBMC_TRACE; + DelayedCallGuard dg(languageHook); + doAddControl(pControl,&CServiceBroker::GetWinSystem()->GetGfxContext(),true); + } + + void Window::doAddControl(Control* pControl, CCriticalSection* gcontext, bool wait) + { + XBMC_TRACE; + if(pControl == NULL) + throw WindowException("NULL Control passed to WindowBase::addControl"); + + if(pControl->iControlId != 0) + throw WindowException("Control is already used"); + + // lock kodi GUI before accessing data from it + pControl->iParentId = iWindowId; + + { + MaybeLock mlock(gcontext); + // assign control id, if id is already in use, try next id + do pControl->iControlId = ++iCurrentControlId; + while (ref(window)->GetControl(pControl->iControlId)); + } + + pControl->Create(); + + // set default navigation for control + pControl->iControlUp = pControl->iControlId; + pControl->iControlDown = pControl->iControlId; + pControl->iControlLeft = pControl->iControlId; + pControl->iControlRight = pControl->iControlId; + + pControl->pGUIControl->SetAction(ACTION_MOVE_UP, CGUIAction(pControl->iControlUp)); + pControl->pGUIControl->SetAction(ACTION_MOVE_DOWN, CGUIAction(pControl->iControlDown)); + pControl->pGUIControl->SetAction(ACTION_MOVE_LEFT, CGUIAction(pControl->iControlLeft)); + pControl->pGUIControl->SetAction(ACTION_MOVE_RIGHT, CGUIAction(pControl->iControlRight)); + + // add control to list and allocate resources for the control + vecControls.emplace_back(pControl); + pControl->pGUIControl->AllocResources(); + + // This calls the CGUIWindow parent class to do the final add + CGUIMessage msg(GUI_MSG_ADD_CONTROL, 0, 0); + msg.SetPointer(pControl->pGUIControl); + CServiceBroker::GetAppMessenger()->SendGUIMessage(msg, iWindowId, wait); + } + + void Window::addControls(std::vector<Control*> pControls) + { + XBMC_TRACE; + SingleLockWithDelayGuard gslock(CServiceBroker::GetWinSystem()->GetGfxContext(),languageHook); + int count = 1; int size = pControls.size(); + for (std::vector<Control*>::iterator iter = pControls.begin(); iter != pControls.end(); count++, ++iter) + doAddControl(*iter,NULL, count == size); + } + + Control* Window::getControl(int iControlId) + { + XBMC_TRACE; + DelayedCallGuard dg(languageHook); + return GetControlById(iControlId,&CServiceBroker::GetWinSystem()->GetGfxContext()); + } + + void Action::setFromCAction(const CAction& action) + { + XBMC_TRACE; + id = action.GetID(); + buttonCode = action.GetButtonCode(); + fAmount1 = action.GetAmount(0); + fAmount2 = action.GetAmount(1); + fRepeat = action.GetRepeat(); + strAction = action.GetName(); + } + + } +} diff --git a/xbmc/interfaces/legacy/Window.h b/xbmc/interfaces/legacy/Window.h new file mode 100644 index 0000000..b30be7b --- /dev/null +++ b/xbmc/interfaces/legacy/Window.h @@ -0,0 +1,878 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "AddonCallback.h" +#include "AddonString.h" +#include "Control.h" +#include "swighelper.h" + +#include <limits.h> +#include <mutex> +#include <vector> + +namespace XBMCAddon +{ + namespace xbmcgui + { + // Forward declare the interceptor as the AddonWindowInterceptor.h + // file needs to include the Window class because of the template + class InterceptorBase; + + // + /// \defgroup python_xbmcgui_action Action + /// \ingroup python_xbmcgui + ///@{ + /// @brief **Action class.** + /// + /// \python_class{ xbmcgui.Action(): } + /// + /// This class serves in addition to identify carried out + /// \ref kodi_key_action_ids of Kodi and to be able to carry out thereby own + /// necessary steps. + /// + /// The data of this class are always transmitted by callback + /// Window::onAction at a window. + /// + class Action : public AddonClass + { + public: + Action() = default; + +#ifndef SWIG + explicit Action(const CAction& caction) { setFromCAction(caction); } + + void setFromCAction(const CAction& caction); + + long id = -1; + float fAmount1 = 0.0f; + float fAmount2 = 0.0f; + float fRepeat = 0.0f; + unsigned long buttonCode = 0; + std::string strAction; + + // Not sure if this is correct but it's here for now. + AddonClass::Ref<Control> control; // previously pObject +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_action + /// @brief \python_func{ getId() } + /// To get \ref kodi_key_action_ids + /// + /// This function returns the identification code used by the explained + /// order, it is necessary to determine the type of command from + /// \ref kodi_key_action_ids. + /// + /// @return The action's current id as a long or 0 if + /// no action is mapped in the xml's. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// def onAction(self, action): + /// if action.getId() == ACTION_PREVIOUS_MENU: + /// print('action received: previous') + /// .. + /// ~~~~~~~~~~~~~ + /// + getId(); +#else + long getId() { XBMC_TRACE; return id; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_action + /// @brief \python_func{ getButtonCode() } + /// Returns the button code for this action. + /// + /// @return [integer] button code + /// + getButtonCode(); +#else + long getButtonCode() { XBMC_TRACE; return buttonCode; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_action + /// @brief \python_func{ getAmount1() } + /// Returns the first amount of force applied to the thumbstick. + /// + /// @return [float] first amount + /// + getAmount1(); +#else + float getAmount1() { XBMC_TRACE; return fAmount1; } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_action + /// @brief \python_func{ getAmount2() } + /// Returns the second amount of force applied to the thumbstick. + /// + /// @return [float] second amount + /// + getAmount2(); +#else + float getAmount2() { XBMC_TRACE; return fAmount2; } +#endif + }; + ///@} + + //========================================================================== + // This is the main class for the xbmcgui.Window functionality. It is tied + // into the main Kodi windowing system via the Interceptor + //========================================================================== + // + /// \defgroup python_xbmcgui_window Window + /// \ingroup python_xbmcgui + /// @{ + /// @brief __GUI window class for Add-Ons.__ + /// + /// This class allows over their functions to create and edit windows that + /// can be accessed from an Add-On. + /// + /// Likewise, all functions from here as well in the other window classes + /// \ref python_xbmcgui_window_dialog "WindowDialog", + /// \ref python_xbmcgui_window_xml "WindowXML" and + /// \ref python_xbmcgui_window_dialog_xml "WindowXMLDialog" + /// with inserted and available. + /// + /// + ///-------------------------------------------------------------------------- + /// Constructor for window + /// ---------------------------- + /// + /// \python_class{ xbmcgui.Window([existingWindowId]): } + /// + /// Creates a new from Add-On usable window class. This is to create + /// window for related controls by system calls. + /// + /// @param existingWindowId [opt] Specify an id to use an existing + /// window. + /// @throws ValueError if supplied window Id does not exist. + /// @throws Exception if more then 200 windows are created. + /// + /// Deleting this window will activate the old window that was active + /// and resets (not delete) all controls that are associated with this + /// window. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.Window() + /// width = win.getWidth() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// + // + class Window : public AddonCallback + { + friend class WindowDialogMixin; + bool isDisposed = false; + + void doAddControl(Control* pControl, CCriticalSection* gcontext, bool wait); + void doRemoveControl(Control* pControl, CCriticalSection* gcontext, bool wait); + + protected: +#ifndef SWIG + InterceptorBase* window; + int iWindowId = -1; + + std::vector<AddonClass::Ref<Control> > vecControls; + int iOldWindowId = 0; + int iCurrentControlId = 3000; + bool bModal = false; + CEvent m_actionEvent; + + bool canPulse = false; + + // I REALLY hate this ... but it's the simplest fix right now. + bool existingWindow = true; + bool destroyAfterDeInit = false; + + /** + * This only takes a boolean to allow subclasses to explicitly use it. A + * default constructor can be used as a concrete class and we need to tell + * the difference. + * subclasses should use this constructor and not the other. + */ + explicit Window(bool discrim); + + void deallocating() override; + + /** + * This helper retrieves the next available id. It is assumed that the + * global lock is already being held. + */ + static int getNextAvailableWindowId(); + + /** + * Child classes MUST call this in their constructors. It should be an + * instance of Interceptor<P extends CGUIWindow>. Control of memory + * management for this class is then given to the Window. + */ + void setWindow(InterceptorBase* _window); + + /** + * This is a helper method since popping the previous window id is a common + * function. + */ + void popActiveWindowId(); + + /** + * This is a helper method since getting a control by it's id is a common + * function. + */ + Control* GetControlById(int iControlId, CCriticalSection* gc); + + SWIGHIDDENVIRTUAL void PulseActionEvent(); + SWIGHIDDENVIRTUAL bool WaitForActionEvent(unsigned int milliseconds); +#endif + + public: + explicit Window(int existingWindowId = -1); + + ~Window() override; + +#ifndef SWIG + SWIGHIDDENVIRTUAL bool OnMessage(CGUIMessage& message); + SWIGHIDDENVIRTUAL bool OnAction(const CAction &action); + SWIGHIDDENVIRTUAL bool OnBack(int actionId); + SWIGHIDDENVIRTUAL void OnDeinitWindow(int nextWindowID); + + SWIGHIDDENVIRTUAL bool IsDialogRunning() const + { + XBMC_TRACE; + return false; + } + SWIGHIDDENVIRTUAL bool IsDialog() const + { + XBMC_TRACE; + return false; + } + SWIGHIDDENVIRTUAL bool IsModalDialog() const + { + XBMC_TRACE; + return false; + } + SWIGHIDDENVIRTUAL bool IsMediaWindow() const + { + XBMC_TRACE; + return false; + } + SWIGHIDDENVIRTUAL void dispose(); + + /** + * This is called from the InterceptorBase destructor to prevent further + * use of the interceptor from the window. + */ + inline void interceptorClear() + { + std::unique_lock<CCriticalSection> lock(*this); + window = NULL; + } +#endif + + // + /// @defgroup python_xbmcgui_window_cb Callback functions from Kodi to add-on + /// \ingroup python_xbmcgui_window + /// @{ + /// @brief __GUI window callback functions.__ + /// + /// Functions to handle control callbacks from Kodi. + /// + /// Likewise, all functions from here as well in the all window classes + /// (\ref python_xbmcgui_window "Window", + /// \ref python_xbmcgui_window_dialog "WindowDialog", + /// \ref python_xbmcgui_window_xml "WindowXML" and + /// \ref python_xbmcgui_window_dialog_xml "WindowXMLDialog") with inserted + /// and available. + /// + /// ------------------------------------------------------------------------ + /// + /// @link python_xbmcgui_window Go back to normal functions from window@endlink + // + + // callback takes a parameter +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onAction(self, Action action) } + /// **onAction method.** + /// + /// This method will receive all actions that the main program will send + /// to this window. + /// + /// @param self Own base class pointer + /// @param action The action id to perform, see + /// \ref python_xbmcgui_action to get use + /// of them + /// + /// @note + /// - By default, only the `PREVIOUS_MENU` and `NAV_BACK actions` are + /// handled. + /// - Overwrite this method to let your script handle all actions. + /// - Don't forget to capture `ACTION_PREVIOUS_MENU` or `ACTION_NAV_BACK`, + /// else the user can't close this window. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onAction(self, action): + /// if action.getId() == ACTION_PREVIOUS_MENU: + /// print('action received: previous') + /// self.close() + /// if action.getId() == ACTION_SHOW_INFO: + /// print('action received: show info') + /// if action.getId() == ACTION_STOP: + /// print('action received: stop') + /// if action.getId() == ACTION_PAUSE: + /// print('action received: pause') + /// .. + /// ~~~~~~~~~~~~~ + /// + onAction(...); +#else + virtual void onAction(Action* action); +#endif + + // on control is not actually on Window in the api but is called into Python anyway. +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onControl(self, Control) } + /// **onControl method.** + /// + /// This method will receive all click events on owned and selected + /// controls when the control itself doesn't handle the message. + /// + /// @param self Own base class pointer + /// @param control The \ref python_xbmcgui_control "Control" class + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onControl(self, control): + /// print("Window.onControl(control=[%s])"%control) + /// .. + /// ~~~~~~~~~~~~~ + /// + void onControl(...); +#else + virtual void onControl(Control* control); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onClick(self, int controlId) } + /// **onClick method.** + /// + /// This method will receive all click events that the main program will + /// send to this window. + /// + /// @param self Own base class pointer + /// @param controlId The one time clicked GUI control + /// identifier + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onClick(self,controlId): + /// if controlId == 10: + /// print("The control with Id 10 is clicked") + /// .. + /// ~~~~~~~~~~~~~ + /// + onClick(...); +#else + virtual void onClick(int controlId); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onDoubleClick(self, int controlId) } + /// __onDoubleClick method.__ + /// + /// This method will receive all double click events that the main program + /// will send to this window. + /// + /// @param self Own base class pointer + /// @param controlId The double clicked GUI control + /// identifier + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onDoubleClick(self,controlId): + /// if controlId == 10: + /// print("The control with Id 10 is double clicked") + /// .. + /// ~~~~~~~~~~~~~ + /// + onDoubleClick(...); +#else + virtual void onDoubleClick(int controlId); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onFocus(self, int controlId) } + /// __onFocus method.__ + /// + /// This method will receive all focus events that the main program will + /// send to this window. + /// + /// @param self Own base class pointer + /// @param controlId The focused GUI control identifier + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onDoubleClick(self,controlId): + /// if controlId == 10: + /// print("The control with Id 10 is focused") + /// .. + /// ~~~~~~~~~~~~~ + /// + onFocus(...); +#else + virtual void onFocus(int controlId); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_cb + /// @brief \python_func{ onInit(self) } + /// __onInit method.__ + /// + /// This method will be called to initialize the window + /// + /// @param self Own base class pointer + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// # Define own function where becomes called from Kodi + /// def onInit(self): + /// print("Window.onInit method called from Kodi") + /// .. + /// ~~~~~~~~~~~~~ + /// + onInit(...); +#else + virtual void onInit(); +#endif + //@} + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ show() } + /// Show this window. + /// + /// Shows this window by activating it, calling close() after it wil + /// activate the current window again. + /// + /// @note If your script ends this window will be closed to. To show it + /// forever, make a loop at the end of your script or use doModal() + /// instead. + /// + show(); +#else + SWIGHIDDENVIRTUAL void show(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ setFocus(Control) } + /// Give the supplied control focus. + /// + /// @param Control \ref python_xbmcgui_control "Control" class to focus + /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control "Control" + /// type + /// @throws SystemError On Internal error + /// @throws RuntimeError If control is not added to a window + /// + setFocus(...); +#else + SWIGHIDDENVIRTUAL void setFocus(Control* pControl); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ setFocusId(ControlId) } + /// Gives the control with the supplied focus. + /// + /// @param ControlId [integer] On skin defined id of control + /// @throws SystemError On Internal error + /// @throws RuntimeError If control is not added to a window + /// + setFocusId(...); +#else + SWIGHIDDENVIRTUAL void setFocusId(int iControlId); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getFocus(Control) } + /// Returns the control which is focused. + /// + /// @return Focused control class + /// @throws SystemError On Internal error + /// @throws RuntimeError If no control has focus + /// + getFocus(); +#else + SWIGHIDDENVIRTUAL Control* getFocus(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getFocusId(int) } + /// Returns the id of the control which is focused. + /// + /// @return Focused control id + /// @throws SystemError On Internal error + /// @throws RuntimeError If no control has focus + /// + getFocusId(); +#else + SWIGHIDDENVIRTUAL long getFocusId(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ removeControl(Control) } + /// Removes the control from this window. + /// + /// @param Control \ref python_xbmcgui_control "Control" class to remove + /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control + /// type + /// @throws RuntimeError If control is not added to this window + /// + /// This will not delete the control. It is only removed from the window. + /// + removeControl(...); +#else + SWIGHIDDENVIRTUAL void removeControl(Control* pControl); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ removeControls(List) } + /// Removes a list of controls from this window. + /// + /// @param List List with controls to remove + /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control + /// type + /// @throws RuntimeError If control is not added to this window + /// + /// This will not delete the controls. They are only removed from the + /// window. + /// + removeControls(...); +#else + SWIGHIDDENVIRTUAL void removeControls(std::vector<Control*> pControls); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getHeight() } + /// Returns the height of this Window instance. + /// + /// @return Window height in pixels + /// + ///----------------------------------------------------------------------- + /// @python_v18 Function changed + /// + getHeight(); +#else + SWIGHIDDENVIRTUAL long getHeight(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getWidth() } + /// Returns the width of this Window instance. + /// + /// @return Window width in pixels + /// + ///----------------------------------------------------------------------- + /// @python_v18 Function changed + /// + getWidth(); +#else + SWIGHIDDENVIRTUAL long getWidth(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ setProperty(key, value) } + /// Sets a window property, similar to an infolabel. + /// + /// @param key string - property name. + /// @param value string or unicode - value of property. + /// + /// @note Key is NOT case sensitive. Setting value to an empty string is + /// equivalent to clearProperty(key).\n + /// You can use the above as keywords for arguments and skip + /// certain optional arguments.\n + /// Once you use a keyword, all following arguments require the + /// keyword. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId()) + /// win.setProperty('Category', 'Newest') + /// .. + /// ~~~~~~~~~~~~~ + /// + setProperty(...); +#else + SWIGHIDDENVIRTUAL void setProperty(const char* key, const String& value); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getProperty(key) } + /// Returns a window property as a string, similar to an infolabel. + /// + /// @param key string - property name. + /// + /// @note Key is NOT case sensitive.\n + /// You can use the above as keywords for arguments and skip + /// certain optional arguments. + /// Once you use a keyword, all following arguments require the + /// keyword. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId()) + /// category = win.getProperty('Category') + /// .. + /// ~~~~~~~~~~~~~ + /// + getProperty(...); +#else + SWIGHIDDENVIRTUAL String getProperty(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ clearProperty(key) } + /// Clears the specific window property. + /// + /// @param key string - property name. + /// + /// @note Key is NOT case sensitive. Equivalent to setProperty(key,'') + /// You can use the above as keywords for arguments and skip certain + /// optional arguments. + /// Once you use a keyword, all following arguments require the + /// keyword. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId()) + /// win.clearProperty('Category') + /// .. + /// ~~~~~~~~~~~~~ + /// + clearProperty(...); +#else + SWIGHIDDENVIRTUAL void clearProperty(const char* key); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ clearProperties() } + /// Clears all window properties. + /// + /// + ///----------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.Window(xbmcgui.getCurrentWindowId()) + /// win.clearProperties() + /// .. + /// ~~~~~~~~~~~~~ + /// + clearProperties(); +#else + SWIGHIDDENVIRTUAL void clearProperties(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ close() } + /// Closes this window. + /// + /// Closes this window by activating the old window. + /// + /// @note The window is not deleted with this method. + /// + close(); +#else + SWIGHIDDENVIRTUAL void close(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ doModal() } + /// Display this window until close() is called. + /// + doModal(); +#else + + SWIGHIDDENVIRTUAL void doModal(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ addControl(Control) } + /// Add a \ref python_xbmcgui_control "Control" to this window. + /// + /// @param Control \ref python_xbmcgui_control "Control" to add + /// @throws TypeError If supplied argument is not a \ref python_xbmcgui_control + /// type + /// @throws ReferenceError If control is already used in another + /// window + /// @throws RuntimeError Should not happen :-) + /// + /// The next controls can be added to a window atm + /// | Control-class | Description | + /// |---------------------|------------------------------------------------------------| + /// | \ref python_xbmcgui_control_label "ControlLabel" | Label control to show text + /// | \ref python_xbmcgui_control_fadelabel "ControlFadeLabel" | The fadelabel has multiple labels which it cycles through + /// | \ref python_xbmcgui_control_textbox "ControlTextBox" | To show bigger text field + /// | \ref python_xbmcgui_control_button "ControlButton" | Brings a button to do some actions + /// | \ref python_xbmcgui_control_edit "ControlEdit" | The edit control allows a user to input text in Kodi + /// | \ref python_xbmcgui_control_fadelabel "ControlFadeLabel" | The fade label control is used for displaying multiple pieces of text in the same space in Kodi + /// | \ref python_xbmcgui_control_list "ControlList" | Add a list for something like files + /// | \ref python_xbmcgui_control_group "ControlGroup" | Is for a group which brings the others together + /// | \ref python_xbmcgui_control_image "ControlImage" | Controls a image on skin + /// | \ref python_xbmcgui_control_radiobutton "ControlRadioButton" | For a radio button which handle boolean values + /// | \ref python_xbmcgui_control_progress "ControlProgress" | Progress bar for a performed work or something else + /// | \ref python_xbmcgui_control_slider "ControlSlider" | The slider control is used for things where a sliding bar best represents the operation at hand + /// | \ref python_xbmcgui_control_spin "ControlSpin" | The spin control is used for when a list of options can be chosen + /// | \ref python_xbmcgui_control_textbox "ControlTextBox" | The text box is used for showing a large multipage piece of text in Kodi + /// + addControl(...); +#else + SWIGHIDDENVIRTUAL void addControl(Control* pControl); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ addControls(List) } + /// Add a list of Controls to this window. + /// + /// @param List List with controls to add + /// @throws TypeError If supplied argument is not of List + /// type, or a control is not of \ref python_xbmcgui_control + /// type + /// @throws ReferenceError If control is already used in another + /// window + /// @throws RuntimeError Should not happen :-) + /// + addControls(...); +#else + SWIGHIDDENVIRTUAL void addControls(std::vector<Control*> pControls); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window + /// @brief \python_func{ getControl(controlId) } + /// Gets the control from this window. + /// + /// @param controlId \ref python_xbmcgui_control id to get + /// @throws Exception If \ref python_xbmcgui_control doesn't exist + /// + /// @remark controlId doesn't have to be a python control, it can be a + /// control id from a Kodi window too (you can find id's in the xml files. + /// + /// @note Not python controls are not completely usable yet + /// You can only use the \ref python_xbmcgui_control functions + /// + getControl(...); +#else + SWIGHIDDENVIRTUAL Control* getControl(int iControlId); +#endif + }; + ///@} + } +} diff --git a/xbmc/interfaces/legacy/WindowDialog.cpp b/xbmc/interfaces/legacy/WindowDialog.cpp new file mode 100644 index 0000000..13ea4fd --- /dev/null +++ b/xbmc/interfaces/legacy/WindowDialog.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WindowDialog.h" + +#include "ServiceBroker.h" +#include "WindowInterceptor.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindow.h" +#include "guilib/GUIWindowManager.h" + +#include <mutex> + +namespace XBMCAddon +{ + namespace xbmcgui + { + WindowDialog::WindowDialog() : + Window(true), WindowDialogMixin(this) + { + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + InterceptorBase* interceptor = new Interceptor<CGUIWindow>("CGUIWindow", this, getNextAvailableWindowId()); + // set the render order to the dialog's default because this dialog is mapped to CGUIWindow instead of CGUIDialog + interceptor->SetRenderOrder(RENDER_ORDER_DIALOG); + setWindow(interceptor); + } + + WindowDialog::~WindowDialog() { deallocating(); } + + bool WindowDialog::OnMessage(CGUIMessage& message) + { +#ifdef ENABLE_XBMC_TRACE_API + XBMC_TRACE; + CLog::Log(LOGDEBUG, "{}NEWADDON WindowDialog::OnMessage Message {}", _tg.getSpaces(), + message.GetMessage()); +#endif + + switch(message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: + { + ref(window)->OnMessage(message); + return true; + } + break; + + case GUI_MSG_CLICKED: + { + return Window::OnMessage(message); + } + break; + } + + // we do not message CGUIPythonWindow here.. + return ref(window)->OnMessage(message); + } + + bool WindowDialog::OnAction(const CAction &action) + { + XBMC_TRACE; + return WindowDialogMixin::OnAction(action) ? true : Window::OnAction(action); + } + + void WindowDialog::OnDeinitWindow(int nextWindowID) + { + CServiceBroker::GetGUI()->GetWindowManager().RemoveDialog(iWindowId); + Window::OnDeinitWindow(nextWindowID); + } + + } +} diff --git a/xbmc/interfaces/legacy/WindowDialog.h b/xbmc/interfaces/legacy/WindowDialog.h new file mode 100644 index 0000000..0d44c86 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowDialog.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Window.h" +#include "WindowDialogMixin.h" + +namespace XBMCAddon +{ + namespace xbmcgui + { + // + /// \defgroup python_xbmcgui_window_dialog Subclass - WindowDialog + /// \ingroup python_xbmcgui_window + /// @{ + /// @brief __GUI window dialog class for Add-Ons.__ + /// + /// \python_class{ xbmcgui.WindowDialog(int windowId): } + /// + /// Creates a new window from Add-On usable dialog class. This is to create + /// window for related controls by system calls. + /// + /// @param windowId [opt] Specify an id to use an existing + /// window. + /// @throws ValueError if supplied window Id does not exist. + /// @throws Exception if more then 200 windows are created. + /// + /// Deleting this window will activate the old window that was active + /// and resets (not delete) all controls that are associated with this + /// window. + /// + /// + ///-------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.WindowDialog() + /// width = dialog.getWidth() + /// .. + /// ~~~~~~~~~~~~~ + /// + /// + // + class WindowDialog : public Window, private WindowDialogMixin + { + public: + WindowDialog(); + ~WindowDialog() override; + +#ifndef SWIG + bool OnMessage(CGUIMessage& message) override; + bool OnAction(const CAction& action) override; + void OnDeinitWindow(int nextWindowID) override; + + bool IsDialogRunning() const override { return WindowDialogMixin::IsDialogRunning(); } + bool IsModalDialog() const override + { + XBMC_TRACE; + return true; + }; + bool IsDialog() const override + { + XBMC_TRACE; + return true; + }; + + inline void show() override + { + XBMC_TRACE; + WindowDialogMixin::show(); + } + inline void close() override + { + XBMC_TRACE; + WindowDialogMixin::close(); + } +#endif + }; + ///@} + } +} diff --git a/xbmc/interfaces/legacy/WindowDialogMixin.cpp b/xbmc/interfaces/legacy/WindowDialogMixin.cpp new file mode 100644 index 0000000..32d6fa9 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowDialogMixin.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WindowDialogMixin.h" + +#include "ServiceBroker.h" +#include "WindowInterceptor.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "messaging/ApplicationMessenger.h" + +namespace XBMCAddon +{ + namespace xbmcgui + { + void WindowDialogMixin::show() + { + XBMC_TRACE; + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_PYTHON_DIALOG, HACK_CUSTOM_ACTION_OPENING, + 0, static_cast<void*>(w->window->get())); + } + + void WindowDialogMixin::close() + { + XBMC_TRACE; + w->bModal = false; + w->PulseActionEvent(); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_PYTHON_DIALOG, HACK_CUSTOM_ACTION_CLOSING, + 0, static_cast<void*>(w->window->get())); + + w->iOldWindowId = 0; + } + + bool WindowDialogMixin::IsDialogRunning() const { XBMC_TRACE; return w->window->isActive(); } + + bool WindowDialogMixin::OnAction(const CAction &action) + { + XBMC_TRACE; + switch (action.GetID()) + { + case HACK_CUSTOM_ACTION_OPENING: + { + // This is from the CGUIPythonWindowXMLDialog::Show_Internal + CServiceBroker::GetGUI()->GetWindowManager().RegisterDialog(w->window->get()); + // active this dialog... + CGUIMessage msg(GUI_MSG_WINDOW_INIT,0,0); + w->OnMessage(msg); + w->window->setActive(true); + //! @todo Figure out how to clean up the CAction + return true; + } + break; + + case HACK_CUSTOM_ACTION_CLOSING: + { + // This is from the CGUIPythonWindowXMLDialog::Show_Internal + w->window->get()->Close(); + return true; + } + break; + } + + return false; + } + } +} + + + diff --git a/xbmc/interfaces/legacy/WindowDialogMixin.h b/xbmc/interfaces/legacy/WindowDialogMixin.h new file mode 100644 index 0000000..5057e81 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowDialogMixin.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Window.h" + +// These messages are a side effect of the way dialogs work through the +// main ApplicationMessenger. At some point it would be nice to remove +// the messenger and have direct (or even drive) communications. +#define HACK_CUSTOM_ACTION_CLOSING -3 +#define HACK_CUSTOM_ACTION_OPENING -4 + +namespace XBMCAddon +{ + namespace xbmcgui + { + class WindowDialogMixin + { + private: + Window* w; + + protected: + inline explicit WindowDialogMixin(Window* window) : w(window) {} + + public: + virtual ~WindowDialogMixin() = default; + + SWIGHIDDENVIRTUAL void show(); + SWIGHIDDENVIRTUAL void close(); + +#ifndef SWIG + SWIGHIDDENVIRTUAL bool IsDialogRunning() const; + SWIGHIDDENVIRTUAL bool OnAction(const CAction &action); +#endif + }; + } +} diff --git a/xbmc/interfaces/legacy/WindowException.h b/xbmc/interfaces/legacy/WindowException.h new file mode 100644 index 0000000..ae35b57 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowException.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Exception.h" + +namespace XBMCAddon +{ + namespace xbmcgui + { + XBMCCOMMONS_STANDARD_EXCEPTION(WindowException); + } +} + + diff --git a/xbmc/interfaces/legacy/WindowInterceptor.h b/xbmc/interfaces/legacy/WindowInterceptor.h new file mode 100644 index 0000000..4890150 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowInterceptor.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Window.h" +#include "guilib/GUIWindow.h" + +namespace XBMCAddon +{ + namespace xbmcgui + { + +#ifndef SWIG + class Window; + class ref; + + /** + * These two classes are closely associated with the Interceptor template below. + * For more detailed explanation, see that class. + */ + class InterceptorBase + { + protected: + AddonClass::Ref<Window> window; + // This instance is in Window.cpp + static thread_local ref* upcallTls; + + InterceptorBase() : window(NULL) { upcallTls = NULL; } + + /** + * Calling up ONCE resets the upcall to to false. The reason is that when + * a call is recursive we cannot assume the ref has cleared the flag. + * so ... + * + * ref(window)->UpCall() + * + * during the context of 'UpCall' it's possible that another call will + * be made back on the window from the xbmc core side (this happens in + * sometimes in OnMessage). In that case, if upcall is still 'true', than + * the call will wrongly proceed back to the xbmc core side rather than + * to the Addon API side. + */ + static bool up() { bool ret = ((upcallTls) != NULL); upcallTls = NULL; return ret; } + public: + + virtual ~InterceptorBase() { if (window.isNotNull()) { window->interceptorClear(); } } + + virtual CGUIWindow* get() = 0; + + virtual void SetRenderOrder(int renderOrder) { } + + virtual void setActive(bool active) { } + virtual bool isActive() { return false; } + + friend class ref; + }; + + /** + * Guard class. But instead of managing memory or thread resources, + * any call made using the operator-> results in an 'upcall.' That is, + * it expects the call about to be made to have come from the XBMCAddon + * xbmcgui Window API and not from either the scripting language or the + * XBMC core Windowing system. + * + * This class is meant to hold references to instances of Interceptor<P>. + * see that template definition below. + */ + class ref + { + InterceptorBase* w; + public: + inline explicit ref(InterceptorBase* b) : w(b) { w->upcallTls = this; } + inline ~ref() { w->upcallTls = NULL; } + inline CGUIWindow* operator->() { return w->get(); } + inline CGUIWindow* get() { return w->get(); } + }; + + /** + * The intention of this class is to intercept calls from + * multiple points in the CGUIWindow class hierarchy and pass + * those calls over to the XBMCAddon API Window hierarchy. It + * is a class template that uses the type template parameter + * to determine the parent class. + * + * Since this class also maintains two way communication between + * the XBMCAddon API Window hierarchy and the core XBMC CGUIWindow + * hierarchy, it is used as a general bridge. For calls going + * TO the window hierarchy (as in callbacks) it operates in a + * straightforward manner. In the reverse direction (from the + * XBMCAddon hierarchy) it uses some hackery in a "smart pointer" + * overloaded -> operator. + */ + +#define checkedb(methcall) ( window.isNotNull() ? window-> methcall : false ) +#define checkedv(methcall) { if (window.isNotNull()) window-> methcall ; } + + template <class P /* extends CGUIWindow*/> class Interceptor : + public P, public InterceptorBase + { + std::string classname; + protected: + CGUIWindow* get() override { return this; } + + public: + Interceptor(const char* specializedName, + Window* _window, int windowid) : P(windowid, ""), + classname("Interceptor<" + std::string(specializedName) + ">") + { +#ifdef ENABLE_XBMC_TRACE_API + XBMCAddonUtils::TraceGuard tg; + CLog::Log(LOGDEBUG, "{}NEWADDON constructing {} 0x{:x}", tg.getSpaces(), classname, + (long)(((void*)this))); +#endif + window.reset(_window); + P::SetLoadType(CGUIWindow::LOAD_ON_GUI_INIT); + } + + Interceptor(const char* specializedName, + Window* _window, int windowid, + const char* xmlfile) : P(windowid, xmlfile), + classname("Interceptor<" + std::string(specializedName) + ">") + { +#ifdef ENABLE_XBMC_TRACE_API + XBMCAddonUtils::TraceGuard tg; + CLog::Log(LOGDEBUG, "{}NEWADDON constructing {} 0x{:x}", tg.getSpaces(), classname, + (long)(((void*)this))); +#endif + window.reset(_window); + P::SetLoadType(CGUIWindow::LOAD_ON_GUI_INIT); + } + +#ifdef ENABLE_XBMC_TRACE_API + ~Interceptor() override + { + XBMCAddonUtils::TraceGuard tg; + CLog::Log(LOGDEBUG, "{}NEWADDON LIFECYCLE destroying {} 0x{:x}", tg.getSpaces(), classname, + (long)(((void*)this))); + } +#else + ~Interceptor() override = default; +#endif + + bool OnMessage(CGUIMessage& message) override + { XBMC_TRACE; return up() ? P::OnMessage(message) : checkedb(OnMessage(message)); } + bool OnAction(const CAction &action) override + { XBMC_TRACE; return up() ? P::OnAction(action) : checkedb(OnAction(action)); } + + // NOTE!!: This ALWAYS skips up to the CGUIWindow instance. + bool OnBack(int actionId) override + { XBMC_TRACE; return up() ? CGUIWindow::OnBack(actionId) : checkedb(OnBack(actionId)); } + + void OnDeinitWindow(int nextWindowID) override + { XBMC_TRACE; if(up()) P::OnDeinitWindow(nextWindowID); else checkedv(OnDeinitWindow(nextWindowID)); } + + bool IsModalDialog() const override + { + XBMC_TRACE; + return checkedb(IsModalDialog()); + } + + bool IsDialogRunning() const override + { + XBMC_TRACE; + return checkedb(IsDialogRunning()); + } + bool IsDialog() const override + { + XBMC_TRACE; + return checkedb(IsDialog()); + } + bool IsMediaWindow() const override + { + XBMC_TRACE; + return checkedb(IsMediaWindow()); + } + + void SetRenderOrder(int renderOrder) override { XBMC_TRACE; P::m_renderOrder = renderOrder; } + + void setActive(bool active) override { XBMC_TRACE; P::m_active = active; } + bool isActive() override { XBMC_TRACE; return P::m_active; } + }; + + template <class P /* extends CGUIWindow*/> class InterceptorDialog : + public Interceptor<P> + { + public: + InterceptorDialog(const char* specializedName, + Window* _window, int windowid) : + Interceptor<P>(specializedName, _window, windowid) + { } + + InterceptorDialog(const char* specializedName, + Window* _window, int windowid, + const char* xmlfile) : + Interceptor<P>(specializedName, _window, windowid,xmlfile) + { } + }; + +#undef checkedb +#undef checkedv + +#endif + } +} diff --git a/xbmc/interfaces/legacy/WindowXML.cpp b/xbmc/interfaces/legacy/WindowXML.cpp new file mode 100644 index 0000000..51a4fbf --- /dev/null +++ b/xbmc/interfaces/legacy/WindowXML.cpp @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WindowXML.h" + +#include "ServiceBroker.h" +#include "WindowException.h" +#include "WindowInterceptor.h" +#include "addons/Addon.h" +#include "addons/Skin.h" +#include "addons/addoninfo/AddonInfo.h" +#include "addons/addoninfo/AddonType.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/TextureManager.h" +#include "utils/FileUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" + +#include <mutex> + +// These #defs are for WindowXML +#define CONTROL_BTNVIEWASICONS 2 +#define CONTROL_BTNSORTBY 3 +#define CONTROL_BTNSORTASC 4 +#define CONTROL_LABELFILES 12 + +#define A(x) interceptor->x + +namespace XBMCAddon +{ + namespace xbmcgui + { + template class Interceptor<CGUIMediaWindow>; + + /** + * This class extends the Interceptor<CGUIMediaWindow> in order to + * add behavior for a few more virtual functions that were unnecessary + * in the Window or WindowDialog. + */ +#define checkedb(methcall) ( window.isNotNull() ? xwin-> methcall : false ) +#define checkedv(methcall) { if (window.isNotNull()) xwin-> methcall ; } + + + //! @todo This should be done with template specialization + class WindowXMLInterceptor : public InterceptorDialog<CGUIMediaWindow> + { + WindowXML* xwin; + public: + WindowXMLInterceptor(WindowXML* _window, int windowid,const char* xmlfile) : + InterceptorDialog<CGUIMediaWindow>("CGUIMediaWindow",_window,windowid,xmlfile), xwin(_window) + { } + + void AllocResources(bool forceLoad = false) override + { XBMC_TRACE; if(up()) CGUIMediaWindow::AllocResources(forceLoad); else checkedv(AllocResources(forceLoad)); } + void FreeResources(bool forceUnLoad = false) override + { XBMC_TRACE; if(up()) CGUIMediaWindow::FreeResources(forceUnLoad); else checkedv(FreeResources(forceUnLoad)); } + bool OnClick(int iItem, const std::string &player = "") override { XBMC_TRACE; return up() ? CGUIMediaWindow::OnClick(iItem, player) : checkedb(OnClick(iItem)); } + + void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions) override + { XBMC_TRACE; if(up()) CGUIMediaWindow::Process(currentTime,dirtyregions); else checkedv(Process(currentTime,dirtyregions)); } + + // this is a hack to SKIP the CGUIMediaWindow + bool OnAction(const CAction &action) override + { XBMC_TRACE; return up() ? CGUIWindow::OnAction(action) : checkedb(OnAction(action)); } + + protected: + // CGUIWindow + bool LoadXML(const std::string &strPath, const std::string &strPathLower) override + { XBMC_TRACE; return up() ? CGUIMediaWindow::LoadXML(strPath,strPathLower) : xwin->LoadXML(strPath,strPathLower); } + + // CGUIMediaWindow + void GetContextButtons(int itemNumber, CContextButtons &buttons) override + { XBMC_TRACE; if (up()) CGUIMediaWindow::GetContextButtons(itemNumber,buttons); else xwin->GetContextButtons(itemNumber,buttons); } + bool Update(const std::string &strPath, bool) override + { XBMC_TRACE; return up() ? CGUIMediaWindow::Update(strPath) : xwin->Update(strPath); } + void SetupShares() override { XBMC_TRACE; if(up()) CGUIMediaWindow::SetupShares(); else checkedv(SetupShares()); } + + friend class WindowXML; + friend class WindowXMLDialog; + + }; + + WindowXML::~WindowXML() { XBMC_TRACE; deallocating(); } + + WindowXML::WindowXML(const String& xmlFilename, + const String& scriptPath, + const String& defaultSkin, + const String& defaultRes, + bool isMedia) : + Window(true) + { + XBMC_TRACE; + RESOLUTION_INFO res; + std::string strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res); + m_isMedia = isMedia; + + if (!CFileUtils::Exists(strSkinPath)) + { + std::string str("none"); + ADDON::AddonInfoPtr addonInfo = + std::make_shared<ADDON::CAddonInfo>(str, ADDON::AddonType::SKIN); + ADDON::CSkinInfo::TranslateResolution(defaultRes, res); + + // Check for the matching folder for the skin in the fallback skins folder + std::string fallbackPath = URIUtils::AddFileToFolder(scriptPath, "resources", "skins"); + std::string basePath = URIUtils::AddFileToFolder(fallbackPath, g_SkinInfo->ID()); + + strSkinPath = g_SkinInfo->GetSkinPath(xmlFilename, &res, basePath); + + // Check for the matching folder for the skin in the fallback skins folder (if it exists) + if (CFileUtils::Exists(basePath)) + { + addonInfo->SetPath(basePath); + std::shared_ptr<ADDON::CSkinInfo> skinInfo = std::make_shared<ADDON::CSkinInfo>(addonInfo, res); + skinInfo->Start(); + strSkinPath = skinInfo->GetSkinPath(xmlFilename, &res); + } + + if (!CFileUtils::Exists(strSkinPath)) + { + // Finally fallback to the DefaultSkin as it didn't exist in either the XBMC Skin folder or the fallback skin folder + addonInfo->SetPath(URIUtils::AddFileToFolder(fallbackPath, defaultSkin)); + std::shared_ptr<ADDON::CSkinInfo> skinInfo = std::make_shared<ADDON::CSkinInfo>(addonInfo, res); + + skinInfo->Start(); + strSkinPath = skinInfo->GetSkinPath(xmlFilename, &res); + if (!CFileUtils::Exists(strSkinPath)) + throw WindowException("XML File for Window is missing"); + } + } + + m_scriptPath = scriptPath; +// sXMLFileName = strSkinPath; + + interceptor = new WindowXMLInterceptor(this, lockingGetNextAvailableWindowId(),strSkinPath.c_str()); + setWindow(interceptor); + interceptor->SetCoordsRes(res); + } + + int WindowXML::lockingGetNextAvailableWindowId() + { + XBMC_TRACE; + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + return getNextAvailableWindowId(); + } + + void WindowXML::addItem(const Alternative<String, const ListItem*>& item, int position) + { + XBMC_TRACE; + // item could be deleted if the reference count is 0. + // so I MAY need to check prior to using a Ref just in + // case this object is managed by Python. I'm not sure + // though. + AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later()); + + // Tells the window to add the item to FileItem vector + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + + //---------------------------------------------------- + // Former AddItem call + //AddItem(ritem->item, pos); + { + CFileItemPtr& fileItem = ritem->item; + if (position == INT_MAX || position > A(m_vecItems)->Size()) + { + A(m_vecItems)->Add(fileItem); + } + else if (position < -1 && !(position*-1 < A(m_vecItems)->Size())) + { + A(m_vecItems)->AddFront(fileItem,0); + } + else + { + A(m_vecItems)->AddFront(fileItem,position); + } + A(m_viewControl).SetItems(*(A(m_vecItems))); + } + //---------------------------------------------------- + } + } + + void WindowXML::addItems(const std::vector<Alternative<String, const XBMCAddon::xbmcgui::ListItem* > > & items) + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + for (auto item : items) + { + AddonClass::Ref<ListItem> ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref<ListItem>(item.later()); + CFileItemPtr& fileItem = ritem->item; + A(m_vecItems)->Add(fileItem); + } + A(m_viewControl).SetItems(*(A(m_vecItems))); + } + + + void WindowXML::removeItem(int position) + { + XBMC_TRACE; + // Tells the window to remove the item at the specified position from the FileItem vector + XBMCAddonUtils::GuiLock lock(languageHook, false); + A(m_vecItems)->Remove(position); + A(m_viewControl).SetItems(*(A(m_vecItems))); + } + + int WindowXML::getCurrentListPosition() + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + int listPos = A(m_viewControl).GetSelectedItem(); + return listPos; + } + + void WindowXML::setCurrentListPosition(int position) + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + A(m_viewControl).SetSelectedItem(position); + } + + ListItem* WindowXML::getListItem(int position) + { + XBMCAddonUtils::GuiLock lock(languageHook, false); + //CFileItemPtr fi = pwx->GetListItem(listPos); + CFileItemPtr fi; + { + if (position < 0 || position >= A(m_vecItems)->Size()) + return new ListItem(); + fi = A(m_vecItems)->Get(position); + } + + if (fi == NULL) + { + throw WindowException("Index out of range (%i)",position); + } + + ListItem* sListItem = new ListItem(); + sListItem->item = fi; + + // let's hope someone reference counts this. + return sListItem; + } + + int WindowXML::getListSize() + { + XBMC_TRACE; + return A(m_vecItems)->Size(); + } + + void WindowXML::clearList() + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + A(ClearFileItems()); + + A(m_viewControl).SetItems(*(A(m_vecItems))); + } + + void WindowXML::setContainerProperty(const String& key, const String& value) + { + XBMC_TRACE; + A(m_vecItems)->SetProperty(key, value); + } + + void WindowXML::setContent(const String& value) + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + A(m_vecItems)->SetContent(value); + } + + int WindowXML::getCurrentContainerId() + { + XBMC_TRACE; + XBMCAddonUtils::GuiLock lock(languageHook, false); + return A(m_viewControl.GetCurrentControl()); + } + + bool WindowXML::OnAction(const CAction &action) + { + XBMC_TRACE; + // do the base class window first, and the call to python after this + bool ret = ref(window)->OnAction(action); // we don't currently want the mediawindow actions here + // look at the WindowXMLInterceptor onAction, it skips + // the CGUIMediaWindow::OnAction and calls directly to + // CGUIWindow::OnAction + AddonClass::Ref<Action> inf(new Action(action)); + invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get())); + PulseActionEvent(); + return ret; + } + + bool WindowXML::OnMessage(CGUIMessage& message) + { +#ifdef ENABLE_XBMC_TRACE_API + XBMC_TRACE; + CLog::Log(LOGDEBUG, "{}Message id:{}", _tg.getSpaces(), (int)message.GetMessage()); +#endif + + //! @todo We shouldn't be dropping down to CGUIWindow in any of this ideally. + //! We have to make up our minds about what python should be doing and + //! what this side of things should be doing + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_DEINIT: + { + return ref(window)->OnMessage(message); + } + break; + + case GUI_MSG_WINDOW_INIT: + { + ref(window)->OnMessage(message); + invokeCallback(new CallbackFunction<WindowXML>(this,&WindowXML::onInit)); + PulseActionEvent(); + return true; + } + break; + + case GUI_MSG_FOCUSED: + { + if (A(m_viewControl).HasControl(message.GetControlId()) && + A(m_viewControl).GetCurrentControl() != message.GetControlId()) + { + A(m_viewControl).SetFocused(); + return true; + } + // check if our focused control is one of our category buttons + int iControl=message.GetControlId(); + + invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onFocus,iControl)); + PulseActionEvent(); + } + break; + + case GUI_MSG_NOTIFY_ALL: + // most messages from GUI_MSG_NOTIFY_ALL break container content, whitelist working ones. + if (message.GetParam1() == GUI_MSG_PAGE_CHANGE || message.GetParam1() == GUI_MSG_WINDOW_RESIZE) + return A(CGUIMediaWindow::OnMessage(message)); + return true; + + case GUI_MSG_CLICKED: + { + int iControl=message.GetSenderId(); + // Handle Sort/View internally. Scripters shouldn't use ID 2, 3 or 4. + if (iControl == CONTROL_BTNSORTASC) // sort asc + { + CLog::Log(LOGINFO, "WindowXML: Internal asc/dsc button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortOrder(); + UpdateFileList();*/ + return true; + } + else if (iControl == CONTROL_BTNSORTBY) // sort by + { + CLog::Log(LOGINFO, "WindowXML: Internal sort button not implemented"); + /*if (m_guiState.get()) + m_guiState->SetNextSortMethod(); + UpdateFileList();*/ + return true; + } + + if(iControl && iControl != interceptor->GetID()) // pCallbackWindow && != this->GetID()) + { + CGUIControl* controlClicked = interceptor->GetControl(iControl); + + // The old python way used to check list AND SELECITEM method + // or if its a button, radiobutton. + // Its done this way for now to allow other controls without a + // python version like togglebutton to still raise a onAction event + if (controlClicked) // Will get problems if we the id is not on the window + // and we try to do GetControlType on it. So check to make sure it exists + { + if ((controlClicked->IsContainer() && (message.GetParam1() == ACTION_SELECT_ITEM || message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)) || !controlClicked->IsContainer()) + { + invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onClick,iControl)); + PulseActionEvent(); + return true; + } + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_DOUBLE_CLICK) + { + invokeCallback(new CallbackFunction<WindowXML,int>(this,&WindowXML::onDoubleClick,iControl)); + PulseActionEvent(); + return true; + } + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_MOUSE_RIGHT_CLICK) + { + AddonClass::Ref<Action> inf(new Action(CAction(ACTION_CONTEXT_MENU))); + invokeCallback(new CallbackFunction<WindowXML,AddonClass::Ref<Action> >(this,&WindowXML::onAction,inf.get())); + PulseActionEvent(); + return true; + } + // the core context menu can lead to all sort of issues right now when used with WindowXMLs, so lets intercept the corresponding message + else if (controlClicked->IsContainer() && message.GetParam1() == ACTION_CONTEXT_MENU) + return true; + } + } + } + break; + } + + return A(CGUIMediaWindow::OnMessage(message)); + } + + void WindowXML::AllocResources(bool forceLoad /*= false */) + { + XBMC_TRACE; + std::string tmpDir = URIUtils::GetDirectory(ref(window)->GetProperty("xmlfile").asString()); + std::string fallbackMediaPath; + URIUtils::GetParentPath(tmpDir, fallbackMediaPath); + URIUtils::RemoveSlashAtEnd(fallbackMediaPath); + m_mediaDir = fallbackMediaPath; + + //CLog::Log(LOGDEBUG, "CGUIPythonWindowXML::AllocResources called: {}", fallbackMediaPath); + CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir); + ref(window)->AllocResources(forceLoad); + CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir); + } + + void WindowXML::FreeResources(bool forceUnLoad /*= false */) + { + XBMC_TRACE; + + ref(window)->FreeResources(forceUnLoad); + } + + void WindowXML::Process(unsigned int currentTime, CDirtyRegionList ®ions) + { + XBMC_TRACE; + CServiceBroker::GetGUI()->GetTextureManager().AddTexturePath(m_mediaDir); + ref(window)->Process(currentTime, regions); + CServiceBroker::GetGUI()->GetTextureManager().RemoveTexturePath(m_mediaDir); + } + + bool WindowXML::OnClick(int iItem) + { + XBMC_TRACE; + // Hook Over calling CGUIMediaWindow::OnClick(iItem) results in it trying to PLAY the file item + // which if its not media is BAD and 99 out of 100 times undesirable. + return false; + } + + bool WindowXML::OnDoubleClick(int iItem) + { + XBMC_TRACE; + return false; + } + + void WindowXML::GetContextButtons(int itemNumber, CContextButtons &buttons) + { + XBMC_TRACE; + // maybe on day we can make an easy way to do this context menu + // with out this method overriding the MediaWindow version, it will display 'Add to Favorites' + } + + bool WindowXML::LoadXML(const String &strPath, const String &strLowerPath) + { + XBMC_TRACE; + return A(CGUIWindow::LoadXML(strPath, strLowerPath)); + } + + void WindowXML::SetupShares() + { + XBMC_TRACE; + } + + bool WindowXML::Update(const String &strPath) + { + XBMC_TRACE; + return true; + } + + WindowXMLDialog::WindowXMLDialog(const String& xmlFilename, const String& scriptPath, + const String& defaultSkin, + const String& defaultRes) : + WindowXML(xmlFilename, scriptPath, defaultSkin, defaultRes), + WindowDialogMixin(this) + { XBMC_TRACE; } + + WindowXMLDialog::~WindowXMLDialog() { XBMC_TRACE; deallocating(); } + + bool WindowXMLDialog::OnMessage(CGUIMessage &message) + { + XBMC_TRACE; + if (message.GetMessage() == GUI_MSG_WINDOW_DEINIT) + return A(CGUIWindow::OnMessage(message)); + + return WindowXML::OnMessage(message); + } + + bool WindowXMLDialog::OnAction(const CAction &action) + { + XBMC_TRACE; + return WindowDialogMixin::OnAction(action) ? true : WindowXML::OnAction(action); + } + + void WindowXMLDialog::OnDeinitWindow(int nextWindowID) + { + XBMC_TRACE; + CServiceBroker::GetGUI()->GetWindowManager().RemoveDialog(interceptor->GetID()); + WindowXML::OnDeinitWindow(nextWindowID); + } + + bool WindowXMLDialog::LoadXML(const String &strPath, const String &strLowerPath) + { + XBMC_TRACE; + if (WindowXML::LoadXML(strPath, strLowerPath)) + { + // Set the render order to the dialog's default in case it's not specified in the skin xml + // because this dialog is mapped to CGUIMediaWindow instead of CGUIDialog. + // This must be done here, because the render order will be reset before loading the skin xml. + if (ref(window)->GetRenderOrder() == RENDER_ORDER_WINDOW) + window->SetRenderOrder(RENDER_ORDER_DIALOG); + return true; + } + return false; + } + + } + +} diff --git a/xbmc/interfaces/legacy/WindowXML.h b/xbmc/interfaces/legacy/WindowXML.h new file mode 100644 index 0000000..349b0a0 --- /dev/null +++ b/xbmc/interfaces/legacy/WindowXML.h @@ -0,0 +1,559 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "Window.h" +#include "WindowDialogMixin.h" +#include "swighelper.h" +#include "windows/GUIMediaWindow.h" + +#include <limits.h> +#include <vector> + +namespace XBMCAddon +{ + namespace xbmcgui + { + class ListItem; + class WindowXMLInterceptor; + + // + /// \defgroup python_xbmcgui_window_xml Subclass - WindowXML + /// \ingroup python_xbmcgui_window + /// @{ + /// @brief __GUI xml window class.__ + /// + /// \python_class{ xbmcgui.WindowXML(xmlFilename, scriptPath[, defaultSkin, defaultRes]) } + /// + /// Creates a new xml file based window class. + /// + /// \note This class include also all calls from <b><c>\ref python_xbmcgui_window</c></b>. + /// + /// @param xmlFilename string - the name of the xml file to + /// look for. + /// @param scriptPath string - path to script. used to + /// fallback to if the xml doesn't exist in + /// the current skin. (eg xbmcaddon.Addon().getAddonInfo('path')) + /// @param defaultSkin [opt] string - name of the folder in the + /// skins path to look in for the xml. + /// (default='Default') + /// @param defaultRes [opt] string - default skins resolution. + /// (1080i, 720p, ntsc16x9, ntsc, pal16x9 or pal. default='720p') + /// @param isMedia [opt] bool - if False, create a regular window. + /// if True, create a mediawindow. + /// (default=False) + /// @throws Exception if more then 200 windows are created. + /// + /// \remark Skin folder structure is e.g. **resources/skins/Default/720p** + /// + /// Deleting this window will activate the old window that was active + /// and resets (not delete) all controls that are associated with this + /// window. + /// + ///-------------------------------------------------------------------------- + /// @python_v18 New param added **isMedia**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// win = xbmcgui.WindowXML('script-Lyrics-main.xml', xbmcaddon.Addon().getAddonInfo('path'), 'default', '1080i', False) + /// win.doModal() + /// del win + /// .. + /// ~~~~~~~~~~~~~ + /// + /// + ///-------------------------------------------------------------------------- + /// + /// On functions defined input variable <b><tt>controlId</tt> (GUI control identifier)</b> + /// is the on window.xml defined value behind type added with <tt><b>id="..."</b></tt> and + /// used to identify for changes there and on callbacks. + /// + /// ~~~~~~~~~~~~~{.xml} + /// <control type="label" id="31"> + /// <description>Title Label</description> + /// ... + /// </control> + /// <control type="progress" id="32"> + /// <description>progress control</description> + /// ... + /// </control> + /// ~~~~~~~~~~~~~ + /// + // + class WindowXML : public Window + { + std::string sFallBackPath; + + protected: +#ifndef SWIG + /** + * This helper retrieves the next available id. It is doesn't + * assume that the global lock is already being held. + */ + static int lockingGetNextAvailableWindowId(); + + WindowXMLInterceptor* interceptor; +#endif + + public: + WindowXML(const String& xmlFilename, const String& scriptPath, + const String& defaultSkin = "Default", + const String& defaultRes = "720p", + bool isMedia = false); + ~WindowXML() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ addItem(item[, position]) } + /// Add a new item to this Window List. + /// + /// @param item string, unicode or ListItem - item to add. + /// @param position [opt] integer - position of item to add. (NO Int = Adds to bottom,0 adds to top, 1 adds to one below from top,-1 adds to one above from bottom etc etc ) + /// - If integer positions are greater than list size, negative positions will add to top of list, positive positions will add to bottom of list + /// + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.addItem('Reboot Kodi', 0) + /// .. + /// ~~~~~~~~~~~~~ + /// + addItem(...); +#else + SWIGHIDDENVIRTUAL void addItem(const Alternative<String, const ListItem*>& item, int position = INT_MAX); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ addItems(items) } + /// Add a list of items to to the window list. + /// + /// + /// @param items List - list of strings, unicode objects or ListItems to add. + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.addItems(['Reboot Kodi', 'Restart Kodi']) + /// .. + /// ~~~~~~~~~~~~~ + /// + addItems(...); +#else + SWIGHIDDENVIRTUAL void addItems(const std::vector<Alternative<String, const XBMCAddon::xbmcgui::ListItem* > > & items); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ removeItem(position) } + /// Removes a specified item based on position, from the Window List. + /// + /// @param position integer - position of item to remove. + /// + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.removeItem(5) + /// .. + /// ~~~~~~~~~~~~~ + /// + removeItem(...); +#else + SWIGHIDDENVIRTUAL void removeItem(int position); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ getCurrentListPosition() } + /// Gets the current position in the Window List. + /// + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// pos = self.getCurrentListPosition() + /// .. + /// ~~~~~~~~~~~~~ + /// + getCurrentListPosition(); +#else + SWIGHIDDENVIRTUAL int getCurrentListPosition(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ setCurrentListPosition(position) } + /// Set the current position in the Window List. + /// + /// @param position integer - position of item to set. + /// + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.setCurrentListPosition(5) + /// .. + /// ~~~~~~~~~~~~~ + /// + setCurrentListPosition(...); +#else + SWIGHIDDENVIRTUAL void setCurrentListPosition(int position); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ getListItem(position) } + /// Returns a given ListItem in this Window List. + /// + /// @param position integer - position of item to return. + /// + /// + /// + /// ---------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// listitem = self.getListItem(6) + /// .. + /// ~~~~~~~~~~~~~ + /// + getListItem(...); +#else + SWIGHIDDENVIRTUAL ListItem* getListItem(int position); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ getListSize() } + /// Returns the number of items in this Window List. + /// + /// + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// listSize = self.getListSize() + /// .. + /// ~~~~~~~~~~~~~ + /// + getListSize(); +#else + SWIGHIDDENVIRTUAL int getListSize(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ clearList() } + /// Clear the Window List. + /// + /// ------------------------------------------------------------------------ + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.clearList() + /// .. + /// ~~~~~~~~~~~~~ + /// + clearList(); +#else + SWIGHIDDENVIRTUAL void clearList(); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ setContainerProperty(key, value) } + /// Sets a container property, similar to an infolabel. + /// + /// @param key string - property name. + /// @param value string or unicode - value of property. + /// + /// @note Key is NOT case sensitive.\n + /// You can use the above as keywords for arguments and skip certain + /// optional arguments.\n + /// Once you use a keyword, all following arguments require the keyword. + /// + /// + /// ------------------------------------------------------------------------ + /// @python_v17 Changed function from **setProperty** to **setContainerProperty**. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.setContainerProperty('Category', 'Newest') + /// .. + /// ~~~~~~~~~~~~~ + /// + setContainerProperty(...); +#else + SWIGHIDDENVIRTUAL void setContainerProperty(const String &strProperty, const String &strValue); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ setContent(value) } + /// Sets the content type of the container. + /// + /// @param value string or unicode - content value. + /// + /// __Available content types__ + /// | Name | Media | + /// |:-----------:|:-----------------------------------------| + /// | actors | Videos + /// | addons | Addons, Music, Pictures, Programs, Videos + /// | albums | Music, Videos + /// | artists | Music, Videos + /// | countries | Music, Videos + /// | directors | Videos + /// | files | Music, Videos + /// | games | Games + /// | genres | Music, Videos + /// | images | Pictures + /// | mixed | Music, Videos + /// | movies | Videos + /// | Musicvideos | Music, Videos + /// | playlists | Music, Videos + /// | seasons | Videos + /// | sets | Videos + /// | songs | Music + /// | studios | Music, Videos + /// | tags | Music, Videos + /// | tvshows | Videos + /// | videos | Videos + /// | years | Music, Videos + /// + /// ------------------------------------------------------------------------ + /// @python_v18 Added new function. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// self.setContent('movies') + /// .. + /// ~~~~~~~~~~~~~ + /// + setContent(...); +#else + SWIGHIDDENVIRTUAL void setContent(const String &strValue); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcgui_window_xml + /// @brief \python_func{ getCurrentContainerId() } + /// Get the id of the currently visible container. + /// + /// ------------------------------------------------------------------------ + /// @python_v17 Added new function. + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// container_id = self.getCurrentContainerId() + /// .. + /// ~~~~~~~~~~~~~ + /// + getCurrentContainerId(...); +#else + SWIGHIDDENVIRTUAL int getCurrentContainerId(); +#endif + +#ifndef SWIG + // CGUIWindow + bool OnMessage(CGUIMessage& message) override; + bool OnAction(const CAction& action) override; + SWIGHIDDENVIRTUAL void AllocResources(bool forceLoad = false); + SWIGHIDDENVIRTUAL void FreeResources(bool forceUnLoad = false); + SWIGHIDDENVIRTUAL bool OnClick(int iItem); + SWIGHIDDENVIRTUAL bool OnDoubleClick(int iItem); + SWIGHIDDENVIRTUAL void Process(unsigned int currentTime, CDirtyRegionList &dirtyregions); + + bool IsMediaWindow() const override + { + XBMC_TRACE; + return m_isMedia; + }; + + // This method is identical to the Window::OnDeinitWindow method + // except it passes the message on to their respective parents. + // Since the respective parent differences are handled by the + // interceptor there's no reason to define this one here. +// SWIGHIDDENVIRTUAL void OnDeinitWindow(int nextWindowID); + + + protected: + // CGUIWindow + SWIGHIDDENVIRTUAL bool LoadXML(const String &strPath, const String &strPathLower); + + // CGUIMediaWindow + SWIGHIDDENVIRTUAL void GetContextButtons(int itemNumber, CContextButtons &buttons); + SWIGHIDDENVIRTUAL bool Update(const String &strPath); + + void SetupShares(); + String m_scriptPath; + String m_mediaDir; + bool m_isMedia; + + friend class WindowXMLInterceptor; +#endif + }; + ///@} + + // Ideally what we want here is a Dialog/Media Window. The problem is that these + // are two orthogonal discriminations of CGUIWindow and there wasn't a previous + // accounting for this possibility through the use of making CGUIWindow a + // virtual base class of the pertinent subclasses. So now we're left with + // no good solution. + // + // <strike>So here we're going to have the 'main' hierarchy (the one visible to SWIG) + // go the way intended - through WindowXML, but we're going to borrow dialog + // functionality from CGUIDialog by using it as a Mixin.</strike> + // + // jmarshall says that this class has no reason to inherit from CGUIMediaWindow. + // At some point this entire hierarchy needs to be reworked. The XML handling + // routines should be put in a mixin. + // + /// \defgroup python_xbmcgui_window_dialog_xml Subclass - WindowDialogXML + /// \ingroup python_xbmcgui_window_xml + /// @{ + /// @brief __GUI xml window dialog__ + /// + /// \python_class{ xbmcgui.WindowXMLDialog(xmlFilename, scriptPath[, defaultSkin, defaultRes]) } + /// + /// Creates a new xml file based window dialog class. + /// + /// @param xmlFilename string - the name of the xml file to + /// look for. + /// @param scriptPath string - path to script. used to + /// fallback to if the xml doesn't exist in + /// the current skin. (eg \ref python_xbmcaddon_Addon "xbmcaddon.Addon().getAddonInfo('path'))" + /// @param defaultSkin [opt] string - name of the folder in the + /// skins path to look in for the xml. + /// (default='Default') + /// @param defaultRes [opt] string - default skins resolution. + /// (1080i, 720p, ntsc16x9, ntsc, pal16x9 or pal. default='720p') + /// @throws Exception if more then 200 windows are created. + /// + /// @note Skin folder structure is e.g. **resources/skins/Default/720p** + /// + /// + ///------------------------------------------------------------------------- + /// + /// **Example:** + /// ~~~~~~~~~~~~~{.py} + /// .. + /// dialog = xbmcgui.WindowXMLDialog('script-Lyrics-main.xml', xbmcaddon.Addon().getAddonInfo('path'), 'default', '1080i') + /// dialog.doModal() + /// del dialog + /// .. + /// ~~~~~~~~~~~~~ + /// + /// + ///------------------------------------------------------------------------- + /// + /// On functions defined input variable <b><tt>controlId</tt> (GUI control identifier)</b> + /// is the on window.xml defined value behind type added with <tt><b>id="..."</b></tt> and + /// used to identify for changes there and on callbacks. + /// + /// ~~~~~~~~~~~~~{.xml} + /// <control type="label" id="31"> + /// <description>Title Label</description> + /// ... + /// </control> + /// <control type="progress" id="32"> + /// <description>progress control</description> + /// ... + /// </control> + /// ~~~~~~~~~~~~~ + /// + // + class WindowXMLDialog : public WindowXML, private WindowDialogMixin + { + public: + WindowXMLDialog(const String& xmlFilename, const String& scriptPath, + const String& defaultSkin = "Default", + const String& defaultRes = "720p"); + + ~WindowXMLDialog() override; + +#ifndef SWIG + bool OnMessage(CGUIMessage& message) override; + bool IsDialogRunning() const override + { + XBMC_TRACE; + return WindowDialogMixin::IsDialogRunning(); + } + bool IsDialog() const override + { + XBMC_TRACE; + return true; + }; + bool IsModalDialog() const override + { + XBMC_TRACE; + return true; + }; + bool IsMediaWindow() const override + { + XBMC_TRACE; + return false; + }; + bool OnAction(const CAction& action) override; + void OnDeinitWindow(int nextWindowID) override; + + bool LoadXML(const String& strPath, const String& strPathLower) override; + + inline void show() override + { + XBMC_TRACE; + WindowDialogMixin::show(); + } + inline void close() override + { + XBMC_TRACE; + WindowDialogMixin::close(); + } + + friend class DialogJumper; +#endif + }; + ///@} + } +} diff --git a/xbmc/interfaces/legacy/aojsonrpc.h b/xbmc/interfaces/legacy/aojsonrpc.h new file mode 100644 index 0000000..7f1d875 --- /dev/null +++ b/xbmc/interfaces/legacy/aojsonrpc.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "interfaces/json-rpc/ITransportLayer.h" +#include "interfaces/json-rpc/JSONRPC.h" + +class CVariant; + +class CAddOnTransport : public JSONRPC::ITransportLayer +{ +public: + bool PrepareDownload(const char *path, CVariant &details, std::string &protocol) override { return false; } + bool Download(const char *path, CVariant& result) override { return false; } + int GetCapabilities() override { return JSONRPC::Response; } + + class CAddOnClient : public JSONRPC::IClient + { + public: + int GetPermissionFlags() override { return JSONRPC::OPERATION_PERMISSION_ALL; } + int GetAnnouncementFlags() override { return 0; } + bool SetAnnouncementFlags(int flags) override { return true; } + }; +}; diff --git a/xbmc/interfaces/legacy/swighelper.h b/xbmc/interfaces/legacy/swighelper.h new file mode 100644 index 0000000..15a822c --- /dev/null +++ b/xbmc/interfaces/legacy/swighelper.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +/** + * SWIGHIDDENVIRTUAL allows the keyword 'virtual' to be there when the main + * Addon api is compiled, but be hidden from the SWIG code generator. + * + * This is to provide finer grain control over which methods are callbackable + * (is that a word? ...) + * into the scripting language, and which ones are not. True polymorphic + * behavior across the scripting language boundary will ONLY occur where + * the keyword 'virtual' is used. In other words, you can use the macro + * SWIGHIDDENVIRTUAL to 'hide' the polymorphic behavior from the scripting + * language using the macro instead. + * + * Note: You should not hide virtual destructors from the scripting language. + */ +#ifdef SWIG +#define SWIGHIDDENVIRTUAL +#else +#define SWIGHIDDENVIRTUAL virtual +#endif + +/** + * SWIG_CONSTANT_FROM_GETTER will define a constant in the scripting + * language from a getter in the Addon api and also provide the + * Addon api declaration. E.g. If you use: + * + * SWIG_CONSTANT_FROM_GETTER(int, MY_CONSTANT); + * + * ... in an Addon api header file then you need to define a function: + * + * int getMy_CONSTANT(); + * + * ... in a .cpp file. That call will be made to determine the value + * of the constant in the scripting language. + */ +#ifdef SWIG +#define SWIG_CONSTANT_FROM_GETTER(type,varname) %constant type varname = get##varname () +#else +#define SWIG_CONSTANT_FROM_GETTER(type,varname) type get##varname () +#endif + +/** + * SWIG_CONSTANT defines a constant in SWIG from an already existing + * definition in the Addon api. E.g. a #define from the core window + * system like SORT_METHOD_PROGRAM_COUNT included in the api via + * a #include can be exposed to the scripting language using + * SWIG_CONSTANT(int,SORT_METHOD_PROGRAM_COUNT). + * + * This macro can be used when the constant name and the C++ reference + * look the same. When they look different see SWIG_CONSTANT2 + * + * Note, this declaration is invisible to the API C++ code and can + * only be seen by the SWIG processor. + */ +#ifdef SWIG +#define SWIG_CONSTANT(type,var) %constant type var = var +#else +#define SWIG_CONSTANT(type,var) +#endif + +/** + * SWIG_CONSTANT2 defines a constant in SWIG from an already existing + * definition in the Addon api. E.g. a #define from the core window + * system like SORT_METHOD_VIDEO_YEAR included in the api via + * a #include can be exposed to the scripting language using + * SWIG_CONSTANT2(int,SORT_METHOD_VIDEO_YEAR,SORT_METHOD_YEAR). + * + * This macro can be used when the constant name and the C++ reference + * don't look the same. When they look the same see SWIG_CONSTANT + * + * Note, this declaration is invisible to the API C++ code and can + * only be seen by the SWIG processor. + */ +#ifdef SWIG +#define SWIG_CONSTANT2(type,var,val) %constant type var = val +#else +#define SWIG_CONSTANT2(type,var,val) +#endif + +/** +* SWIG_IMMUTABLE defines a member as immutable i.e. read-only. +* +* Note, this declaration is invisible to the API C++ code and can +* only be seen by the SWIG processor. +*/ +#ifdef SWIG +#define SWIG_IMMUTABLE(var) %feature("immutable"); var; %feature("immutable", "") +#else +#define SWIG_IMMUTABLE(var) var +#endif + diff --git a/xbmc/interfaces/legacy/wsgi/CMakeLists.txt b/xbmc/interfaces/legacy/wsgi/CMakeLists.txt new file mode 100644 index 0000000..cc29eb4 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/CMakeLists.txt @@ -0,0 +1,13 @@ +if(MICROHTTPD_FOUND) + set(SOURCES WsgiErrorStream.cpp + WsgiInputStream.cpp + WsgiResponseBody.cpp + WsgiResponse.cpp) + + set(HEADERS WsgiErrorStream.h + WsgiInputStream.h + WsgiResponse.h + WsgiResponseBody.h) + + core_add_library(legacy_interface_wsgi) +endif() diff --git a/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.cpp b/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.cpp new file mode 100644 index 0000000..b8096fe --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WsgiErrorStream.h" + +#include "network/httprequesthandler/python/HTTPPythonRequest.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + WsgiErrorStream::WsgiErrorStream() + : m_request(NULL) + { } + + WsgiErrorStream::~WsgiErrorStream() + { + m_request = NULL; + } + + void WsgiErrorStream::write(const String& str) + { + if (str.empty()) + return; + + String msg = str; + // remove a trailing \n + if (msg.at(msg.size() - 1) == '\n') + msg.erase(msg.size() - 1); + + if (m_request != NULL) + CLog::Log(LOGERROR, "WSGI [{}]: {}", m_request->url, msg); + else + CLog::Log(LOGERROR, "WSGI: {}", msg); + } + + void WsgiErrorStream::writelines(const std::vector<String>& seq) + { + if (seq.empty()) + return; + + String msg = StringUtils::Join(seq, ""); + write(msg); + } + +#ifndef SWIG + void WsgiErrorStream::SetRequest(HTTPPythonRequest* request) + { + if (m_request != NULL) + return; + + m_request = request; + } +#endif + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.h b/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.h new file mode 100644 index 0000000..e9e7694 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiErrorStream.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "interfaces/legacy/AddonClass.h" + +#include <vector> + +struct HTTPPythonRequest; + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + + /// \defgroup python_xbmcwsgi_WsgiErrorStream WsgiErrorStream + /// \ingroup python_xbmcwsgi + /// @{ + /// @brief **Represents the wsgi.errors stream to write error messages.** + /// + /// \python_class{ WsgiErrorStream() } + /// + /// This implementation writes the error messages to the application's log + /// file. + /// + ///------------------------------------------------------------------------- + /// + class WsgiErrorStream : public AddonClass + { + public: + WsgiErrorStream(); + ~WsgiErrorStream() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcwsgi_WsgiErrorStream + /// \python_func{ flush() } + /// Since nothing is buffered this is a no-op. + /// + /// + flush(); +#else + inline void flush() { } +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcwsgi_WsgiErrorStream + /// \python_func{ write(str) } + /// Writes the given error message to the application's log file. + /// + /// @param str A string to save in log file + /// + /// @note A trailing `\n` is removed. + /// + write(...); +#else + void write(const String& str); +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// + /// \ingroup python_xbmcwsgi_WsgiErrorStream + /// \python_func{ writelines(seq) } + /// Joins the given list of error messages (without any separator) into + /// a single error message which is written to the application's log file. + /// + /// @param seq A list of strings which will be logged. + /// + writelines(...); +#else + void writelines(const std::vector<String>& seq); +#endif + +#ifndef SWIG + /** + * Sets the given request. + */ + void SetRequest(HTTPPythonRequest* request); + + HTTPPythonRequest* m_request; +#endif + }; + /// @} + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiInputStream.cpp b/xbmc/interfaces/legacy/wsgi/WsgiInputStream.cpp new file mode 100644 index 0000000..5146007 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiInputStream.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WsgiInputStream.h" + +#include "network/httprequesthandler/python/HTTPPythonRequest.h" +#include "utils/StringUtils.h" + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + WsgiInputStreamIterator::WsgiInputStreamIterator() + : m_data(), + m_line() + { } + +#ifndef SWIG + WsgiInputStreamIterator::WsgiInputStreamIterator(const String& data, bool end /* = false */) + : m_data(data), + m_remaining(end ? 0 : data.size()), + m_line() + { } +#endif + + WsgiInputStreamIterator::~WsgiInputStreamIterator() = default; + + String WsgiInputStreamIterator::read(unsigned long size /* = 0 */) const + { + // make sure we don't try to read more data than we have + if (size <= 0 || size > m_remaining) + size = m_remaining; + + // remember the current read offset + size_t offset = static_cast<size_t>(m_offset); + + // adjust the read offset and the remaining data length + m_offset += size; + m_remaining -= size; + + // return the data being requested + return m_data.substr(offset, size); + } + + String WsgiInputStreamIterator::readline(unsigned long size /* = 0 */) const + { + // make sure we don't try to read more data than we have + if (size <= 0 || size > m_remaining) + size = m_remaining; + + size_t offset = static_cast<size_t>(m_offset); + size_t pos = m_data.find('\n', offset); + + // make sure pos has a valid value and includes the \n character + if (pos == std::string::npos) + pos = m_data.size(); + else + pos += 1; + + if (pos - offset < size) + size = pos - offset; + + // read the next line + String line = read(size); + + // remove any trailing \r\n + StringUtils::TrimRight(line, "\r\n"); + + return line; + } + + std::vector<String> WsgiInputStreamIterator::readlines(unsigned long sizehint /* = 0 */) const + { + std::vector<String> lines; + + // make sure we don't try to read more data than we have + if (sizehint <= 0 || sizehint > m_remaining) + sizehint = m_remaining; + + do + { + // read a full line + String line = readline(); + + // adjust the sizehint by the number of bytes just read + sizehint -= line.length(); + + // add it to the list of read lines + lines.push_back(line); + } while (sizehint > 0); + + return lines; + } + +#ifndef SWIG + WsgiInputStreamIterator& WsgiInputStreamIterator::operator++() + { + m_line.clear(); + + if (!end()) + { + // read the next line + m_line = readline(); + } + + return *this; + } + + bool WsgiInputStreamIterator::operator==(const WsgiInputStreamIterator& rhs) + { + return m_data == rhs.m_data && + m_offset == rhs.m_offset && + m_remaining == rhs.m_remaining; + } + + bool WsgiInputStreamIterator::operator!=(const WsgiInputStreamIterator& rhs) + { + return !(*this == rhs); + } + + String& WsgiInputStreamIterator::operator*() + { + return m_line; + } +#endif + + WsgiInputStream::WsgiInputStream() + : m_request(NULL) + { } + + WsgiInputStream::~WsgiInputStream() + { + m_request = NULL; + } + +#ifndef SWIG + WsgiInputStreamIterator* WsgiInputStream::begin() + { + return new WsgiInputStreamIterator(m_data, false); + } + + WsgiInputStreamIterator* WsgiInputStream::end() + { + return new WsgiInputStreamIterator(m_data, true); + } + + void WsgiInputStream::SetRequest(HTTPPythonRequest* request) + { + if (m_request != NULL) + return; + + m_request = request; + + // set the remaining bytes to be read + m_data = m_request->requestContent; + m_offset = 0; + m_remaining = m_data.size(); + } +#endif + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiInputStream.h b/xbmc/interfaces/legacy/wsgi/WsgiInputStream.h new file mode 100644 index 0000000..d7bf73f --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiInputStream.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "interfaces/legacy/AddonClass.h" + +#include <vector> + +struct HTTPPythonRequest; + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + + // Iterator for the wsgi.input stream. + class WsgiInputStreamIterator : public AddonClass + { + public: + WsgiInputStreamIterator(); + ~WsgiInputStreamIterator() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcwsgi_WsgiInputStream + /// \python_func{ read([size]) } + /// + /// Read a maximum of `<size>` bytes from the wsgi.input stream. + /// + /// @param size [opt] bytes to read + /// @return Returns the readed string + /// + read(...); +#else + String read(unsigned long size = 0) const; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcwsgi_WsgiInputStream + /// \python_func{ readline([size]) } + /// + /// Read a full line up to a maximum of `<size>` bytes from the wsgi.input + /// stream. + /// + /// @param size [opt] bytes to read + /// @return Returns the readed string line + /// + read(...); +#else + String readline(unsigned long size = 0) const; +#endif + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcwsgi_WsgiInputStream + /// \python_func{ readlines([sizehint]) } + /// + /// Read multiple full lines up to at least `<sizehint>` bytes from the + /// wsgi.input stream and return them as a list. + /// + /// @param sizehint [opt] bytes to read + /// @return Returns a list readed string lines + /// + read(...); +#else + std::vector<String> readlines(unsigned long sizehint = 0) const; +#endif + +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + WsgiInputStreamIterator(const String& data, bool end = false); + + WsgiInputStreamIterator& operator++(); + bool operator==(const WsgiInputStreamIterator& rhs); + bool operator!=(const WsgiInputStreamIterator& rhs); + String& operator*(); + inline bool end() const { return m_remaining <= 0; } + + protected: + String m_data; + mutable unsigned long m_offset = 0; + mutable unsigned long m_remaining = 0; + + private: + String m_line; +#endif + }; + + /// \defgroup python_xbmcwsgi_WsgiInputStream WsgiInputStream + /// \ingroup python_xbmcwsgi + /// @{ + /// @brief **Represents the wsgi.input stream to access data from a HTTP request.** + /// + /// \python_class{ WsgiInputStream() } + /// + ///------------------------------------------------------------------------- + /// + class WsgiInputStream : public WsgiInputStreamIterator + { + public: + WsgiInputStream(); + ~WsgiInputStream() override; + +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + WsgiInputStreamIterator* begin(); + WsgiInputStreamIterator* end(); + + /** + * Sets the given request. + */ + void SetRequest(HTTPPythonRequest* request); + + HTTPPythonRequest* m_request; +#endif + }; + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiResponse.cpp b/xbmc/interfaces/legacy/wsgi/WsgiResponse.cpp new file mode 100644 index 0000000..175e83d --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiResponse.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WsgiResponse.h" + +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <inttypes.h> +#include <utility> + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + WsgiResponse::WsgiResponse() + : m_responseHeaders(), + m_body() + { } + + WsgiResponse::~WsgiResponse() = default; + + WsgiResponseBody* WsgiResponse::operator()(const String& status, const std::vector<WsgiHttpHeader>& response_headers, void* exc_info /* = NULL */) + { + if (m_called) + { + CLog::Log(LOGWARNING, "WsgiResponse: callable has already been called"); + return NULL; + } + + m_called = true; + + // parse the status + if (!status.empty()) + { + std::vector<String> statusParts = StringUtils::Split(status, ' ', 2); + if (statusParts.size() == 2 && StringUtils::IsNaturalNumber(statusParts.front())) + { + int64_t parsedStatus = strtol(statusParts.front().c_str(), NULL, 0); + if (parsedStatus >= MHD_HTTP_OK && parsedStatus <= MHD_HTTP_NOT_EXTENDED) + m_status = static_cast<int>(parsedStatus); + else + CLog::Log(LOGWARNING, "WsgiResponse: invalid status number {} in \"{}\" provided", + parsedStatus, status); + } + else + CLog::Log(LOGWARNING, "WsgiResponse: invalid status \"{}\" provided", status); + } + else + CLog::Log(LOGWARNING, "WsgiResponse: empty status provided"); + + // copy the response headers + for (const auto& headerIt : response_headers) + m_responseHeaders.insert({headerIt.first(), headerIt.second()}); + + return &m_body; + } + +#ifndef SWIG + void WsgiResponse::Append(const std::string& data) + { + if (!data.empty()) + m_body.m_data.append(data); + } + + bool WsgiResponse::Finalize(HTTPPythonRequest* request) const + { + if (request == NULL || !m_called) + return false; + + // copy the response status + request->responseStatus = m_status; + + // copy the response headers + if (m_status >= MHD_HTTP_OK && m_status < MHD_HTTP_BAD_REQUEST) + request->responseHeaders.insert(m_responseHeaders.begin(), m_responseHeaders.end()); + else + request->responseHeadersError.insert(m_responseHeaders.begin(), m_responseHeaders.end()); + + // copy the body + request->responseData = m_body.m_data; + + return true; + } +#endif + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiResponse.h b/xbmc/interfaces/legacy/wsgi/WsgiResponse.h new file mode 100644 index 0000000..412e520 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiResponse.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "interfaces/legacy/AddonClass.h" +#include "interfaces/legacy/Tuple.h" +#include "interfaces/legacy/wsgi/WsgiResponseBody.h" +#include "network/httprequesthandler/python/HTTPPythonRequest.h" + +#include <vector> + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + typedef Tuple<String, String> WsgiHttpHeader; + + /// \defgroup python_xbmcwsgi_WsgiResponse WsgiResponse + /// \ingroup python_xbmcwsgi + /// @{ + /// @brief **Represents the start_response callable passed to a WSGI handler.** + /// + /// \python_class{ WsgiResponse() } + /// + ///------------------------------------------------------------------------- + /// + class WsgiResponse : public AddonClass + { + public: + WsgiResponse(); + ~WsgiResponse() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcwsgi_WsgiInputStreamIterator + /// \python_func{ operator(status, response_headers[, exc_info]) } + /// + /// Callable implementation to initialize the response with the given + /// HTTP status and the HTTP response headers. + /// + /// @param status an HTTP status string like 200 OK or 404 + /// Not Found. + /// @param response_headers a list of (header_name, header_value) + /// tuples. It must be a Python list. Each + /// header_name must be a valid HTTP header + /// field-name (as + /// @param exc_info [optional] python sys.exc_info() tuple. + /// This argument should be supplied by the + /// application only if start_response is + /// being called by an error + /// @return The write() method \ref python_xbmcwsgi_WsgiResponseBody "WsgiResponseBody" + /// + operator(...); +#else + WsgiResponseBody* operator()(const String& status, const std::vector<WsgiHttpHeader>& response_headers, void* exc_info = NULL); +#endif + +#ifndef SWIG + void Append(const std::string& data); + + bool Finalize(HTTPPythonRequest* request) const; + + private: + bool m_called = false; + int m_status = MHD_HTTP_INTERNAL_SERVER_ERROR; + std::multimap<std::string, std::string> m_responseHeaders; + + WsgiResponseBody m_body; +#endif + }; + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.cpp b/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.cpp new file mode 100644 index 0000000..2e84319 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WsgiResponseBody.h" + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + WsgiResponseBody::WsgiResponseBody() + : m_data() + { } + + WsgiResponseBody::~WsgiResponseBody() = default; + + void WsgiResponseBody::operator()(const String& data) + { + if (data.empty()) + return; + + m_data.append(data); + } + } +} diff --git a/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.h b/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.h new file mode 100644 index 0000000..4f18583 --- /dev/null +++ b/xbmc/interfaces/legacy/wsgi/WsgiResponseBody.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "interfaces/legacy/AddonClass.h" + +namespace XBMCAddon +{ + namespace xbmcwsgi + { + /// \defgroup python_xbmcwsgi_WsgiResponseBody WsgiResponseBody + /// \ingroup python_xbmcwsgi + /// @{ + /// @brief **Represents the write callable returned by the start_response callable passed to a WSGI handler.** + /// + /// \python_class{ WsgiResponseBody() } + /// + ///------------------------------------------------------------------------- + /// + class WsgiResponseBody : public AddonClass + { + public: + WsgiResponseBody(); + ~WsgiResponseBody() override; + +#ifdef DOXYGEN_SHOULD_USE_THIS + /// \ingroup python_xbmcwsgi_WsgiInputStreamIterator + /// \python_func{ operator(status, response_headers[, exc_info]) } + /// + /// Callable implementation to write data to the response. + /// + /// @param data string data to write + /// + operator()(...); +#else + void operator()(const String& data); +#endif + +#if !defined SWIG && !defined DOXYGEN_SHOULD_SKIP_THIS + String m_data; +#endif + }; + } +} |