From a09feb738627abfce71026352214e1512d40bcb8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 27 Jun 2012 21:41:17 +0000 Subject: [PATCH] option so operators can be executed with undo enabled (and redo). --- intern/guardedalloc/intern/mallocn.c | 4 +-- release/scripts/modules/bpy/ops.py | 36 +++++++++++-------- source/blender/python/intern/bpy_operator.c | 6 ++-- source/blender/windowmanager/WM_api.h | 6 ++-- .../windowmanager/intern/wm_event_system.c | 14 ++++---- .../bad_level_call_stubs/stubs.c | 2 +- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index f1a83583715..a234f31f87a 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -57,11 +57,11 @@ * lets you count the allocations so as to find the allocator of unfreed memory * in situations where the leak is predictable */ -// #define DEBUG_MEMCOUNTER +#define DEBUG_MEMCOUNTER #ifdef DEBUG_MEMCOUNTER /* set this to the value that isn't being freed */ -# define DEBUG_MEMCOUNTER_ERROR_VAL 0 +# define DEBUG_MEMCOUNTER_ERROR_VAL 34723 static int _mallocn_count = 0; /* breakpoint here */ diff --git a/release/scripts/modules/bpy/ops.py b/release/scripts/modules/bpy/ops.py index 056fcdb519e..c4e7e6ac19e 100644 --- a/release/scripts/modules/bpy/ops.py +++ b/release/scripts/modules/bpy/ops.py @@ -120,20 +120,28 @@ class BPyOpsSubModOp(object): def _parse_args(args): C_dict = None C_exec = 'EXEC_DEFAULT' + C_undo = False - if len(args) == 0: - pass - elif len(args) == 1: - if type(args[0]) != str: - C_dict = args[0] + is_dict = is_exec = is_undo = False + + for i, arg in enumerate(args): + if is_dict is False and isinstance(arg, dict): + if is_exec is True or is_undo is True: + raise ValueError("dict arg must come first") + C_dict = arg + is_dict = True + elif is_exec is False and isinstance(arg, str): + if is_undo is True: + raise ValueError("string arg must come before the boolean") + C_exec = arg + is_exec = True + elif is_undo is False and isinstance(arg, int): + C_undo = arg + is_undo = True else: - C_exec = args[0] - elif len(args) == 2: - C_exec, C_dict = args - else: - raise ValueError("1 or 2 args execution context is supported") + raise ValueError("1-3 args execution context is supported") - return C_dict, C_exec + return C_dict, C_exec, C_undo @staticmethod def _scene_update(context): @@ -152,7 +160,7 @@ class BPyOpsSubModOp(object): self.func = func def poll(self, *args): - C_dict, C_exec = BPyOpsSubModOp._parse_args(args) + C_dict, C_exec, C_undo = BPyOpsSubModOp._parse_args(args) return op_poll(self.idname_py(), C_dict, C_exec) def idname(self): @@ -174,8 +182,8 @@ class BPyOpsSubModOp(object): BPyOpsSubModOp._scene_update(context) if args: - C_dict, C_exec = BPyOpsSubModOp._parse_args(args) - ret = op_call(self.idname_py(), C_dict, kw, C_exec) + C_dict, C_exec, C_undo = BPyOpsSubModOp._parse_args(args) + ret = op_call(self.idname_py(), C_dict, kw, C_exec, C_undo) else: ret = op_call(self.idname_py(), None, kw) diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 10562271941..7375ad454a0 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -147,6 +147,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) /* note that context is an int, python does the conversion in this case */ int context = WM_OP_EXEC_DEFAULT; + int is_undo = FALSE; /* XXX Todo, work out a better solution for passing on context, * could make a tuple from self and pack the name and Context into it... */ @@ -157,7 +158,8 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - if (!PyArg_ParseTuple(args, "sO|O!s:_bpy.ops.call", &opname, &context_dict, &PyDict_Type, &kw, &context_str)) + if (!PyArg_ParseTuple(args, "sO|O!si:_bpy.ops.call", + &opname, &context_dict, &PyDict_Type, &kw, &context_str, &is_undo)) return NULL; ot = WM_operatortype_find(opname, TRUE); @@ -236,7 +238,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) PyThreadState *ts = PyEval_SaveThread(); #endif - operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports); + operator_ret = WM_operator_call_py(C, ot, context, &ptr, reports, is_undo); #ifdef BPY_RELEASE_GIL /* regain GIL */ diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index bfea3f3c40b..5011c785e90 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -193,13 +193,13 @@ struct wmOperatorTypeMacro *WM_operatortype_macro_define(struct wmOperatorType * int WM_operator_poll (struct bContext *C, struct wmOperatorType *ot); -int WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, int context); +int WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, short context); int WM_operator_call (struct bContext *C, struct wmOperator *op); int WM_operator_call_notest(struct bContext *C, struct wmOperator *op); int WM_operator_repeat (struct bContext *C, struct wmOperator *op); int WM_operator_repeat_check(const struct bContext *C, struct wmOperator *op); -int WM_operator_name_call (struct bContext *C, const char *opstring, int context, struct PointerRNA *properties); -int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, int context, struct PointerRNA *properties, struct ReportList *reports); +int WM_operator_name_call (struct bContext *C, const char *opstring, short context, struct PointerRNA *properties); +int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, short context, struct PointerRNA *properties, struct ReportList *reports, short is_undo); void WM_operator_properties_alloc(struct PointerRNA **ptr, struct IDProperty **properties, const char *opstring); /* used for keymap and macro items */ void WM_operator_properties_sanitize(struct PointerRNA *ptr, const short no_context); /* make props context sensitive or not */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index e55f7404798..9795c482af2 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -417,7 +417,7 @@ int WM_operator_poll(bContext *C, wmOperatorType *ot) } /* sets up the new context and calls 'wm_operator_invoke()' with poll_only */ -int WM_operator_poll_context(bContext *C, wmOperatorType *ot, int context) +int WM_operator_poll_context(bContext *C, wmOperatorType *ot, short context) { return wm_operator_call_internal(C, ot, NULL, NULL, context, TRUE); } @@ -1068,7 +1068,7 @@ static int wm_operator_call_internal(bContext *C, wmOperatorType *ot, PointerRNA /* invokes operator in context */ -int WM_operator_name_call(bContext *C, const char *opstring, int context, PointerRNA *properties) +int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties) { wmOperatorType *ot = WM_operatortype_find(opstring, 0); if (ot) @@ -1082,7 +1082,7 @@ int WM_operator_name_call(bContext *C, const char *opstring, int context, Pointe * - poll() must be called by python before this runs. * - reports can be passed to this function (so python can report them as exceptions) */ -int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA *properties, ReportList *reports) +int WM_operator_call_py(bContext *C, wmOperatorType *ot, short context, PointerRNA *properties, ReportList *reports, short is_undo) { int retval = OPERATOR_CANCELLED; @@ -1091,13 +1091,13 @@ int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA op = wm_operator_create(wm, ot, properties, reports); if (op->type->exec) { - if (op->type->flag & OPTYPE_UNDO) + if (is_undo && op->type->flag & OPTYPE_UNDO) wm->op_undo_depth++; retval = op->type->exec(C, op); OPERATOR_RETVAL_CHECK(retval); - if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) + if (is_undo && op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) wm->op_undo_depth--; } else @@ -1110,11 +1110,11 @@ int WM_operator_call_py(bContext *C, wmOperatorType *ot, int context, PointerRNA * we could have some more obvious way of doing this like passing a flag. */ wmWindowManager *wm = CTX_wm_manager(C); - if (wm) wm->op_undo_depth++; + if (!is_undo && wm) wm->op_undo_depth++; retval = wm_operator_call_internal(C, ot, properties, reports, context, FALSE); - if (wm && (wm == CTX_wm_manager(C))) wm->op_undo_depth--; + if (!is_undo && wm && (wm == CTX_wm_manager(C))) wm->op_undo_depth--; /* keep the reports around if needed later */ if ((retval & OPERATOR_RUNNING_MODAL) || diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 97fb9435180..53f3dacbffa 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -449,7 +449,7 @@ struct wmOperatorType *WM_operatortype_find(const char *idname, int quiet) {retu struct GHashIterator *WM_operatortype_iter() {return (struct GHashIterator *) NULL;} struct wmOperatorType *WM_operatortype_exists(const char *idname) {return (struct wmOperatorType *) NULL;} struct wmOperatorTypeMacro *WM_operatortype_macro_define(struct wmOperatorType *ot, const char *idname) {return (struct wmOperatorTypeMacro *) NULL;} -int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, int context, struct PointerRNA *properties, struct ReportList *reports) {return 0;} +int WM_operator_call_py(struct bContext *C, struct wmOperatorType *ot, short context, short is_undo, struct PointerRNA *properties, struct ReportList *reports) {return 0;} int WM_operatortype_remove(const char *idname) {return 0;} int WM_operator_poll(struct bContext *C, struct wmOperatorType *ot) {return 0;} int WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, int context) {return 0;}