Adds operator handlers to operators. #117912

Open
Jaume Bellet wants to merge 2 commits from JaumeBellet/blender:pr-0011-operator-handlers into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
14 changed files with 994 additions and 4 deletions

View File

@ -13,8 +13,27 @@ _op_as_string = _ops_module.as_string
_op_get_rna_type = _ops_module.get_rna_type
_op_get_bl_options = _ops_module.get_bl_options
_op_handlers = _ops_module.handlers
_ModuleType = type(_ops_module)
class handler_action:
def __init__(self, mod, append_func, remove_func):
self._mod = mod
self._append_func = append_func
self._remove_func = remove_func
def append(self,cb, owner = None, args = None, poll = None):
#is there a way to remove self from console show?
self._append_func(owner=owner, op = self._mod.idname(), cb=cb, args=args, poll = poll)
def remove(self, cb = None, owner = None):
self._remove_func(owner=owner, op = self._mod.idname(), cb=cb, args=None, poll = None)
def remove_handlers(owner = None, cb = None):
_op_handlers.remove(owner = owner, cb = cb, op = None, args = None, poll = None)
# -----------------------------------------------------------------------------
# Callable Operator Wrapper
@ -26,7 +45,14 @@ class _BPyOpsSubModOp:
eg. bpy.ops.object.somefunc
"""
__slots__ = ("_module", "_func")
__slots__ = ("_module", "_func", "handlers")
class _handlers:
def __init__(self, mod):
self.invoke_pre = handler_action(mod, _op_handlers.pre_invoke, _op_handlers.pre_invoke_remove)
self.invoke_post = handler_action(mod, _op_handlers.post_invoke, _op_handlers.post_invoke_remove)
self.modal = handler_action(mod, _op_handlers.modal, _op_handlers.modal_remove)
self.modal_end = handler_action(mod, _op_handlers.modal_end, _op_handlers.modal_end_remove)
def _get_doc(self):
idname = self.idname()
@ -77,6 +103,7 @@ class _BPyOpsSubModOp:
def __init__(self, module, func):
self._module = module
self._func = func
self.handlers = self._handlers(self)
def poll(self, *args):
C_exec, _C_undo = _BPyOpsSubModOp._parse_args(args)
@ -178,4 +205,6 @@ def __dir__():
else:
submodules.add(id_split[0])
submodules.add("remove_handlers")
return list(submodules)

View File

@ -69,6 +69,7 @@ struct View3D;
struct ViewLayer;
struct wmGizmoGroup;
struct wmMsgBus;
struct wmOpHandlers;
struct wmWindow;
struct wmWindowManager;
struct WorkSpace;
@ -191,6 +192,7 @@ void *CTX_wm_region_data(const bContext *C);
ARegion *CTX_wm_menu(const bContext *C);
wmGizmoGroup *CTX_wm_gizmo_group(const bContext *C);
wmMsgBus *CTX_wm_message_bus(const bContext *C);
wmOpHandlers *CTX_wm_op_handlers(const bContext *C);
ReportList *CTX_wm_reports(const bContext *C);
View3D *CTX_wm_view3d(const bContext *C);

View File

@ -763,6 +763,11 @@ wmMsgBus *CTX_wm_message_bus(const bContext *C)
return C->wm.manager ? C->wm.manager->message_bus : nullptr;
}
struct wmOpHandlers *CTX_wm_op_handlers(const bContext *C)
{
return C->wm.manager ? C->wm.manager->op_handlers : nullptr;
}
ReportList *CTX_wm_reports(const bContext *C)
{
if (C->wm.manager) {

View File

@ -213,6 +213,8 @@ typedef struct wmWindowManager {
struct wmMsgBus *message_bus;
struct wmOpHandlers *op_handlers;
// #ifdef WITH_XR_OPENXR
wmXrData xr;
// #endif

View File

@ -36,6 +36,7 @@
#include "RNA_prototypes.h"
#include "WM_api.hh"
#include "WM_op_handlers.h"
#include "WM_types.hh"
#include "MEM_guardedalloc.h"
@ -460,11 +461,493 @@ static PyModuleDef bpy_ops_module = {
/*m_free*/ nullptr,
};
static int bpy_op_handler_check(void *py_data, void* owner, void *callback)
{
PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
PyObject *py_callback = PyTuple_GET_ITEM(py_data, 2);
if (owner != NULL && callback != NULL) {
return (py_owner == owner && py_callback == callback);
}
else if (owner != NULL)
{
return (py_owner == owner);
}
else { //callback != NULL
return (py_callback == callback);
}
}
/*
* @Properties Rna Properties that has been used to set the operator
*/
static PyObject *bpy_op_get_operator_params(PointerRNA *properties)
{
const char *arg_name = NULL;
PyObject *py_dict = PyDict_New();
PyObject *data;
RNA_STRUCT_BEGIN (properties, prop) {
arg_name = RNA_property_identifier(prop);
data = NULL;
if (STREQ(arg_name, "rna_type")) {
continue;
}
switch (RNA_property_type(prop)) {
case PROP_BOOLEAN: {
bool val = RNA_property_boolean_get(properties, prop);
// from Py Docs, Py_False and Py_truee needs to be treated just like any other object with
// respect to reference counts.
data = val ? Py_False : Py_True;
break;
}
case PROP_INT: {
const int prop_array_length = RNA_property_array_length(properties, prop);
if (prop_array_length == 0) {
int val = RNA_property_int_get(properties, prop);
data = PyLong_FromLong(val);
}
else {
int *values = (int*) MEM_callocN(sizeof(int) * prop_array_length, __func__);
RNA_property_int_get_array(properties, prop, values);
data = PyTuple_New(prop_array_length);
for (int i = 0; i < prop_array_length; i++) {
PyObject *py_val = PyLong_FromLong(*(values + i));
PyTuple_SET_ITEM(data, i, py_val);
}
MEM_freeN(values);
}
break;
}
case PROP_FLOAT: {
const int prop_array_length = RNA_property_array_length(properties, prop);
if (prop_array_length == 0) {
float val;
val = RNA_property_float_get(properties, prop);
data = PyFloat_FromDouble(val);
}
else {
float *values = (float*) MEM_callocN(sizeof(float) * prop_array_length, __func__);
RNA_property_float_get_array(properties, prop, values);
data = PyTuple_New(prop_array_length);
for (int i = 0; i < prop_array_length; i++) {
PyObject *py_val = PyFloat_FromDouble(*(values + i));
PyTuple_SET_ITEM(data, i, py_val);
}
MEM_freeN(values);
}
break;
}
case PROP_STRING: {
char buff[256];
char *value = RNA_property_string_get_alloc(properties, prop, buff, sizeof(buff), NULL);
data = PyUnicode_FromString(value);
if (value != buff) {
MEM_freeN(value);
}
break;
}
case PROP_ENUM: {
int val = RNA_property_enum_get(properties, prop);
data = PyLong_FromLong(val);
break;
}
case PROP_POINTER: {
data = PyUnicode_FromString("POINTER");
break;
}
case PROP_COLLECTION: {
data = PyUnicode_FromString("COLLECTION");
break;
}
default:
BLI_assert(false);
}
if (data != NULL) {
PyDict_SetItemString(py_dict, arg_name, data);
}
}
RNA_STRUCT_END;
return py_dict;
}
static bool bpy_op_callback_get_return_value(PyObject *callback, PyObject *py_ret)
{
bool ret = true; // Do not interrump on error
if (py_ret == NULL) {
PyC_Err_PrintWithFunc(callback);
}
else {
if (py_ret == Py_None) {
// pass
}
else if (py_ret == Py_True) {
// pass
}
else if (py_ret == Py_False){
ret = false;
} else {
PyErr_SetString(PyExc_ValueError, "the return value must be None or boolean");
PyC_Err_PrintWithFunc(callback);
}
Py_DECREF(py_ret);
}
return ret;
}
static PyObject *bpy_op_get_callback_call(PyObject *callback,
bContext *C, const wmEvent *event, int *operator_ret, PyObject *params, PyObject *callback_args)
{
PointerRNA ctx_ptr;
PointerRNA event_ptr;
PyObject *bpy_ctx;
PyObject *bpy_event;
PyObject *py_ret;
ctx_ptr = RNA_pointer_create(nullptr, &RNA_Context, C);
bpy_ctx = pyrna_struct_CreatePyObject(&ctx_ptr);
if (event != NULL) {
event_ptr = RNA_pointer_create(NULL, &RNA_Event, (void*) event);
bpy_event = pyrna_struct_CreatePyObject(&event_ptr);
}
else {
bpy_event = Py_None;
}
int s = (operator_ret == NULL) ? 3 : 4;
int c = (callback_args == Py_None) ? s : PyTuple_GET_SIZE(callback_args) + s;
PyObject *func_args = PyTuple_New(c);
PyTuple_SET_ITEM(func_args, 0, bpy_ctx);
PyTuple_SET_ITEM(func_args, 1, bpy_event);
PyTuple_SET_ITEM(func_args, 2, params);
if (operator_ret != NULL) {
PyObject *op_ret = pyrna_enum_bitfield_to_py(rna_enum_operator_return_items, *operator_ret);
PyTuple_SET_ITEM(func_args, 3, op_ret);
}
for (int i = s; i < c; i++) {
PyTuple_SET_ITEM(func_args, i, PyTuple_GET_ITEM(callback_args, i - s));
}
py_ret = PyObject_CallObject(callback, func_args);
Py_DECREF(func_args);
return py_ret;
}
static bool bpy_op_handler_poll(struct bContext *C,
const wmEvent *event, void *py_data,
PointerRNA *properties)
{
bool ret = true;
PyGILState_STATE gilstate; // this is because is not thread safe
bpy_context_set(C, &gilstate);
{
PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
PyObject *py_poll = PyTuple_GET_ITEM(py_data, 4);
// Properties get null on modall poll, params are not bypassed to Py poll function
PyObject *params = (properties == NULL) ? Py_None : bpy_op_get_operator_params(properties);
if (py_poll != Py_None) {
PyObject *py_ret = bpy_op_get_callback_call(py_poll, C, event, NULL, params, callback_args);
if (py_ret == NULL) {
// Error
PyErr_Print();
return false;
}
else if (py_ret == Py_True) {
ret = true;
Py_DECREF(py_ret);
}
else if (py_ret == Py_False) {
ret = false;
Py_DECREF(py_ret);
}
else {
ret = false;
Py_DECREF(py_ret);
PyErr_SetString(PyExc_ValueError, "the return value must be boolean");
PyC_Err_PrintWithFunc(py_poll);
}
}
// Py_DECREF(params);
}
bpy_context_clear(C, &gilstate);
return ret;
}
static bool bpy_op_handler_modal (bContext *C, const wmEvent *event, void *py_data, PointerRNA *properties, int operator_ret)
{
// this is because is not thread safe
bool ret = true;
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
{
PyObject *callback = PyTuple_GET_ITEM(py_data, 2);
PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
PyObject *py_ret = bpy_op_get_callback_call(callback, C, event, &operator_ret, Py_None, callback_args);
ret = bpy_op_callback_get_return_value(callback, py_ret);
}
bpy_context_clear(C, &gilstate);
return ret;
}
static bool bpy_op_handler_invoke(bContext *C,
const wmEvent *event,
void *py_data,
PointerRNA *properties,
int operator_ret)
{
bool ret = true;
// this is because is not thread safe
PyGILState_STATE gilstate;
bpy_context_set(C, &gilstate);
{
PyObject *callback = PyTuple_GET_ITEM(py_data, 2);
PyObject *callback_args = PyTuple_GET_ITEM(py_data, 3);
PyObject *params = bpy_op_get_operator_params(properties);
PyObject *py_ret = bpy_op_get_callback_call(callback, C, event, operator_ret ? &operator_ret : NULL , params, callback_args);
ret = bpy_op_callback_get_return_value(callback, py_ret);
}
bpy_context_clear(C, &gilstate);
return ret;
}
static PyObject *bpy_op_handler_proc(
PyObject *args,
PyObject *kw)
{
const char *error_prefix = "op_handler_proc";
PyObject *py_op = NULL;
PyObject *py_owner = NULL; // Object who creates the handler
PyObject *callback = NULL, *py_poll = NULL;
PyObject *callback_args = NULL;
if (PyTuple_GET_SIZE(args) != 0) {
PyErr_Format(PyExc_TypeError, "%s: only keyword arguments are supported", error_prefix);
}
// see https://docs.python.org/3/c-api/arg.html
static const char *_keywords[] = {"owner", "op", "cb", "args", "poll", NULL};
static _PyArg_Parser _parser = {"OOOOO|:handler_proc", _keywords, 0};
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &py_owner, &py_op, &callback, &callback_args, &py_poll)) {
PyErr_SetString(
PyExc_TypeError, "Cannot set arguments, or types does not match");
}
if (callback != Py_None && !PyFunction_Check(callback)) {
// Callback may be none on remove
PyErr_Format(
PyExc_TypeError, "callback expects a function, found %.200s", Py_TYPE(callback)->tp_name);
}
if (py_poll != Py_None && !PyFunction_Check(py_poll)) {
// Callback may be none on remove
PyErr_Format(
PyExc_TypeError, "poll expects a function, found %.200s", Py_TYPE(callback)->tp_name);
}
if (py_op != Py_None && !PyUnicode_Check(py_op)) {
PyErr_Format(
PyExc_TypeError, "op expects an astring, found %.200s", Py_TYPE(py_op)->tp_name);
}
if (PyErr_Occurred() != NULL) {
PyErr_Print();
return NULL;
}
PyObject *py_data = PyTuple_New(5);
PyTuple_SET_ITEMS(py_data,
Py_INCREF_RET(py_owner), // 0
Py_INCREF_RET(py_op), // 1
Py_INCREF_RET(callback), // 2
Py_INCREF_RET(callback_args), // 3
Py_INCREF_RET(py_poll)); // 4
return Py_INCREF_RET(py_data);
}
static PyObject *op_handler_append(int handler_id , PyObject *args, PyObject *kw)
{
bContext *C = BPY_context_get();
struct wmOpHandlers *op_handlers = CTX_wm_op_handlers(C);
PyObject *py_data = bpy_op_handler_proc(args, kw);
bool (*func)(bContext * C,const wmEvent * event, void *, PointerRNA *, int) = nullptr;
switch (handler_id) {
case HANDLER_TYPE_PRE_INVOKE:
case HANDLER_TYPE_POST_INVOKE:
func = bpy_op_handler_invoke;
break;
case HANDLER_TYPE_MODAL:
case HANDLER_TYPE_MODAL_END:
func = bpy_op_handler_modal;
break;
}
if (py_data != NULL) {
PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
PyObject *py_op = PyTuple_GET_ITEM(py_data, 1);
PyObject *py_callback = PyTuple_GET_ITEM(py_data, 2);
PyObject *py_poll = PyTuple_GET_ITEM(py_data, 4);
if (py_op == Py_None) {
PyErr_Format(PyExc_TypeError, "missing operator");
}
else if (py_callback == Py_None) {
PyErr_Format(PyExc_TypeError, "callback expects a function");
} else {
WM_op_handlers_append(op_handlers,
handler_id,
py_owner,
PyUnicode_AsUTF8(py_op),
func,
bpy_op_handler_check,
py_poll == Py_None ? NULL : bpy_op_handler_poll,
py_data);
}
}
if (PyErr_Occurred() != NULL) {
PyErr_Print();
}
Py_RETURN_NONE;
}
static PyObject *op_handler_remove(int handler_id, PyObject *args, PyObject *kw)
{
bContext *C = BPY_context_get();
struct wmOpHandlers *op_handlers = CTX_wm_op_handlers(C);
PyObject *py_data = bpy_op_handler_proc(args, kw);
if (py_data != NULL) {
PyObject *py_owner = PyTuple_GET_ITEM(py_data, 0);
PyObject *py_op = PyTuple_GET_ITEM(py_data, 1);
PyObject *py_cb = PyTuple_GET_ITEM(py_data, 2);
if (py_owner == Py_None && py_cb == Py_None) {
PyErr_Format(PyExc_TypeError, "missing owner or callback");
} else {
if (WM_op_handlers_remove(op_handlers,
handler_id,
(py_op == Py_None ? NULL : PyUnicode_AsUTF8(py_op)),
(py_cb == Py_None ? NULL : py_cb),
(py_owner == Py_None ? NULL : py_owner)) == 0) {
PyErr_Format(PyExc_NameError, "data not found on %s", PyUnicode_AsUTF8(py_op));
}
}
}
if (PyErr_Occurred() != NULL) {
PyErr_Print();
}
Py_RETURN_NONE;
}
static PyObject *op_handler_append_pre_invoke(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_append(HANDLER_TYPE_PRE_INVOKE, args, kw);
}
static PyObject *op_handler_append_post_invoke(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_append(HANDLER_TYPE_POST_INVOKE, args, kw);
}
static PyObject *op_handler_append_modal(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_append(HANDLER_TYPE_MODAL, args, kw);
}
static PyObject *op_handler_append_modal_end(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_append(HANDLER_TYPE_MODAL_END, args, kw);
}
static PyObject *op_handler_remove_pre_invoke(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_remove(HANDLER_TYPE_PRE_INVOKE, args, kw);
}
static PyObject *op_handler_remove_post_invoke(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_remove(HANDLER_TYPE_POST_INVOKE, args, kw);
}
static PyObject *op_handler_remove_modal(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_remove(HANDLER_TYPE_MODAL, args, kw);
}
static PyObject *op_handler_remove_modal_end(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_remove(HANDLER_TYPE_MODAL_END, args, kw);
}
static PyObject *op_handlers_remove(PyObject *self, PyObject *args, PyObject *kw)
{
return op_handler_remove(HANDLER_TYPE_ALL, args, kw);
}
static struct PyMethodDef bpy_ops_handlers_methods[] = {
{"pre_invoke", (PyCFunction)op_handler_append_pre_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
{"post_invoke", (PyCFunction)op_handler_append_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
{"modal", (PyCFunction)op_handler_append_modal, METH_VARARGS | METH_KEYWORDS, NULL},
{"modal_end", (PyCFunction)op_handler_append_modal_end, METH_VARARGS | METH_KEYWORDS, NULL},
{"pre_invoke_remove", (PyCFunction)op_handler_remove_pre_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
{"post_invoke_remove", (PyCFunction)op_handler_remove_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
{"modal_remove",(PyCFunction)op_handler_remove_post_invoke, METH_VARARGS | METH_KEYWORDS, NULL},
{"modal_end_remove", (PyCFunction)op_handler_remove_modal_end, METH_VARARGS | METH_KEYWORDS, NULL},
{"remove", (PyCFunction)op_handlers_remove, METH_VARARGS | METH_KEYWORDS, NULL},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef bpy_ops_handlers = {
PyModuleDef_HEAD_INIT,
"_bpy.ops.handlers",
NULL,
-1, /* multiple "initialization" just copies the module dict. */
bpy_ops_handlers_methods,
NULL,
NULL,
NULL,
NULL,
};
PyObject *BPY_operator_module()
{
PyObject *submodule;
submodule = PyModule_Create(&bpy_ops_module);
PyObject *handlers = PyModule_Create(&bpy_ops_handlers);
Py_INCREF(handlers);
if (PyModule_AddObject(submodule, "handlers", handlers) < 0) {
Py_DECREF(submodule);
Py_DECREF(handlers);
return NULL;
}
return submodule;
}

View File

@ -1299,6 +1299,25 @@ static int pyrna_prop_to_enum_bitfield(
return ret;
}
PyObject *pyrna_enum_bitfield_to_py(const EnumPropertyItem *items, int value)
{
PyObject *ret = PySet_New(NULL);
const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1];
if (RNA_enum_bitflag_identifiers(items, value, identifier)) {
PyObject *item;
int index;
for (index = 0; identifier[index]; index++) {
item = PyUnicode_FromString(identifier[index]);
PySet_Add(ret, item);
Py_DECREF(item);
}
}
return ret;
}
static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val)
{
PyObject *item, *ret = nullptr;

View File

@ -189,6 +189,8 @@ PyObject *pyrna_id_CreatePyObject(struct ID *id);
bool pyrna_id_FromPyObject(PyObject *obj, struct ID **id);
bool pyrna_id_CheckPyObject(PyObject *obj);
PyObject *pyrna_enum_bitfield_to_py(const struct EnumPropertyItem *items, int value);
/* operators also need this to set args */
int pyrna_pydict_to_props(PointerRNA *ptr, PyObject *kw, bool all_args, const char *error_prefix);
PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop);

View File

@ -69,10 +69,12 @@ set(SRC
message_bus/intern/wm_message_bus.cc
message_bus/intern/wm_message_bus_rna.cc
message_bus/intern/wm_message_bus_static.cc
op_handlers/intern/wm_op_handlers.cc
WM_api.hh
WM_keymap.hh
WM_message.hh
WM_op_handlers.h
WM_toolsystem.hh
WM_types.hh
wm.hh
@ -92,6 +94,8 @@ set(SRC
gizmo/intern/wm_gizmo_intern.hh
message_bus/intern/wm_message_bus_intern.hh
message_bus/wm_message_bus.hh
op_handlers/wm_op_handlers.h
op_handlers/intern/wm_op_handlers_intern.h
)
set(LIB

View File

@ -0,0 +1,25 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*/
#pragma once
struct wmOpHandlers;
#include "op_handlers/wm_op_handlers.h"

View File

@ -41,6 +41,7 @@
#include "WM_api.hh"
#include "WM_message.hh"
#include "WM_op_handlers.h"
#include "WM_types.hh"
#include "wm.hh"
#include "wm_draw.hh"
@ -217,6 +218,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
wm->undo_stack = nullptr;
wm->message_bus = nullptr;
wm->op_handlers = nullptr;
wm->xr.runtime = nullptr;
@ -474,6 +476,10 @@ void WM_check(bContext *C)
wm->message_bus = WM_msgbus_create();
}
if (wm->op_handlers == NULL) {
wm->op_handlers = WM_op_handlers_create();
}
if (!G.background) {
/* Case: file-read. */
if ((wm->init_flag & WM_INIT_FLAG_WINDOW) == 0) {
@ -574,6 +580,11 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
WM_msgbus_destroy(wm->message_bus);
}
if (wm->op_handlers != nullptr) {
WM_op_handlers_destroy(wm->op_handlers);
}
#ifdef WITH_PYTHON
BPY_callback_wm_free(wm);
#endif

View File

@ -66,6 +66,7 @@
#include "WM_api.hh"
#include "WM_message.hh"
#include "WM_op_handlers.h"
#include "WM_toolsystem.hh"
#include "WM_types.hh"
@ -1510,6 +1511,12 @@ static int wm_operator_invoke(bContext *C,
return WM_operator_poll(C, ot);
}
// Ensure any change is processed by poll
if (WM_op_handlers_operator_pre_invoke(C, event, CTX_wm_op_handlers(C), ot, properties) ==
false) {
return OPERATOR_FINISHED;
}
if (WM_operator_poll(C, ot)) {
wmWindowManager *wm = CTX_wm_manager(C);
const intptr_t undo_id_prev = wm_operator_undo_active_id(wm);
@ -1550,6 +1557,8 @@ static int wm_operator_invoke(bContext *C,
retval = op->type->invoke(C, op, &event_temp);
OPERATOR_RETVAL_CHECK(retval);
WM_op_handlers_operator_post_invoke(C, event, CTX_wm_op_handlers(C), ot, op->ptr, retval);
if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
}
@ -1562,6 +1571,8 @@ static int wm_operator_invoke(bContext *C,
retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval);
WM_op_handlers_operator_post_invoke(C, event, CTX_wm_op_handlers(C), ot, op->ptr, retval);
if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
}
@ -2476,9 +2487,20 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
wm->op_undo_depth++;
}
/* Warning, after this call all context data and 'event' may be freed. see check below. */
retval = ot->modal(C, op, event);
OPERATOR_RETVAL_CHECK(retval);
if ( (WM_get_op_handlers(CTX_wm_op_handlers(C), ot->idname) != NULL) && !ot->poll(C)) {
// Py Handler, changing poll conditions
retval = OPERATOR_CANCELLED;
}
else {
/* Warning, after this call all context data and 'event' may be freed. see check below. */
retval = ot->modal(C, op, event);
OPERATOR_RETVAL_CHECK(retval);
if (WM_op_handlers_operator_modal(C, event, CTX_wm_op_handlers(C), ot, retval) == false) {
retval = OPERATOR_CANCELLED;
}
}
if (ot->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) {
wm->op_undo_depth--;
@ -2492,6 +2514,9 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
wm_event_modalkeymap_end(event, &event_backup);
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
WM_op_handlers_operator_modal_end(C, nullptr, CTX_wm_op_handlers(C), ot, retval);
wm_operator_reports(C, op, retval, false);
wmOperator *op_test = handler->op->opm ? handler->op->opm : handler->op;

View File

@ -0,0 +1,246 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*/
#include <string.h>
#include "MEM_guardedalloc.h"
#include "DNA_windowmanager_types.h"
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "BLI_string.h"
#include "WM_types.hh"
#include "op_handlers/intern/wm_op_handlers_intern.h"
#include "op_handlers/wm_op_handlers.h"
/* -------------------------------------------------------------------------- */
/** \name Public API
* \{ */
struct wmOpHandlers *WM_op_handlers_create(void)
{
struct wmOpHandlers *op_handlers = (wmOpHandlers*) MEM_callocN(sizeof(*op_handlers), __func__);
return op_handlers;
}
void WM_op_handlers_destroy(struct wmOpHandlers *op_handlers)
{
LISTBASE_FOREACH (wmOpHandlerData *, opHandlers, &op_handlers->handlers ) {
BLI_freelistN(&opHandlers->pre_invoke);
BLI_freelistN(&opHandlers->post_invoke);
BLI_freelistN(&opHandlers->modal);
BLI_freelistN(&opHandlers->modal_end);
}
BLI_freelistN(&op_handlers->handlers);
MEM_freeN(op_handlers);
op_handlers = NULL;
}
ListBase *WM_op_handlers_get_handler_list(wmOpHandlerData *opHandlers, int id)
{
ListBase *list = NULL;
switch (id) {
case HANDLER_TYPE_PRE_INVOKE:
list = &opHandlers->pre_invoke;
break;
case HANDLER_TYPE_POST_INVOKE:
list = &opHandlers->post_invoke;
break;
case HANDLER_TYPE_MODAL:
list = &opHandlers->modal;
break;
case HANDLER_TYPE_MODAL_END:
list = &opHandlers->modal_end;
break;
default:
BLI_assert(false);
}
return list;
}
wmOpHandlerData *WM_get_op_handlers(struct wmOpHandlers *op_handlers, const char *op_name)
{
return (wmOpHandlerData*) BLI_findstring(&op_handlers->handlers, op_name, offsetof(wmOpHandlerData, id_name));
}
void WM_op_handlers_append(struct wmOpHandlers *op_handlers,
int id,
void *py_handle,
const char *op_name,
bool (*cb)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties, int),
int (*check)(void *, void*, void*),
bool (*poll)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties),
void *py_data)
{
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, op_name);
wmHandlerData *data = (wmHandlerData *)MEM_mallocN(sizeof(wmHandlerData), "wmHandlerData");
if (opHandlers == NULL) {
// Create
opHandlers = (wmOpHandlerData *) MEM_callocN(sizeof(wmOpHandlerData), "wmOpHandlerData");
BLI_strncpy(opHandlers->id_name, op_name, OP_MAX_TYPENAME);
BLI_addtail(&op_handlers->handlers, opHandlers);
}
data->py_handle = py_handle;
data->id_name = opHandlers->id_name;
data->cb = cb;
data->check = check;
data->poll = poll;
data->py_data = py_data;
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, id);
BLI_addtail(list, data);
}
int WM_op_handlers_remove_all(struct wmOpHandlers *op_handlers, void *cb, void *owner)
{
int ret = 0;
LISTBASE_FOREACH (wmOpHandlerData *, opHandlers, &op_handlers->handlers) {
ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_PRE_INVOKE, opHandlers->id_name, cb, owner);
ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_POST_INVOKE, opHandlers->id_name, cb, owner);
ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_MODAL, opHandlers->id_name, cb, owner);
ret += WM_op_handlers_remove(op_handlers, HANDLER_TYPE_MODAL_END, opHandlers->id_name, cb, owner);
}
return ret;
}
int WM_op_handlers_remove(
struct wmOpHandlers *op_handlers, int id, const char *op_name, void *cb, void *owner)
{
int ret = 0;
if (id == HANDLER_TYPE_ALL) {
ret = WM_op_handlers_remove_all(op_handlers, cb, owner);
}
else {
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, op_name);
if (opHandlers != NULL) {
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, id);
wmHandlerData *next;
for (wmHandlerData *data = (wmHandlerData*) list->first; data; data = next) {
next = data->next;
if (data->check(data->py_data, owner, cb)) {
BLI_freelinkN(list, data);
ret++;
}
}
}
}
return ret;
}
bool WM_op_handlers_operator_exec(struct bContext *C,
const wmEvent *event,
ListBase *list,
struct wmOperatorType *ot,
struct PointerRNA *properties,
int retval)
{
bool ret = true;
LISTBASE_FOREACH (wmHandlerData *, data, list) {
if (data->poll == nullptr || data->poll(C, event, data->py_data, properties)) {
ret = ret && data->cb(C, event, data->py_data, properties, retval);
}
}
return ret;
}
// Previous to invoke
bool WM_op_handlers_operator_pre_invoke(struct bContext *C,
const wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
PointerRNA *properties)
{
bool ret = true;
if (op_handlers != NULL) {
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
if (opHandlers != NULL) {
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_PRE_INVOKE);
ret = ret && WM_op_handlers_operator_exec(C, event, list, ot, properties, 0);
}
}
return ret;
}
// Post To invoke
void WM_op_handlers_operator_post_invoke(struct bContext *C,
const wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
struct PointerRNA *properties,
int retval)
{
if (op_handlers != NULL) {
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
if (opHandlers != NULL) {
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_POST_INVOKE);
WM_op_handlers_operator_exec(C, event, list, ot, properties, retval);
}
}
}
bool WM_op_handlers_operator_modal(struct bContext *C,
const wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
int retval
)
{
bool ret = true;
if (op_handlers != NULL) {
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
if (opHandlers != NULL) {
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_MODAL);
ret = ret && WM_op_handlers_operator_exec(C, event, list, ot, NULL, retval);
}
}
return ret;
}
void WM_op_handlers_operator_modal_end(struct bContext *C,
const wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
int retval)
{
if (op_handlers != NULL) {
wmOpHandlerData *opHandlers = WM_get_op_handlers(op_handlers, ot->idname);
if (opHandlers != NULL) {
ListBase *list = WM_op_handlers_get_handler_list(opHandlers, HANDLER_TYPE_MODAL_END);
WM_op_handlers_operator_exec(C, event, list, ot, NULL, retval);
}
}
}
/** \} */

View File

@ -0,0 +1,36 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*/
#pragma once
#include "../wm_op_handlers.h"
typedef struct wmHandlerData {
struct wmHandlerData *next, *prev;
char *id_name; // Pointer to wmOpHandlerData.id_name
void *py_handle;
void *py_data;
bool (*cb)(struct bContext *, const wmEvent *event, void *, PointerRNA *properties, int); // callback
int (*check)(void *, void*, void*);
bool (*poll)(struct bContext *, const wmEvent *event, void *, PointerRNA *properties);
} wmHandlerData;

View File

@ -0,0 +1,101 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup wm
*/
#pragma once
#include <stdio.h>
#include "RNA_types.hh"
#define HANDLER_TYPE_ALL 0
#define HANDLER_TYPE_PRE_INVOKE 1
#define HANDLER_TYPE_POST_INVOKE 2
#define HANDLER_TYPE_MODAL 3
#define HANDLER_TYPE_MODAL_END 4
#ifdef __cplusplus
extern "C" {
#endif
struct wmOpHandlers {
/** Handlers in order of being added. */
ListBase handlers;
};
typedef struct wmOpHandlerData {
struct wmOpHandlerData *next, *prev;
char id_name[OP_MAX_TYPENAME];
/** Handlers in order of being added. */
ListBase pre_invoke;
ListBase post_invoke;
ListBase modal;
ListBase modal_end;
} wmOpHandlerData;
struct wmOpHandlers *WM_op_handlers_create(void);
void WM_op_handlers_destroy(struct wmOpHandlers *opHandlers);
wmOpHandlerData *WM_get_op_handlers(struct wmOpHandlers *op_handlers, const char *op_name);
void WM_op_handlers_append(struct wmOpHandlers *op_handlers, int id,
void *handle,
const char *op_name,
bool (*cb)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties, int),
int (*check)(void *, void*, void *),
bool (*poll)(struct bContext *, const struct wmEvent *event, void *, PointerRNA *properties),
void *py_data);
int WM_op_handlers_remove(struct wmOpHandlers *op_handlers,
int id,
const char *op_name,
void *cb,
void *owner);
bool WM_op_handlers_operator_pre_invoke(struct bContext *C,
const struct wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
PointerRNA *properties);
void WM_op_handlers_operator_post_invoke(struct bContext *C,
const struct wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
PointerRNA *properties, int retval);
bool WM_op_handlers_operator_modal(struct bContext *C,
const struct wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot, int retval);
void WM_op_handlers_operator_modal_end(struct bContext *C,
const struct wmEvent *event,
struct wmOpHandlers *op_handlers,
struct wmOperatorType *ot,
int retval);
#ifdef __cplusplus
}
#endif