Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDalai Felinto <dfelinto@gmail.com>2016-11-15 13:50:11 +0300
committerDalai Felinto <dfelinto@gmail.com>2016-11-15 16:25:10 +0300
commit69470e36d6b17042260b06f26ca3c2f702747324 (patch)
treeb09f43552eb90264eb8b59c83b3ff997cbff0e04
parent445274fc4fd8d563de585c27711f8385ce4174be (diff)
Implement grouped undo option for operators
This option makes an operator to not push a task to the undo stack if the previous stored elemen is the same operator or part of the same undo group. The main usage is for animation, so you can change frames to inspect the poses, and revert the previous pose without having to roll back tons of "change frame" operator, or even see the undo stack full. This complements rB13ee9b8e Design with help by Sergey Sharybin. Reviewers: sergey, mont29 Reviewed By: mont29, sergey Subscribers: pyc0d3r, hjalti, Severin, lowercase, brecht, monio, aligorith, hadrien, jbakker Differential Revision: https://developer.blender.org/D2330
-rw-r--r--source/blender/blenkernel/BKE_blender_undo.h1
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c7
-rw-r--r--source/blender/editors/animation/anim_ops.c4
-rw-r--r--source/blender/editors/include/ED_util.h2
-rw-r--r--source/blender/editors/screen/screen_ops.c12
-rw-r--r--source/blender/editors/util/undo.c23
-rw-r--r--source/blender/makesrna/intern/rna_wm.c46
-rw-r--r--source/blender/windowmanager/WM_types.h2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c4
9 files changed, 92 insertions, 9 deletions
diff --git a/source/blender/blenkernel/BKE_blender_undo.h b/source/blender/blenkernel/BKE_blender_undo.h
index 9547eeb9838..84a6d07be7d 100644
--- a/source/blender/blenkernel/BKE_blender_undo.h
+++ b/source/blender/blenkernel/BKE_blender_undo.h
@@ -42,6 +42,7 @@ extern bool BKE_undo_is_valid(const char *name);
extern void BKE_undo_reset(void);
extern void BKE_undo_number(struct bContext *C, int nr);
extern const char *BKE_undo_get_name(int nr, bool *r_active);
+extern const char *BKE_undo_get_name_last(void);
extern bool BKE_undo_save_file(const char *filename);
extern struct Main *BKE_undo_get_main(struct Scene **r_scene);
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
index d64bf7ecf43..ce6d29bbfee 100644
--- a/source/blender/blenkernel/intern/blender_undo.c
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -319,6 +319,13 @@ const char *BKE_undo_get_name(int nr, bool *r_active)
return NULL;
}
+/* return the name of the last item */
+const char *BKE_undo_get_name_last()
+{
+ UndoElem *uel = undobase.last;
+ return (uel ? uel->name : NULL);
+}
+
/**
* Saves .blend using undo buffer.
*
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index d7899061218..c0d6963acbb 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -57,6 +57,7 @@
#include "ED_anim_api.h"
#include "ED_screen.h"
#include "ED_sequencer.h"
+#include "ED_util.h"
#include "anim_intern.h"
@@ -263,7 +264,8 @@ static void ANIM_OT_change_frame(wmOperatorType *ot)
ot->poll = change_frame_poll;
/* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR | OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index f5968397f65..a4afa958450 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -52,6 +52,8 @@ void ED_OT_flush_edits(struct wmOperatorType *ot);
/* undo.c */
void ED_undo_push(struct bContext *C, const char *str);
void ED_undo_push_op(struct bContext *C, struct wmOperator *op);
+void ED_undo_grouped_push(struct bContext *C, const char *str);
+void ED_undo_grouped_push_op(struct bContext *C, struct wmOperator *op);
void ED_undo_pop_op(struct bContext *C, struct wmOperator *op);
void ED_undo_pop(struct bContext *C);
void ED_undo_redo(struct bContext *C);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 860a865466a..022b8605436 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -2136,7 +2136,8 @@ static void SCREEN_OT_frame_offset(wmOperatorType *ot)
ot->exec = frame_offset_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = 0;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
@@ -2189,7 +2190,8 @@ static void SCREEN_OT_frame_jump(wmOperatorType *ot)
ot->exec = frame_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* rna */
RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of the frame range");
@@ -2295,7 +2297,8 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot)
ot->exec = keyframe_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* properties */
RNA_def_boolean(ot->srna, "next", true, "Next Keyframe", "");
@@ -2357,7 +2360,8 @@ static void SCREEN_OT_marker_jump(wmOperatorType *ot)
ot->exec = marker_jump_exec;
ot->poll = ED_operator_screenactive_norender;
- ot->flag = OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO_GROUPED;
+ ot->undo_group = "FRAME_CHANGE";
/* properties */
RNA_def_boolean(ot->srna, "next", true, "Next Marker", "");
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index ee6700666c0..4a9311416b3 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -217,6 +217,19 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
return OPERATOR_FINISHED;
}
+void ED_undo_grouped_push(bContext *C, const char *str)
+{
+ /* do nothing if previous undo task is the same as this one (or from the same undo group) */
+ const char *last_undo = BKE_undo_get_name_last();
+
+ if (last_undo && STREQ(str, last_undo)) {
+ return;
+ }
+
+ /* push as usual */
+ ED_undo_push(C, str);
+}
+
void ED_undo_pop(bContext *C)
{
ed_undo_step(C, 1, NULL);
@@ -232,6 +245,16 @@ void ED_undo_push_op(bContext *C, wmOperator *op)
ED_undo_push(C, op->type->name);
}
+void ED_undo_grouped_push_op(bContext *C, wmOperator *op)
+{
+ if (op->type->undo_group[0] != '\0') {
+ ED_undo_grouped_push(C, op->type->undo_group);
+ }
+ else {
+ ED_undo_grouped_push(C, op->type->name);
+ }
+}
+
void ED_undo_pop_op(bContext *C, wmOperator *op)
{
/* search back a couple of undo's, in case something else added pushes */
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 90081a93188..35c9c9bcc89 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -419,6 +419,7 @@ static EnumPropertyItem keymap_modifiers_items[] = {
static EnumPropertyItem operator_flag_items[] = {
{OPTYPE_REGISTER, "REGISTER", 0, "Register", "Display in the info window and support the redo toolbar panel"},
{OPTYPE_UNDO, "UNDO", 0, "Undo", "Push an undo event (needed for operator redo)"},
+ {OPTYPE_UNDO_GROUPED, "UNDO_GROUPED", 0, "Grouped Undo", "Push a single undo event for repetead instances of this operator"},
{OPTYPE_BLOCKING, "BLOCKING", 0, "Blocking", "Block anything else from using the cursor"},
{OPTYPE_MACRO, "MACRO", 0, "Macro", "Use to check if an operator is a macro"},
{OPTYPE_GRAB_CURSOR, "GRAB_CURSOR", 0, "Grab Pointer",
@@ -1139,6 +1140,7 @@ static char _operator_idname[OP_MAX_TYPENAME];
static char _operator_name[OP_MAX_TYPENAME];
static char _operator_descr[RNA_DYN_DESCR_MAX];
static char _operator_ctxt[RNA_DYN_DESCR_MAX];
+static char _operator_undo_group[OP_MAX_TYPENAME];
static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *data, const char *identifier,
StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free)
{
@@ -1153,10 +1155,11 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */
dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */
dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */
+ dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */
RNA_pointer_create(NULL, &RNA_Operator, &dummyop, &dummyotr);
/* clear in case they are left unset */
- _operator_idname[0] = _operator_name[0] = _operator_descr[0] = '\0';
+ _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0';
/* We have to set default op context! */
strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
@@ -1210,9 +1213,10 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
int namelen = strlen(_operator_name) + 1;
int desclen = strlen(_operator_descr) + 1;
int ctxtlen = strlen(_operator_ctxt) + 1;
+ int ugrouplen = strlen(_operator_undo_group) + 1;
char *ch;
/* 2 terminators and 3 to convert a.b -> A_OT_b */
- ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname");
+ ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname");
WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */
dummyot.idname = ch;
ch += idlen;
@@ -1224,6 +1228,9 @@ static StructRNA *rna_Operator_register(Main *bmain, ReportList *reports, void *
ch += desclen;
strcpy(ch, _operator_ctxt);
dummyot.translation_context = ch;
+ ch += ctxtlen;
+ strcpy(ch, _operator_undo_group);
+ dummyot.undo_group = ch;
}
}
@@ -1280,10 +1287,11 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
dummyot.name = _operator_name; /* only assigne the pointer, string is NULL'd */
dummyot.description = _operator_descr; /* only assigne the pointer, string is NULL'd */
dummyot.translation_context = _operator_ctxt; /* only assigne the pointer, string is NULL'd */
+ dummyot.undo_group = _operator_undo_group; /* only assigne the pointer, string is NULL'd */
RNA_pointer_create(NULL, &RNA_Macro, &dummyop, &dummyotr);
/* clear in case they are left unset */
- _operator_idname[0] = _operator_name[0] = _operator_descr[0] = '\0';
+ _operator_idname[0] = _operator_name[0] = _operator_descr[0] = _operator_undo_group[0] = '\0';
/* We have to set default op context! */
strcpy(_operator_ctxt, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
@@ -1297,9 +1305,10 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
int namelen = strlen(_operator_name) + 1;
int desclen = strlen(_operator_descr) + 1;
int ctxtlen = strlen(_operator_ctxt) + 1;
+ int ugrouplen = strlen(_operator_undo_group) + 1;
char *ch;
/* 2 terminators and 3 to convert a.b -> A_OT_b */
- ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen), "_operator_idname");
+ ch = MEM_callocN(sizeof(char) * (idlen + namelen + desclen + ctxtlen + ugrouplen), "_operator_idname");
WM_operator_bl_idname(ch, _operator_idname); /* convert the idname from python */
dummyot.idname = ch;
ch += idlen;
@@ -1311,6 +1320,9 @@ static StructRNA *rna_MacroOperator_register(Main *bmain, ReportList *reports, v
ch += desclen;
strcpy(ch, _operator_ctxt);
dummyot.translation_context = ch;
+ ch += ctxtlen;
+ strcpy(ch, _operator_undo_group);
+ dummyot.undo_group = ch;
}
if (strlen(identifier) >= sizeof(dummyop.idname)) {
@@ -1401,6 +1413,16 @@ static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value)
assert(!"setting the bl_description on a non-builtin operator");
}
+static void rna_Operator_bl_undo_group_set(PointerRNA *ptr, const char *value)
+{
+ wmOperator *data = (wmOperator *)(ptr->data);
+ char *str = (char *)data->type->undo_group;
+ if (!str[0])
+ BLI_strncpy(str, value, OP_MAX_TYPENAME); /* utf8 already ensured */
+ else
+ assert(!"setting the bl_undo_group on a non-builtin operator");
+}
+
static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
wmKeyMapItem *kmi = ptr->data;
@@ -1509,6 +1531,14 @@ static void rna_def_operator(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+ prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
+ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
RNA_def_property_enum_items(prop, operator_flag_items);
@@ -1587,6 +1617,14 @@ static void rna_def_macro_operator(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+ prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "type->undo_group");
+ RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_undo_group_set");
+ /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
+ RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */
+
prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "type->flag");
RNA_def_property_enum_items(prop, operator_flag_items);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 0fe3e8a0fcf..cd46e24264d 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -138,6 +138,7 @@ enum {
OPTYPE_INTERNAL = (1 << 6),
OPTYPE_LOCK_BYPASS = (1 << 7), /* Allow operator to run when interface is locked */
+ OPTYPE_UNDO_GROUPED = (1 << 8), /* Special type of undo which doesn't store itself multiple times */
};
/* context to call operator in for WM_operator_name_call */
@@ -522,6 +523,7 @@ typedef struct wmOperatorType {
const char *idname; /* unique identifier */
const char *translation_context;
const char *description; /* tooltips and python docs */
+ const char *undo_group; /* identifier to group operators together */
/* this callback executes the operator without any interactive input,
* parameters may be provided through operator properties. cannot use
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index dc34e8015c9..ad8a67027c0 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -730,6 +730,8 @@ static void wm_operator_finished(bContext *C, wmOperator *op, const bool repeat)
if (wm->op_undo_depth == 0)
if (op->type->flag & OPTYPE_UNDO)
ED_undo_push_op(C, op);
+ else if (op->type->flag & OPTYPE_UNDO_GROUPED)
+ ED_undo_grouped_push_op(C, op);
if (repeat == 0) {
if (G.debug & G_DEBUG_WM) {
@@ -1857,6 +1859,8 @@ static int wm_handler_fileselect_do(bContext *C, ListBase *handlers, wmEventHand
if (CTX_wm_manager(C) == wm && wm->op_undo_depth == 0)
if (handler->op->type->flag & OPTYPE_UNDO)
ED_undo_push_op(C, handler->op);
+ else if (handler->op->type->flag & OPTYPE_UNDO_GROUPED)
+ ED_undo_grouped_push_op(C, handler->op);
if (handler->op->reports->list.first) {