diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 1199de9..1548b40 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -27,7 +27,7 @@ namespace boost using namespace MOBase; -#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_(class_name, include_requirements) \ bool class_name::init(MOBase::IOrganizer *moInfo) \ { \ return basicWrapperFunctionImplementation(this, "init", boost::python::ptr(moInfo)); \ @@ -63,17 +63,21 @@ MOBase::VersionInfo class_name::version() const \ return basicWrapperFunctionImplementation(this, "version"); \ } \ \ -bool class_name::isActive() const \ -{ \ - return basicWrapperFunctionImplementation(this, "isActive"); \ -} \ - \ QList class_name::settings() const \ { \ return basicWrapperFunctionImplementation>(this, "settings"); \ } \ QString class_name::localizedName_Default() const { return IPlugin::localizedName(); } \ -IPlugin* class_name::master_Default() const { return IPlugin::master(); } +IPlugin* class_name::master_Default() const { return IPlugin::master(); } \ +BOOST_PP_EXPR_IF(include_requirements, \ + QList class_name::requirements() const { \ + QList reqs = basicWrapperFunctionImplementationWithDefault>(this, &class_name::requirements_Default, "requirements"); \ + reqs.prepend(Requirements::pluginDependency("Python Proxy")); \ + return reqs; \ + } \ + QList class_name::requirements_Default() const { return IPlugin::requirements(); }) + +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_(class_name, 1) /// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS ///////////////////////////// @@ -293,7 +297,7 @@ QString IPluginGameWrapper::getLauncherName() const return basicWrapperFunctionImplementation(this, "getLauncherName"); } -COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginGameWrapper) +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS_(IPluginGameWrapper, 0) std::map IPluginGameWrapper::featureList() const { diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index f75424f..0b17a84 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -2,6 +2,7 @@ #define PROXYPLUGINWRAPPERS_H +#include #include #include #include @@ -16,7 +17,7 @@ #endif -#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: \ +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_(include_requirements) public: \ virtual bool init(MOBase::IOrganizer *moInfo) override; \ virtual QString name() const override; \ virtual QString localizedName() const override; \ @@ -24,10 +25,14 @@ virtual IPlugin* master() const override; \ virtual QString author() const override; \ virtual QString description() const override; \ virtual MOBase::VersionInfo version() const override; \ -virtual bool isActive() const override; \ virtual QList settings() const override; \ QString localizedName_Default() const; \ -IPlugin* master_Default() const; +IPlugin* master_Default() const; \ +BOOST_PP_EXPR_IF(include_requirements, \ + virtual QList requirements() const override; \ + QList requirements_Default() const;) + +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_(1) // Even though the base interface is not a QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. // QObject must be the first base class because moc assumes the first base class is a QObject @@ -126,7 +131,7 @@ public: virtual QString gameVersion() const override; virtual QString getLauncherName() const override; - COMMON_I_PLUGIN_WRAPPER_DECLARATIONS + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_(0) protected: // Apparently, Python developers interpret an underscore in a function name as it being protected diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index a500ad9..09b5db9 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -94,18 +94,18 @@ BOOST_PYTHON_MODULE(mobase) // Containers: utils::register_sequence_container>(); + utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); + utils::register_sequence_container>(); utils::register_sequence_container(); utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_sequence_container>(); // Required for QVariant since this is QVariantList. utils::register_sequence_container>>(); utils::register_sequence_container>(); - - utils::register_sequence_container>(); utils::register_sequence_container>(); utils::register_set_container>(); @@ -149,6 +149,7 @@ BOOST_PYTHON_MODULE(mobase) utils::register_functor_converter const&)>(); utils::register_functor_converter(QString const&)>(); utils::register_functor_converter>(); + utils::register_functor_converter>(); // This one is kept for backward-compatibility while we deprecate onModStateChanged for singl mod. utils::register_functor_converter(); // converter for the onModStateChanged-callback (IModList). @@ -261,6 +262,38 @@ BOOST_PYTHON_MODULE(mobase) Q_DELEGATE(ISaveGameInfoWidget, QWidget, "_widget") ; + // Plugin requirements: + bpy::class_, IPluginRequirement*, boost::noncopyable>("IPluginRequirement", bpy::no_init) + .def("problems", &IPluginRequirement::problems) + .def("description", &IPluginRequirement::description) + ; + + bpy::class_("PluginRequirementFactory") + // pluginDependency + .def("pluginDependency", +[](QStringList const& pluginNames) { + return PluginRequirementFactory::pluginDependency(pluginNames); + }, bpy::return_value_policy(), bpy::arg("plugins")) + .def("pluginDependency", +[](QString const& pluginName) { + return PluginRequirementFactory::pluginDependency(pluginName); + }, bpy::return_value_policy(), bpy::arg("plugin")) + .staticmethod("pluginDependency") + // gameDependency + .def("gameDependency", +[](QStringList const& gameNames) { + return PluginRequirementFactory::gameDependency(gameNames); + }, bpy::return_value_policy(), bpy::arg("games")) + .def("gameDependency", +[](QString const& gameNames) { + return PluginRequirementFactory::gameDependency(gameNames); + }, bpy::return_value_policy(), bpy::arg("game")) + .staticmethod("gameDependency") + // diagnose + .def("diagnose", &PluginRequirementFactory::diagnose, + bpy::return_value_policy(), bpy::arg("diagnose")) + .staticmethod("diagnose") + // basic + .def("basic", &PluginRequirementFactory::basic, + bpy::return_value_policy(), (bpy::arg("checker"), "description")) + .staticmethod("basic"); + bpy::class_("FileInfo", bpy::init<>()) .def_readwrite("filePath", &IOrganizer::FileInfo::filePath) .def_readwrite("archive", &IOrganizer::FileInfo::archive) @@ -279,6 +312,8 @@ BOOST_PYTHON_MODULE(mobase) .def("createMod", &IOrganizer::createMod, bpy::return_value_policy(), bpy::arg("name")) .def("getGame", &IOrganizer::getGame, bpy::return_value_policy(), bpy::arg("name")) .def("modDataChanged", &IOrganizer::modDataChanged, bpy::arg("mod")) + .def("isPluginEnabled", +[](IOrganizer* o, IPlugin* plugin) { return o->isPluginEnabled(plugin); }, bpy::arg("plugin")) + .def("isPluginEnabled", +[](IOrganizer* o, QString const& plugin) { return o->isPluginEnabled(plugin); }, bpy::arg("plugin")) .def("pluginSetting", &IOrganizer::pluginSetting, (bpy::arg("plugin_name"), "key")) .def("setPluginSetting", &IOrganizer::setPluginSetting, (bpy::arg("plugin_name"), "key", "value")) .def("persistent", &IOrganizer::persistent, (bpy::arg("plugin_name"), "key", bpy::arg("default") = QVariant())) @@ -771,7 +806,7 @@ BOOST_PYTHON_MODULE(mobase) .def("onModMoved", &MOBase::IModList::onModMoved, bpy::arg("callback")) ; - // Note: localizedName() and master() have to go in all the plugin wrappers declaration, + // Note: localizedName(), master() and requirements have to go in all the plugin wrappers declaration, // since the default functions are specific to each wrapper, otherwise in turns into an // infinite recursion mess. bpy::class_("IPlugin") @@ -782,13 +817,14 @@ BOOST_PYTHON_MODULE(mobase) .def("author", bpy::pure_virtual(&MOBase::IPlugin::author)) .def("description", bpy::pure_virtual(&MOBase::IPlugin::description)) .def("version", bpy::pure_virtual(&MOBase::IPlugin::version)) - .def("isActive", bpy::pure_virtual(&MOBase::IPlugin::isActive)) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginWrapper::requirements_Default) .def("settings", bpy::pure_virtual(&MOBase::IPlugin::settings)) ; bpy::class_, boost::noncopyable>("IPluginDiagnose") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginDiagnoseWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginDiagnoseWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginDiagnoseWrapper::requirements_Default) .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription), bpy::arg("key")) @@ -815,6 +851,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginFileMapper") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginFileMapperWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginFileMapperWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginFileMapperWrapper::requirements_Default) .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) ; @@ -947,6 +984,7 @@ BOOST_PYTHON_MODULE(mobase) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerSimpleWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginInstallerSimpleWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerSimpleWrapper::requirements_Default) // Note: Keeping the variant here even if we always return a tuple to be consistent with the wrapper and // have proper stubs generation. @@ -964,6 +1002,7 @@ BOOST_PYTHON_MODULE(mobase) .def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, (bpy::arg("result"), bpy::arg("new_mod"))) .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginInstallerCustomWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginInstallerCustomWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginInstallerCustomWrapper::requirements_Default) // Needs to add both otherwize boost does not understand: .def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, bpy::arg("tree")) @@ -977,6 +1016,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginModPage") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginModPageWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginModPageWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginModPageWrapper::requirements_Default) .def("displayName", bpy::pure_virtual(&IPluginModPage::displayName)) .def("icon", bpy::pure_virtual(&IPluginModPage::icon)) @@ -990,6 +1030,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginPreview") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginPreviewWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginPreviewWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginPreviewWrapper::requirements_Default) .def("supportedExtensions", bpy::pure_virtual(&IPluginPreview::supportedExtensions)) .def("genFilePreview", bpy::pure_virtual(&IPluginPreview::genFilePreview), bpy::return_value_policy(), @@ -999,6 +1040,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_, boost::noncopyable>("IPluginTool") .def("localizedName", &MOBase::IPlugin::localizedName, &IPluginToolWrapper::localizedName_Default) .def("master", &MOBase::IPlugin::master, &IPluginToolWrapper::master_Default, bpy::return_value_policy()) + .def("requirements", &MOBase::IPlugin::requirements, &IPluginToolWrapper::requirements_Default) .def("displayName", bpy::pure_virtual(&IPluginTool::displayName)) .def("tooltip", bpy::pure_virtual(&IPluginTool::tooltip)) diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index a032853..e572e34 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -16,12 +16,25 @@ #include #include #include +#include #include "error.h" #include "gilock.h" #include "pythonwrapperutilities.h" -// This needs to be extendable in Python, so actually needs a wrapper (everything else probably doesn't): +// This can be extended in C++, so why not in Python: +class IPluginRequirementWrapper : public MOBase::IPluginRequirement, public boost::python::wrapper +{ +public: + static constexpr const char* className = "IPluginRequirement"; + using boost::python::wrapper::get_override; + + virtual std::vector problems(MOBase::IOrganizer *o) const override { + return basicWrapperFunctionImplementation>(this, "problems", boost::python::ptr(o)); }; + virtual QString description(unsigned int id) const override { return basicWrapperFunctionImplementation(this, "getCreationTime", id); }; +}; + +// This needs to be extendable in Python, so actually needs a wrapper: class ISaveGameWrapper : public MOBase::ISaveGame, public boost::python::wrapper { public: