Undo: unified undo system w/ linear history
authorCampbell Barton <ideasman42@gmail.com>
Mon, 19 Mar 2018 13:17:59 +0000 (14:17 +0100)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 31 Mar 2018 18:40:37 +0000 (20:40 +0200)
- Use a single undo history for all operations.
- UndoType's are registered and poll the context to check if they
  should be used when performing an undo push.
- Mode switching is used to ensure the state is correct before
  undo data is restored.
- Some undo types accumulate changes (image & text editing)
  others store the state multiple times (with de-duplication).
  This is supported by checking UndoStack.mode `ACCUMULATE` / `STORE`.
- Each undo step stores ID datablocks they use with utilities to help
  manage restoring correct ID's.
  Needed since global undo is now mixed with other modes undo.
- Currently performs each undo step when going up/down history
  Previously this wasn't done, making history fail in some cases.
  This can be optimized to skip some combinations of undo steps.

grease-pencil is an exception which has not been updated
since it integrates undo into the draw-session.

See D3113

66 files changed:
build_files/cmake/macros.cmake
source/blender/blenkernel/BKE_blender_undo.h
source/blender/blenkernel/BKE_main.h
source/blender/blenkernel/BKE_pointcache.h
source/blender/blenkernel/BKE_undo_system.h [new file with mode: 0644]
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/blender_undo.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/undo_system.c [new file with mode: 0644]
source/blender/blenloader/BLO_readfile.h
source/blender/blenloader/BLO_undofile.h
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/undofile.c
source/blender/editors/armature/editarmature_undo.c
source/blender/editors/curve/editcurve.c
source/blender/editors/curve/editcurve_undo.c
source/blender/editors/curve/editfont_undo.c
source/blender/editors/include/ED_armature.h
source/blender/editors/include/ED_curve.h
source/blender/editors/include/ED_lattice.h
source/blender/editors/include/ED_mball.h
source/blender/editors/include/ED_mesh.h
source/blender/editors/include/ED_object.h
source/blender/editors/include/ED_paint.h
source/blender/editors/include/ED_particle.h
source/blender/editors/include/ED_sculpt.h
source/blender/editors/include/ED_text.h
source/blender/editors/include/ED_util.h
source/blender/editors/lattice/editlattice_undo.c
source/blender/editors/mesh/editmesh_undo.c
source/blender/editors/metaball/editmball_undo.c
source/blender/editors/object/object_edit.c
source/blender/editors/object/object_modes.c
source/blender/editors/physics/particle_edit.c
source/blender/editors/physics/particle_edit_undo.c
source/blender/editors/physics/particle_object.c
source/blender/editors/physics/physics_intern.h
source/blender/editors/render/render_internal.c
source/blender/editors/sculpt_paint/CMakeLists.txt
source/blender/editors/sculpt_paint/paint_curve.c
source/blender/editors/sculpt_paint/paint_curve_undo.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/sculpt_paint/paint_image_undo.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/editors/sculpt_paint/paint_undo.c [deleted file]
source/blender/editors/sculpt_paint/sculpt_intern.h
source/blender/editors/sculpt_paint/sculpt_undo.c
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_text/CMakeLists.txt
source/blender/editors/space_text/text_ops.c
source/blender/editors/space_text/text_undo.c [new file with mode: 0644]
source/blender/editors/util/CMakeLists.txt
source/blender/editors/util/ed_util.c
source/blender/editors/util/editmode_undo.c [deleted file]
source/blender/editors/util/memfile_undo.c [new file with mode: 0644]
source/blender/editors/util/undo.c
source/blender/editors/util/undo_system_types.c [new file with mode: 0644]
source/blender/editors/util/util_intern.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/windowmanager/WM_undo.h [new file with mode: 0644]
source/blender/windowmanager/intern/wm.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_init_exit.c
source/creator/creator_signals.c

index f0cff75c4170081b1990472c016c36611d80ea18..3f0868229e2f184e824dc3d58c68862909d3c095 100644 (file)
@@ -559,6 +559,9 @@ function(SETUP_BLENDER_SORTED_LIBS)
        set(BLENDER_SORTED_LIBS
                bf_windowmanager
 
+               # needed twice because of text undo
+               bf_editor_util
+
                bf_editor_space_api
                bf_editor_space_action
                bf_editor_space_buttons
index 84a6d07be7da94554556ce727123203b2f18826f..a96f8af1fdb34f1d04b573d04552260d4a47974e 100644 (file)
@@ -31,22 +31,13 @@ extern "C" {
 struct bContext;
 struct Scene;
 struct Main;
+struct MemFileUndoData;
 
 #define BKE_UNDO_STR_MAX 64
 
-/* global undo */
-extern void          BKE_undo_write(struct bContext *C, const char *name);
-extern void          BKE_undo_step(struct bContext *C, int step);
-extern void          BKE_undo_name(struct bContext *C, const char *name);
-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);
-
-extern void          BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C));
+struct MemFileUndoData *BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev);
+bool                    BKE_memfile_undo_decode(struct MemFileUndoData *mfu, struct bContext *C);
+void                    BKE_memfile_undo_free(struct MemFileUndoData *mfu);
 
 #ifdef __cplusplus
 }
index d8318bfcf5d99f21f34af20dbb9d6641a330324e..e224155726f2bf1ca0c26a73129330fd0561a116 100644 (file)
@@ -84,10 +84,12 @@ typedef struct Main {
        short minversionfile, minsubversionfile;
        uint64_t build_commit_timestamp; /* commit's timestamp from buildinfo */
        char build_hash[16];  /* hash from buildinfo */
-       short recovered;        /* indicate the main->name (file) is the recovered one */
+       char recovered; /* indicate the main->name (file) is the recovered one */
+       /** All current ID's exist in the last memfile undo step. */
+       char is_memfile_undo_written;
 
        BlendThumbnail *blen_thumb;
-       
+
        struct Library *curlib;
        ListBase scene;
        ListBase library;
index f31ae715539868c9d563ecc4704b8d6b324bcdaa..cc60df1b2d67c551404d02a64e85993273226f71 100644 (file)
@@ -227,7 +227,6 @@ typedef struct PTCacheEditPoint {
 } PTCacheEditPoint;
 
 typedef struct PTCacheUndo {
-       struct PTCacheUndo *next, *prev;
        struct PTCacheEditPoint *points;
 
        /* particles stuff */
@@ -240,12 +239,11 @@ typedef struct PTCacheUndo {
        struct ListBase mem_cache;
 
        int totpoint;
-       char name[64];
+
+       size_t undo_size;
 } PTCacheUndo;
 
 typedef struct PTCacheEdit {
-       ListBase undo;
-       struct PTCacheUndo *curundo;
        PTCacheEditPoint *points;
 
        struct PTCacheID pid;
diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h
new file mode 100644 (file)
index 0000000..6072ecf
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BKE_UNDO_SYSTEM_H__
+#define __BKE_UNDO_SYSTEM_H__
+
+/** \file BKE_undo_system.h
+ *  \ingroup bke
+ */
+
+struct Main;
+struct UndoStep;
+struct bContext;
+
+/* ID's */
+struct Mesh;
+struct Object;
+struct Scene;
+struct Text;
+
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+
+typedef struct UndoRefID { struct ID *ptr; char name[MAX_ID_NAME]; } UndoRefID;
+/* UndoRefID_Mesh & friends. */
+#define UNDO_REF_ID_TYPE(ptr_ty) \
+       typedef struct UndoRefID_##ptr_ty { struct ptr_ty *ptr; char name[MAX_ID_NAME]; } UndoRefID_##ptr_ty
+UNDO_REF_ID_TYPE(Mesh);
+UNDO_REF_ID_TYPE(Object);
+UNDO_REF_ID_TYPE(Scene);
+UNDO_REF_ID_TYPE(Text);
+
+typedef struct UndoStack {
+       ListBase         steps;
+       struct UndoStep *step_active;
+
+       /**
+        * Some undo systems require begin/end, see: #UndoType.step_encode_init
+        *
+        * \note This is not included in the 'steps' list.
+        * That is done once end is called.
+        */
+       struct UndoStep *step_init;
+} UndoStack;
+
+
+typedef struct UndoStep {
+       struct UndoStep *next, *prev;
+       char name[64];
+       const struct UndoType *type;
+       /** Size in bytes of all data in step (not including the step). */
+       size_t data_size;
+       /** Users should never see this step (only use for internal consistency). */
+       bool skip;
+       /* Over alloc 'type->struct_size'. */
+} UndoStep;
+
+typedef enum eUndoTypeMode {
+       /**
+        * Each undo step stores a version of the state.
+        * This means we can simply load in a previous state at any time.
+        */
+       BKE_UNDOTYPE_MODE_STORE = 1,
+       /**
+        * Each undo step is a series of edits.
+        * This means to change states we need to apply each edit.
+        * It also means the 'step_decode' callback needs to detect the difference between undo and redo.
+        * (Currently used for text edit and image & sculpt painting).
+        */
+       BKE_UNDOTYPE_MODE_ACCUMULATE = 2,
+} eUndoTypeMode;
+
+typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref);
+
+typedef struct UndoType {
+       struct UndoType *next, *prev;
+       /** Only for debugging. */
+       const char *name;
+
+       /**
+        * When NULL, we don't consider this undo type for context checks.
+        * Operators must explicitly set the undo type and handle adding the undo step.
+        * This is needed when tools operate on data which isn't the primary mode (eg, paint-curve in sculpt mode).
+        */
+       bool (*poll)(struct bContext *C);
+
+       /**
+        * None of these callbacks manage list add/removal.
+        *
+        * Note that 'step_encode_init' is optional,
+        * some undo types need to perform operatons before undo push finishes.
+        */
+       void (*step_encode_init)(struct bContext *C, UndoStep *us);
+
+       bool (*step_encode)(struct bContext *C, UndoStep *us);
+       void (*step_decode)(struct bContext *C, UndoStep *us, int dir);
+
+       /**
+        * \note When freeing all steps,
+        * free from the last since #MemFileUndoType will merge with the next undo type in the list. */
+       void (*step_free)(UndoStep *us);
+
+       void (*step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+
+       eUndoTypeMode mode;
+       bool use_context;
+
+       int step_size;
+} UndoType;
+
+/* expose since we need to perform operations on spesific undo types (rarely). */
+extern const UndoType *BKE_UNDOSYS_TYPE_MEMFILE;
+extern const UndoType *BKE_UNDOSYS_TYPE_IMAGE;
+extern const UndoType *BKE_UNDOSYS_TYPE_SCULPT;
+extern const UndoType *BKE_UNDOSYS_TYPE_PARTICLE;
+extern const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE;
+
+UndoStack      *BKE_undosys_stack_create(void);
+void            BKE_undosys_stack_destroy(UndoStack *ustack);
+void            BKE_undosys_stack_clear(UndoStack *ustack);
+bool            BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name);
+void            BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain);
+UndoStep       *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut);
+UndoStep       *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut);
+void            BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit);
+
+/* Only some UndoType's require init. */
+void BKE_undosys_step_push_init_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
+void BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name);
+
+bool BKE_undosys_step_push_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
+bool BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name);
+
+UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut);
+UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name);
+
+bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
+bool BKE_undosys_step_undo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+bool BKE_undosys_step_undo(UndoStack *ustack, struct bContext *C);
+
+bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
+bool BKE_undosys_step_redo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+bool BKE_undosys_step_redo(UndoStack *ustack, struct bContext *C);
+
+bool BKE_undosys_step_load_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
+
+bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, struct bContext *C, int step);
+void BKE_undosys_step_undo_from_index(UndoStack *ustack, struct bContext *C, int index);
+UndoStep *BKE_undosys_step_same_type_next(UndoStep *us);
+UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us);
+
+/* Type System */
+UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *));
+void      BKE_undosys_type_free_all(void);
+
+/* ID Accessor */
+#if 0  /* functionality is only used internally for now. */
+void BKE_undosys_foreach_ID_ref(UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+#endif
+
+/* Use when the undo step stores many arbitrary pointers. */
+struct UndoIDPtrMap;
+struct UndoIDPtrMap *BKE_undosys_ID_map_create(void);
+void                 BKE_undosys_ID_map_destroy(struct UndoIDPtrMap *map);
+void                 BKE_undosys_ID_map_add(struct UndoIDPtrMap *map, ID *id);
+struct ID           *BKE_undosys_ID_map_lookup(const struct UndoIDPtrMap *map, const struct ID *id_src);
+void                 BKE_undosys_ID_map_foreach_ID_ref(
+        struct UndoIDPtrMap *map,
+        UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
+
+#endif  /* __BKE_UNDO_SYSTEM_H__ */
index 25954f277b8aedc40ca870cc4cd5cf92cc504cd0..d789671ab24597514283f780304916ad96c7c061 100644 (file)
@@ -188,6 +188,7 @@ set(SRC
        intern/tracking_solver.c
        intern/tracking_stabilize.c
        intern/tracking_util.c
+       intern/undo_system.c
        intern/unit.c
        intern/world.c
        intern/writeavi.c
@@ -296,6 +297,7 @@ set(SRC
        BKE_text.h
        BKE_texture.h
        BKE_tracking.h
+       BKE_undo_system.h
        BKE_unit.h
        BKE_world.h
        BKE_writeavi.h
index 4efca5bcc410b1c00ec5fa31e7dd13d11e446db8..6b31c8c96f9b0e110d2a5b3ef175441083f504e3 100644 (file)
 
 #include "DNA_scene_types.h"
 
-#include "BLI_fileops.h"
-#include "BLI_listbase.h"
 #include "BLI_path_util.h"
 #include "BLI_string.h"
 #include "BLI_utildefines.h"
 
-#include "IMB_imbuf.h"
-#include "IMB_moviecache.h"
-
 #include "BKE_blender_undo.h"  /* own include */
 #include "BKE_blendfile.h"
 #include "BKE_appdir.h"
-#include "BKE_brush.h"
 #include "BKE_context.h"
 #include "BKE_depsgraph.h"
 #include "BKE_global.h"
-#include "BKE_image.h"
 #include "BKE_main.h"
-#include "RE_pipeline.h"
 
 #include "BLO_undofile.h"
-#include "BLO_readfile.h"
 #include "BLO_writefile.h"
 
 /* -------------------------------------------------------------------- */
 
 #define UNDO_DISK   0
 
-typedef struct UndoElem {
-       struct UndoElem *next, *prev;
-       /* Only for 'UNDO_DISK' */
-       char filename[FILE_MAX];
-       char name[BKE_UNDO_STR_MAX];
-       MemFile memfile;
-       size_t undo_size;
-} UndoElem;
-
-static ListBase undobase = {NULL, NULL};
-static UndoElem *curundo = NULL;
-
-/**
- * Avoid bad-level call to #WM_jobs_kill_all_except()
- */
-static void (*undo_wm_job_kill_callback)(struct bContext *C) = NULL;
-
-void BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C))
-{
-       undo_wm_job_kill_callback = callback;
-}
-
-static int read_undosave(bContext *C, UndoElem *uel)
+bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C)
 {
        char mainstr[sizeof(G.main->name)];
        int success = 0, fileflags;
 
-       /* This is needed so undoing/redoing doesn't crash with threaded previews going */
-       undo_wm_job_kill_callback(C);
-
        BLI_strncpy(mainstr, G.main->name, sizeof(mainstr));    /* temporal store */
 
        fileflags = G.fileflags;
        G.fileflags |= G_FILE_NO_UI;
 
        if (UNDO_DISK) {
-               success = (BKE_blendfile_read(C, uel->filename, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
+               success = (BKE_blendfile_read(C, mfu->filename, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
        }
        else {
-               success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL, 0);
+               success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, NULL, 0);
        }
 
        /* restore */
@@ -127,51 +93,9 @@ static int read_undosave(bContext *C, UndoElem *uel)
        return success;
 }
 
-/* name can be a dynamic string */
-void BKE_undo_write(bContext *C, const char *name)
+MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
 {
-       int nr /*, success */ /* UNUSED */;
-       UndoElem *uel;
-
-       if ((U.uiflag & USER_GLOBALUNDO) == 0) {
-               return;
-       }
-
-       if (U.undosteps == 0) {
-               return;
-       }
-
-       /* remove all undos after (also when curundo == NULL) */
-       while (undobase.last != curundo) {
-               uel = undobase.last;
-               BLI_remlink(&undobase, uel);
-               BLO_memfile_free(&uel->memfile);
-               MEM_freeN(uel);
-       }
-
-       /* make new */
-       curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file");
-       BLI_strncpy(uel->name, name, sizeof(uel->name));
-       BLI_addtail(&undobase, uel);
-
-       /* and limit amount to the maximum */
-       nr = 0;
-       uel = undobase.last;
-       while (uel) {
-               nr++;
-               if (nr == U.undosteps) break;
-               uel = uel->prev;
-       }
-       if (uel) {
-               while (undobase.first != uel) {
-                       UndoElem *first = undobase.first;
-                       BLI_remlink(&undobase, first);
-                       /* the merge is because of compression */
-                       BLO_memfile_merge(&first->memfile, &first->next->memfile);
-                       MEM_freeN(first);
-               }
-       }
-
+       MemFileUndoData *mfu = MEM_callocN(sizeof(MemFileUndoData), __func__);
 
        /* disk save version */
        if (UNDO_DISK) {
@@ -187,222 +111,25 @@ void BKE_undo_write(bContext *C, const char *name)
                BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
                BLI_make_file_string("/", filename, BKE_tempdir_session(), numstr);
 
-               /* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL);
+               /* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL);
 
-               BLI_strncpy(curundo->filename, filename, sizeof(curundo->filename));
+               BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename));
        }
        else {
-               MemFile *prevfile = NULL;
-
-               if (curundo->prev) prevfile = &(curundo->prev->memfile);
-
-               /* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags);
-               curundo->undo_size = curundo->memfile.size;
-       }
-
-       if (U.undomemory != 0) {
-               size_t maxmem, totmem;
-               /* limit to maximum memory (afterwards, we can't know in advance) */
-               totmem = 0;
-               maxmem = ((size_t)U.undomemory) * 1024 * 1024;
-
-               /* keep at least two (original + other) */
-               uel = undobase.last;
-               while (uel && uel->prev) {
-                       totmem += uel->undo_size;
-                       if (totmem > maxmem) break;
-                       uel = uel->prev;
-               }
-
-               if (uel) {
-                       if (uel->prev && uel->prev->prev)
-                               uel = uel->prev;
-
-                       while (undobase.first != uel) {
-                               UndoElem *first = undobase.first;
-                               BLI_remlink(&undobase, first);
-                               /* the merge is because of compression */
-                               BLO_memfile_merge(&first->memfile, &first->next->memfile);
-                               MEM_freeN(first);
-                       }
-               }
-       }
-}
-
-/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
-void BKE_undo_step(bContext *C, int step)
-{
-
-       if (step == 0) {
-               read_undosave(C, curundo);
-       }
-       else if (step == 1) {
-               /* curundo should never be NULL, after restart or load file it should call undo_save */
-               if (curundo == NULL || curundo->prev == NULL) {
-                       // XXX error("No undo available");
-               }
-               else {
-                       if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
-                       curundo = curundo->prev;
-                       read_undosave(C, curundo);
-               }
-       }
-       else {
-               /* curundo has to remain current situation! */
-
-               if (curundo == NULL || curundo->next == NULL) {
-                       // XXX error("No redo available");
-               }
-               else {
-                       read_undosave(C, curundo->next);
-                       curundo = curundo->next;
-                       if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
-               }
-       }
-}
-
-void BKE_undo_reset(void)
-{
-       UndoElem *uel;
-
-       uel = undobase.first;
-       while (uel) {
-               BLO_memfile_free(&uel->memfile);
-               uel = uel->next;
-       }
-
-       BLI_freelistN(&undobase);
-       curundo = NULL;
-}
-
-/* based on index nr it does a restore */
-void BKE_undo_number(bContext *C, int nr)
-{
-       curundo = BLI_findlink(&undobase, nr);
-       BKE_undo_step(C, 0);
-}
-
-/* go back to the last occurance of name in stack */
-void BKE_undo_name(bContext *C, const char *name)
-{
-       UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
-
-       if (uel && uel->prev) {
-               curundo = uel->prev;
-               BKE_undo_step(C, 0);
-       }
-}
-
-/* name optional */
-bool BKE_undo_is_valid(const char *name)
-{
-       if (name) {
-               UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
-               return uel && uel->prev;
+               MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL;
+               /* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, G.fileflags);
+               mfu->undo_size = mfu->memfile.size;
        }
 
-       return undobase.last != undobase.first;
-}
-
-/* get name of undo item, return null if no item with this index */
-/* if active pointer, set it to 1 if true */
-const char *BKE_undo_get_name(int nr, bool *r_active)
-{
-       UndoElem *uel = BLI_findlink(&undobase, nr);
+       bmain->is_memfile_undo_written = true;
 
-       if (r_active) *r_active = false;
-
-       if (uel) {
-               if (r_active && (uel == curundo)) {
-                       *r_active = true;
-               }
-               return uel->name;
-       }
-       return NULL;
+       return mfu;
 }
 
-/* return the name of the last item */
-const char *BKE_undo_get_name_last(void)
+void BKE_memfile_undo_free(MemFileUndoData *mfu)
 {
-       UndoElem *uel = undobase.last;
-       return (uel ? uel->name : NULL);
-}
-
-/**
- * Saves .blend using undo buffer.
- *
- * \return success.
- */
-bool BKE_undo_save_file(const char *filename)
-{
-       UndoElem *uel;
-       MemFileChunk *chunk;
-       int file, oflags;
-
-       if ((U.uiflag & USER_GLOBALUNDO) == 0) {
-               return false;
-       }
-
-       uel = curundo;
-       if (uel == NULL) {
-               fprintf(stderr, "No undo buffer to save recovery file\n");
-               return false;
-       }
-
-       /* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
-        * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
-        */
-
-       oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
-#ifdef O_NOFOLLOW
-       /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
-       oflags |= O_NOFOLLOW;
-#else
-       /* TODO(sergey): How to deal with symlinks on windows? */
-#  ifndef _MSC_VER
-#    warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
-#  endif
-#endif
-       file = BLI_open(filename,  oflags, 0666);
-
-       if (file == -1) {
-               fprintf(stderr, "Unable to save '%s': %s\n",
-                       filename, errno ? strerror(errno) : "Unknown error opening file");
-               return false;
-       }
-
-       for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) {
-               if (write(file, chunk->buf, chunk->size) != chunk->size) {
-                       break;
-               }
-       }
-
-       close(file);
-
-       if (chunk) {
-               fprintf(stderr, "Unable to save '%s': %s\n",
-                       filename, errno ? strerror(errno) : "Unknown error writing file");
-               return false;
-       }
-       return true;
-}
-
-/* sets curscene */
-Main *BKE_undo_get_main(Scene **r_scene)
-{
-       Main *mainp = NULL;
-       BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL, BLO_READ_SKIP_NONE);
-
-       if (bfd) {
-               mainp = bfd->main;
-               if (r_scene) {
-                       *r_scene = bfd->curscene;
-               }
-
-               MEM_freeN(bfd);
-       }
-
-       return mainp;
+       BLO_memfile_free(&mfu->memfile);
+       MEM_freeN(mfu);
 }
 
 /** \} */
index 99423ee26a6586a2c809a6299a8d508eb45d560d..10b724f9f69706b6f169b6165898611390b8a132 100644 (file)
@@ -757,6 +757,7 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
        new_id(lb, id, NULL);
        /* alphabetic insertion: is in new_id */
        id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
+       bmain->is_memfile_undo_written = false;
        BKE_main_unlock(bmain);
 }
 
@@ -776,6 +777,7 @@ void BKE_libblock_management_main_remove(Main *bmain, void *idv)
        BKE_main_lock(bmain);
        BLI_remlink(lb, id);
        id->tag |= LIB_TAG_NO_MAIN;
+       bmain->is_memfile_undo_written = false;
        BKE_main_unlock(bmain);
 }
 
@@ -1138,6 +1140,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
                        BKE_main_lock(bmain);
                        BLI_addtail(lb, id);
                        new_id(lb, id, name);
+                       bmain->is_memfile_undo_written = false;
                        /* alphabetic insertion: is in new_id */
                        BKE_main_unlock(bmain);
 
diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c
new file mode 100644 (file)
index 0000000..ddcd16f
--- /dev/null
@@ -0,0 +1,795 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/util/undo_system.c
+ *  \ingroup edutil
+ *
+ * Used by ED_undo.h, internal implementation.
+ */
+
+#include <string.h>
+
+#include "CLG_log.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_sys_types.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_sort_utils.h"
+
+#include "DNA_listBase.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
+
+#include "MEM_guardedalloc.h"
+
+#define undo_stack _wm_undo_stack_disallow  /* pass in as a variable always. */
+
+/** Odd requirement of Blender that we always keep a memfile undo in the stack. */
+#define WITH_GLOBAL_UNDO_KEEP_ONE
+
+/** Make sure all ID's created at the point we add an undo step that uses ID's. */
+#define WITH_GLOBAL_UNDO_ENSURE_UPDATED
+
+/** We only need this locally. */
+static CLG_LogRef LOG = {"bke.undosys"};
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Nested Undo Checks
+ *
+ * Make sure we're not running undo operations from 'step_encode', 'step_decode' callbacks.
+ * bugs caused by this situation aren't _that_ hard to spot but aren't always so obvious.
+ * Best we have a check which shows the problem immediately.
+ *
+ * \{ */
+#define WITH_NESTED_UNDO_CHECK
+
+#ifdef WITH_NESTED_UNDO_CHECK
+static bool g_undo_callback_running = false;
+#  define UNDO_NESTED_ASSERT(state) BLI_assert(g_undo_callback_running == state)
+#  define UNDO_NESTED_CHECK_BEGIN { \
+       UNDO_NESTED_ASSERT(false); \
+       g_undo_callback_running = true; \
+} ((void)0)
+#  define UNDO_NESTED_CHECK_END { \
+       UNDO_NESTED_ASSERT(true); \
+       g_undo_callback_running = false; \
+} ((void)0)
+#else
+#  define UNDO_NESTED_ASSERT(state) ((void)0)
+#  define UNDO_NESTED_CHECK_BEGIN ((void)0)
+#  define UNDO_NESTED_CHECK_END   ((void)0)
+#endif
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public Undo Types
+ *
+ * Unfortunately we need this for a handful of places.
+ */
+const UndoType *BKE_UNDOSYS_TYPE_MEMFILE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_IMAGE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_SCULPT = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_PARTICLE = NULL;
+const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE = NULL;
+/** \} */
+
+/* UndoType */
+
+static ListBase g_undo_types = {NULL, NULL};
+
+static const UndoType *BKE_undosys_type_from_context(bContext *C)
+{
+       for (const UndoType *ut = g_undo_types.first; ut; ut = ut->next) {
+               /* No poll means we don't check context. */
+               if (ut->poll && ut->poll(C)) {
+                       return ut;
+               }
+       }
+       return NULL;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Callback Wrappers
+ *
+ * #UndoRefID is simply a way to avoid inlining name copy and lookups,
+ * since it's easy to forget a single case when done inline (crashing in some cases).
+ *
+ * \{ */
+
+static void undosys_id_ref_store(void *UNUSED(user_data), UndoRefID *id_ref)
+{
+       BLI_assert(id_ref->name[0] == '\0');
+       if (id_ref->ptr) {
+               BLI_strncpy(id_ref->name, id_ref->ptr->name, sizeof(id_ref->name));
+               /* Not needed, just prevents stale data access. */
+               id_ref->ptr = NULL;
+       }
+}
+
+static void undosys_id_ref_resolve(void *user_data, UndoRefID *id_ref)
+{
+       /* Note: we could optimize this, for now it's not too bad since it only runs when we access undo! */
+       Main *bmain = user_data;
+       ListBase *lb = which_libbase(bmain, GS(id_ref->name));
+       for (ID *id = lb->first; id; id = id->next) {
+               if (STREQ(id_ref->name, id->name) && (id->lib == NULL)) {
+                       id_ref->ptr = id;
+                       break;
+               }
+       }
+}
+
+static bool undosys_step_encode(bContext *C, UndoStep *us)
+{
+       CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
+       UNDO_NESTED_CHECK_BEGIN;
+       bool ok = us->type->step_encode(C, us);
+       UNDO_NESTED_CHECK_END;
+       if (ok) {
+               if (us->type->step_foreach_ID_ref != NULL) {
+                       /* Don't use from context yet because sometimes context is fake and not all members are filled in. */
+                       Main *bmain = G.main;
+                       us->type->step_foreach_ID_ref(us, undosys_id_ref_store, bmain);
+               }
+       }
+       return ok;
+}
+
+static void undosys_step_decode(bContext *C, UndoStep *us, int dir)
+{
+       CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
+       if (us->type->step_foreach_ID_ref) {
+               /* Don't use from context yet because sometimes context is fake and not all members are filled in. */
+               Main *bmain = G.main;
+               us->type->step_foreach_ID_ref(us, undosys_id_ref_resolve, bmain);
+       }
+
+       UNDO_NESTED_CHECK_BEGIN;
+       us->type->step_decode(C, us, dir);
+       UNDO_NESTED_CHECK_END;
+}
+
+static void undosys_step_free_and_unlink(UndoStack *ustack, UndoStep *us)
+{
+       CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
+       UNDO_NESTED_CHECK_BEGIN;
+       us->type->step_free(us);
+       UNDO_NESTED_CHECK_END;
+
+       BLI_remlink(&ustack->steps, us);
+       MEM_freeN(us);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Stack
+ * \{ */
+
+#ifndef NDEBUG
+static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
+{
+       if (ustack->step_active != NULL) {
+               BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
+               BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
+       }
+       if (expect_non_empty) {
+               BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
+       }
+}
+#else
+static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
+{
+       UNUSED_VARS(ustack, expect_non_empty);
+}
+#endif
+
+UndoStack *BKE_undosys_stack_create(void)
+{
+       UndoStack *ustack = MEM_callocN(sizeof(UndoStack), __func__);
+       return ustack;
+}
+
+void BKE_undosys_stack_destroy(UndoStack *ustack)
+{
+       BKE_undosys_stack_clear(ustack);
+       MEM_freeN(ustack);
+}
+
+void BKE_undosys_stack_clear(UndoStack *ustack)
+{
+       UNDO_NESTED_ASSERT(false);
+       CLOG_INFO(&LOG, 1, "steps=%d", BLI_listbase_count(&ustack->steps));
+       for (UndoStep *us = ustack->steps.last, *us_prev; us; us = us_prev) {
+               us_prev = us->prev;
+               undosys_step_free_and_unlink(ustack, us);
+       }
+       BLI_listbase_clear(&ustack->steps);
+       ustack->step_active = NULL;
+}
+
+static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct Main *bmain)
+{
+       UNDO_NESTED_ASSERT(false);
+       CLOG_INFO(&LOG, 1, "'%s'", name);
+       bContext *C_temp = CTX_create();
+       CTX_data_main_set(C_temp, bmain);
+       bool ok = BKE_undosys_step_push_with_type(ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE);
+       CTX_free(C_temp);
+       return ok;
+}
+
+void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain)
+{
+       UNDO_NESTED_ASSERT(false);
+       undosys_stack_push_main(ustack, "original", bmain);
+}
+
+/* name optional */
+bool BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name)
+{
+       if (name) {
+               UndoStep *us = BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
+               return us && us->prev;
+       }
+
+       return !BLI_listbase_is_empty(&ustack->steps);
+}
+
+UndoStep *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut)
+{
+       UndoStep *us = ustack->step_active;
+       while (us && (us->type != ut)) {
+               us = us->prev;
+       }
+       return us;
+}
+
+UndoStep *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut)
+{
+       UNDO_NESTED_ASSERT(false);
+       CLOG_INFO(&LOG, 1, "type='%s'", ut->name);
+       if (ustack->step_init && (ustack->step_init->type == ut)) {
+               return ustack->step_init;
+       }
+       return BKE_undosys_stack_active_with_type(ustack, ut);
+}
+
+/**
+ * \param steps: Limit the number of undo steps.
+ * \param memory_limit: Limit the amount of memory used by the undo stack.
+ */
+void BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit)
+{
+       UNDO_NESTED_ASSERT(false);
+       if (!(steps || memory_limit)) {
+               return;
+       }
+
+       CLOG_INFO(&LOG, 1, "steps=%d, memory_limit=%zu", steps, memory_limit);
+       UndoStep *us;
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+       UndoStep *us_exclude = NULL;
+#endif
+       /* keep at least two (original + other) */
+       size_t data_size_all = 0;
+       size_t us_count = 0;
+       for (us = ustack->steps.last; us && us->prev; us = us->prev) {
+               if (memory_limit) {
+                       data_size_all += us->data_size;
+                       if (data_size_all > memory_limit) {
+                               break;
+                       }
+               }
+               if (steps) {
+                       if (us_count == steps) {
+                               break;
+                       }
+                       if (us->skip == false) {
+                               us_count += 1;
+                       }
+               }
+       }
+
+       if (us) {
+               if (us->prev && us->prev->prev) {
+                       us = us->prev;
+               }
+
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+               /* Hack, we need to keep at least one BKE_UNDOSYS_TYPE_MEMFILE. */
+               if (us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
+                       us_exclude = us->prev;
+                       while (us_exclude && us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
+                               us_exclude = us_exclude->prev;
+                       }
+                       if (us_exclude) {
+                               BLI_remlink(&ustack->steps, us_exclude);
+                       }
+               }
+#endif
+               /* Free from first to last, free functions may update de-duplication info (see #MemFileUndoStep). */
+               while (ustack->steps.first != us) {
+                       UndoStep *us_first = ustack->steps.first;
+                       BLI_assert(us_first != ustack->step_active);
+                       undosys_step_free_and_unlink(ustack, us_first);
+               }
+
+#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
+               if (us_exclude) {
+                       BLI_addhead(&ustack->steps, us_exclude);
+               }
+#endif
+       }
+}
+
+/** \} */
+
+void BKE_undosys_step_push_init_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
+{
+       UNDO_NESTED_ASSERT(false);
+       /* We could detect and clean this up (but it should never happen!). */
+       BLI_assert(ustack->step_init == NULL);
+       if (ut->step_encode_init) {
+               undosys_stack_validate(ustack, false);
+               UndoStep *us = MEM_callocN(ut->step_size, __func__);
+               CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, name, us->type->name);
+               if (name != NULL) {
+                       BLI_strncpy(us->name, name, sizeof(us->name));
+               }
+               us->type = ut;
+               ustack->step_init = us;
+               ut->step_encode_init(C, us);
+               undosys_stack_validate(ustack, true);
+       }
+}
+
+void BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char *name)
+{
+       UNDO_NESTED_ASSERT(false);
+       /* We could detect and clean this up (but it should never happen!). */
+       BLI_assert(ustack->step_init == NULL);
+       const UndoType *ut = BKE_undosys_type_from_context(C);
+       if (ut == NULL) {
+               return;
+       }
+       return BKE_undosys_step_push_init_with_type(ustack, C, name, ut);
+}
+
+bool BKE_undosys_step_push_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
+{
+       UNDO_NESTED_ASSERT(false);
+       undosys_stack_validate(ustack, false);
+       bool is_not_empty = ustack->step_active != NULL;
+
+       /* Remove all undos after (also when 'ustack->step_active == NULL'). */
+       while (ustack->steps.last != ustack->step_active) {
+               UndoStep *us_iter = ustack->steps.last;
+               undosys_step_free_and_unlink(ustack, us_iter);
+               undosys_stack_validate(ustack, is_not_empty);
+       }
+
+       if (ustack->step_active) {
+               BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
+       }
+
+#ifdef WITH_GLOBAL_UNDO_ENSURE_UPDATED
+       if (ut->step_foreach_ID_ref != NULL) {
+               Main *bmain = G.main;
+               if (bmain->is_memfile_undo_written == false) {
+                       const char *name_internal = "MemFile Internal";
+                       if (undosys_stack_push_main(ustack, name_internal, bmain)) {
+                               UndoStep *us = ustack->steps.last;
+                               BLI_assert(STREQ(us->name, name_internal));
+                               us->skip = true;
+                       }
+               }
+       }
+#endif
+
+       UndoStep *us = ustack->step_init ? ustack->step_init : MEM_callocN(ut->step_size, __func__);
+       ustack->step_init = NULL;
+       if (us->name[0] == '\0') {
+               BLI_strncpy(us->name, name, sizeof(us->name));
+       }
+       us->type = ut;
+       /* initialized, not added yet. */
+
+       if (undosys_step_encode(C, us)) {
+               ustack->step_active = us;
+               BLI_addtail(&ustack->steps, us);
+               undosys_stack_validate(ustack, true);
+               return true;
+       }
+       else {
+               MEM_freeN(us);
+               undosys_stack_validate(ustack, true);
+               return false;
+       }
+}
+
+bool BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
+{
+       UNDO_NESTED_ASSERT(false);
+       const UndoType *ut = ustack->step_init ? ustack->step_init->type : BKE_undosys_type_from_context(C);
+       if (ut == NULL) {
+               return false;
+       }
+       return BKE_undosys_step_push_with_type(ustack, C, name, ut);
+}
+
+
+/**
+ * Useful when we want to diff against previous undo data but can't be sure the types match.
+ */
+UndoStep *BKE_undosys_step_same_type_next(UndoStep *us)
+{
+       if (us) {
+               const UndoType *ut = us->type;
+               while ((us = us->next)) {
+                       if (us->type == ut) {
+                               return us;
+                       }
+               }
+
+       }
+       return us;
+}
+
+/**
+ * Useful when we want to diff against previous undo data but can't be sure the types match.
+ */
+UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us)
+{
+       if (us) {
+               const UndoType *ut = us->type;
+               while ((us = us->prev)) {
+                       if (us->type == ut) {
+                               return us;
+                       }
+               }
+
+       }
+       return us;
+}
+
+UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut)
+{
+       for (UndoStep *us = ustack->steps.last; us; us = us->prev) {
+               if (us->type == ut) {
+                       if (STREQ(name, us->name)) {
+                               return us;
+                       }
+               }
+       }
+       return NULL;
+}
+
+UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name)
+{
+       return BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
+}
+
+bool BKE_undosys_step_undo_with_data_ex(
+        UndoStack *ustack, bContext *C, UndoStep *us,
+        bool use_skip)
+{
+       UNDO_NESTED_ASSERT(false);
+       if (us) {
+               undosys_stack_validate(ustack, true);
+       }
+       UndoStep *us_prev = us ? us->prev : NULL;
+       if (us && us->type->mode == BKE_UNDOTYPE_MODE_STORE) {
+               /* The current state is a copy, we need to load the previous state. */
+               us = us_prev;
+       }
+
+       if (us != NULL) {
+               CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, us->name, us->type->name);
+               undosys_step_decode(C, us, -1);
+               ustack->step_active = us_prev;
+               undosys_stack_validate(ustack, true);
+               if (use_skip) {
+                       if (ustack->step_active && ustack->step_active->skip) {
+                               CLOG_INFO(&LOG, 2, "undo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
+                               BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+                       }
+               }
+               return true;
+       }
+       return false;
+}
+bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+       return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true);
+}
+
+bool BKE_undosys_step_undo(UndoStack *ustack, bContext *C)
+{
+       return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+}
+
+void BKE_undosys_step_undo_from_index(UndoStack *ustack, bContext *C, int index)
+{
+       UndoStep *us = BLI_findlink(&ustack->steps, index);
+       BLI_assert(us->skip == false);
+       BKE_undosys_step_load_data(ustack, C, us);
+}
+
+bool BKE_undosys_step_redo_with_data_ex(
+        UndoStack *ustack, bContext *C, UndoStep *us,
+        bool use_skip)
+{
+       UNDO_NESTED_ASSERT(false);
+       UndoStep *us_next = us ? us->next : NULL;
+       /* Unlike undo accumulate, we always use the next. */
+       us = us_next;
+
+       if (us != NULL) {
+               CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, us->name, us->type->name);
+               undosys_step_decode(C, us, 1);
+               ustack->step_active = us_next;
+               if (use_skip) {
+                       if (ustack->step_active && ustack->step_active->skip) {
+                               CLOG_INFO(&LOG, 2, "redo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
+                               BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
+                       }
+               }
+               return true;
+       }
+       return false;
+}
+bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+       return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true);
+}
+
+bool BKE_undosys_step_redo(UndoStack *ustack, bContext *C)
+{
+       return BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
+}
+
+bool BKE_undosys_step_load_data(UndoStack *ustack, bContext *C, UndoStep *us)
+{
+       UNDO_NESTED_ASSERT(false);
+       const int index_active = BLI_findindex(&ustack->steps, ustack->step_active);
+       const int index_target = BLI_findindex(&ustack->steps, us);
+       BLI_assert(!ELEM(-1, index_active, index_target));
+       bool ok = true;
+
+       if (index_target < index_active) {
+               uint i = index_active - index_target;
+               while (i-- && ok) {
+                       ok = BKE_undosys_step_undo_with_data_ex(ustack, C, ustack->step_active, false);
+               }
+       }
+       else if (index_target > index_active) {
+               uint i = index_target - index_active;
+               while (i-- && ok) {
+                       ok = BKE_undosys_step_redo_with_data_ex(ustack, C, ustack->step_active, false);
+               }
+       }
+
+       if (ok) {
+               BLI_assert(ustack->step_active == us);
+       }
+
+       return ok;
+}
+
+bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, bContext *C, int step)
+{
+       if (step == 0) {
+               return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
+       }
+       else if (step == 1) {
+               return BKE_undosys_step_undo(ustack, C);
+       }
+       else {
+               return BKE_undosys_step_redo(ustack, C);
+       }
+}
+/**
+ * Similar to #WM_operatortype_append
+ */
+UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *))
+{
+       UndoType *ut;
+
+       ut = MEM_callocN(sizeof(UndoType), __func__);
+
+       undosys_fn(ut);
+
+       BLI_assert(ut->mode != 0);
+
+       BLI_addtail(&g_undo_types, ut);
+
+       return ut;
+}
+
+void BKE_undosys_type_free_all(void)
+{
+       UndoType *ut;
+       while ((ut = BLI_pophead(&g_undo_types))) {
+               MEM_freeN(ut);
+       }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID Reference Utilities
+ *
+ * Unfortunately we need this for a handful of places.
+ */
+
+static void UNUSED_FUNCTION(BKE_undosys_foreach_ID_ref(
+        UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data))
+{
+       for (UndoStep *us = ustack->steps.first; us; us = us->next) {
+               const UndoType *ut = us->type;
+               if (ut->step_foreach_ID_ref != NULL) {
+                       ut->step_foreach_ID_ref(us, foreach_ID_ref_fn, user_data);
+               }
+       }
+}
+
+typedef struct UndoIDPtrMapItem {
+       /** Never changes (matches undo data). Use as sort key for binary search. */
+       const void *ptr;
+       /** Write the new pointers here. */
+       uint index;
+} UndoIDPtrMapItem;
+
+typedef struct UndoIDPtrMap {
+       UndoRefID *refs;
+       /**
+        * Pointer map, update 'dst' members before use.
+        * This is always sorted (adds some overhead when adding, in practice it's acceptable since).
+        */
+       UndoIDPtrMapItem *pmap;
+
+       /** Length for both 'refs' & 'pmap' */
+       uint len;
+       uint len_alloc;
+} UndoIDPtrMap;
+
+#ifdef DEBUG
+#  define PMAP_DEFAULT_ALLOC 1
+#else
+#  define PMAP_DEFAULT_ALLOC 32
+#endif
+
+void BKE_undosys_ID_map_foreach_ID_ref(
+        UndoIDPtrMap *map,
+        UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       for (uint i = 0; i < map->len; i++) {
+               foreach_ID_ref_fn(user_data, &map->refs[i]);
+       }
+}
+
+/**
+ * Return true when found, otherwise index is set to the index we should insert.
+ */
+static bool undosys_ID_map_lookup_index(const UndoIDPtrMap *map, const void *key, uint *r_index)
+{
+       const UndoIDPtrMapItem *pmap = map->pmap;
+       const uint len = map->len;
+       if (len == 0) {
+               if (*r_index) {
+                       *r_index = 0;
+               }
+               return false;
+       }
+       int min = 0, max = len - 1;
+       while (min <= max) {
+               const uint mid = (min + max) / 2;
+               if (pmap[mid].ptr < key) {
+                       min = mid + 1;
+               }
+               else if (pmap[mid].ptr == key) {
+                       if (r_index) {
+                               *r_index = mid;
+                       }
+                       return true;
+               }
+               else if (pmap[mid].ptr > key) {
+                       max = mid - 1;
+               }
+       }
+       return false;
+}
+
+/**
+ * A set of ID's use for efficient decoding, so we can map pointers back to the newly loaded data
+ * without performing full look ups each time.
+ *
+ * This can be used as an old_pointer -> new_pointer lookup.
+ */
+UndoIDPtrMap *BKE_undosys_ID_map_create(void)
+{
+       UndoIDPtrMap *map = MEM_mallocN(sizeof(*map), __func__);
+       map->len_alloc = PMAP_DEFAULT_ALLOC;
+       map->refs = MEM_mallocN(sizeof(*map->refs) * map->len_alloc, __func__);
+       map->pmap = MEM_mallocN(sizeof(*map->pmap) * map->len_alloc, __func__);
+       map->len = 0;
+       return map;
+}
+void BKE_undosys_ID_map_destroy(UndoIDPtrMap *idpmap)
+{
+       MEM_SAFE_FREE(idpmap->refs);
+       MEM_SAFE_FREE(idpmap->pmap);
+       MEM_freeN(idpmap);
+}
+
+void BKE_undosys_ID_map_add(UndoIDPtrMap *map, ID *id)
+{
+       uint index;
+       if (id->lib != NULL) {
+               return;
+       }
+
+       if (undosys_ID_map_lookup_index(map, id, &index)) {
+               return;  /* exists. */
+       }
+
+       const uint len_src = map->len;
+       const uint len_dst = map->len + 1;
+       if (len_dst > map->len_alloc) {
+               map->len_alloc *= 2;
+               BLI_assert(map->len_alloc >= len_dst);
+               map->pmap = MEM_reallocN(map->pmap, sizeof(*map->pmap) * map->len_alloc);
+               map->refs = MEM_reallocN(map->refs, sizeof(*map->refs) * map->len_alloc);
+       }
+
+#if 0  /* Will be done automatically in callback. */
+       BLI_strncpy(map->refs[len_src].name, id->name, sizeof(id->name));
+#else
+       map->refs[len_src].name[0] = '\0';
+#endif
+       map->refs[len_src].ptr = id;
+
+       map->pmap[len_src].ptr = id;
+       map->pmap[len_src].index = len_src;
+       map->len = len_dst;
+
+       qsort(map->pmap, map->len, sizeof(*map->pmap), BLI_sortutil_cmp_ptr);
+}
+
+ID *BKE_undosys_ID_map_lookup(const UndoIDPtrMap *map, const ID *id_src)
+{
+       /* We should only ever lookup indices which exist! */
+       uint index;
+       if (!undosys_ID_map_lookup_index(map, id_src, &index)) {
+               BLI_assert(0);
+       }
+       ID *id_dst = map->refs[index].ptr;
+       BLI_assert(id_dst != NULL);
+       BLI_assert(STREQ(id_dst->name, map->refs[index].name));
+       return id_dst;
+}
+
+/** \} */
index 4fd2e227bc5c332f7f83c4d0c3b62a9381a44dc0..0b4ff13c7bd76e9759ebc6bd54dc774f7c74b6b6 100644 (file)
@@ -147,6 +147,8 @@ void BLO_update_defaults_startup_blend(struct Main *mainvar);
 
 struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
 
+struct Main *BLO_main_from_memfile(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
+
 #ifdef __cplusplus
 } 
 #endif
index d3c0130a63b4f5fba7d5397cb015a6701170174f..b713b963056b52b8d5407122b0b3a7be0e0c9362 100644 (file)
@@ -33,6 +33,8 @@
  *  \ingroup blenloader
  */
 
+struct Scene;
+
 typedef struct {
        void *next, *prev;
        const char *buf;
@@ -47,6 +49,12 @@ typedef struct MemFile {
        size_t size;
 } MemFile;
 
+typedef struct MemFileUndoData {
+       char filename[1024];  /* FILE_MAX */
+       MemFile memfile;
+       size_t undo_size;
+} MemFileUndoData;
+
 /* actually only used writefile.c */
 extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsigned int size);
 
@@ -54,5 +62,9 @@ extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *bu
 extern void BLO_memfile_free(MemFile *memfile);
 extern void BLO_memfile_merge(MemFile *first, MemFile *second);
 
-#endif
+/* utilities */
+extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
+extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename);
+
+#endif  /* __BLO_UNDOFILE_H__ */
 
index e85af67b105744648f3a05003a709ea6ce841019..7b51ddcce920b785ff38db29979a3afedc26e92a 100644 (file)
@@ -6300,7 +6300,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
        wm->defaultconf = NULL;
        wm->addonconf = NULL;
        wm->userconf = NULL;
-       
+       wm->undo_stack = NULL;
+
        BLI_listbase_clear(&wm->jobs);
        BLI_listbase_clear(&wm->drags);
        
index ffc7d7c83f515b0e2a123716be9e4d2d0bc8dc29..287936e276eed75189de5eb7c09f581d5a595dc5 100644 (file)
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/* open/close */
+#ifndef _WIN32
+#  include <unistd.h>
+#else
+#  include <io.h>
+#endif
 
 #include "MEM_guardedalloc.h"
 
@@ -42,6 +51,9 @@
 #include "BLI_blenlib.h"
 
 #include "BLO_undofile.h"
+#include "BLO_readfile.h"
+
+#include "BKE_main.h"
 
 /* keep last */
 #include "BLI_strict_flags.h"
@@ -124,3 +136,69 @@ void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsi
                current->size += size;
        }
 }
+
+struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *oldmain, struct Scene **r_scene)
+{
+       struct Main *bmain_undo = NULL;
+       BlendFileData *bfd = BLO_read_from_memfile(oldmain, oldmain->name, memfile, NULL, BLO_READ_SKIP_NONE);
+
+       if (bfd) {
+               bmain_undo = bfd->main;
+               if (r_scene) {
+                       *r_scene = bfd->curscene;
+               }
+
+               MEM_freeN(bfd);
+       }
+
+       return bmain_undo;
+}
+
+
+/**
+ * Saves .blend using undo buffer.
+ *
+ * \return success.
+ */
+bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
+{
+       MemFileChunk *chunk;
+       int file, oflags;
+
+       /* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
+        * however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
+        */
+
+       oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
+#ifdef O_NOFOLLOW
+       /* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
+       oflags |= O_NOFOLLOW;
+#else
+       /* TODO(sergey): How to deal with symlinks on windows? */
+#  ifndef _MSC_VER
+#    warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
+#  endif
+#endif
+       file = BLI_open(filename,  oflags, 0666);
+
+       if (file == -1) {
+               fprintf(stderr, "Unable to save '%s': %s\n",
+                       filename, errno ? strerror(errno) : "Unknown error opening file");
+               return false;
+       }
+
+       for (chunk = memfile->chunks.first; chunk; chunk = chunk->next) {
+               if (write(file, chunk->buf, chunk->size) != chunk->size) {
+                       break;
+               }
+       }
+
+       close(file);
+
+       if (chunk) {
+               fprintf(stderr, "Unable to save '%s': %s\n",
+                       filename, errno ? strerror(errno) : "Unknown error writing file");
+               return false;
+       }
+       return true;
+}
index 36e6ec4ba7f087bd763bf129992696bc87b91621..f27d68d063480ae93f9f4114933f4f23f6455733 100644 (file)
 #include "MEM_guardedalloc.h"
 
 #include "BLI_math.h"
+#include "BLI_array_utils.h"
 
 #include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
 #include "ED_armature.h"
+#include "ED_object.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoArmature {
        EditBone *act_edbone;
        ListBase lb;
+       size_t undo_size;
 } UndoArmature;
 
-static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
+static void undoarm_to_editarm(UndoArmature *uarm, bArmature *arm)
 {
-       UndoArmature *uarm = uarmv;
-       bArmature *arm = armv;
        EditBone *ebone;
 
        ED_armature_ebone_listbase_free(arm->edbo);
@@ -65,48 +75,117 @@ static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
        ED_armature_ebone_listbase_temp_clear(arm->edbo);
 }
 
-static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
+static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm)
 {
-       bArmature *arm = armv;
-       UndoArmature *uarm;
-       EditBone *ebone;
+       BLI_assert(BLI_array_is_zeroed(uarm, 1));
 
-       uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
+       /* TODO: include size of ID-properties. */
+       uarm->undo_size = 0;
 
        ED_armature_ebone_listbase_copy(&uarm->lb, arm->edbo);
 
        /* active bone */
        if (arm->act_edbone) {
-               ebone = arm->act_edbone;
+               EditBone *ebone = arm->act_edbone;
                uarm->act_edbone = ebone->temp.ebone;
        }
 
        ED_armature_ebone_listbase_temp_clear(&uarm->lb);
 
+       for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) {
+               uarm->undo_size += sizeof(EditBone);
+       }
+
        return uarm;
 }
 
-static void free_undoBones(void *uarmv)
+static void undoarm_free_data(UndoArmature *uarm)
 {
-       UndoArmature *uarm = uarmv;
-
        ED_armature_ebone_listbase_free(&uarm->lb);
-
-       MEM_freeN(uarm);
 }
 
-static void *get_armature_edit(bContext *C)
+static Object *editarm_object_from_context(bContext *C)
 {
        Object *obedit = CTX_data_edit_object(C);
        if (obedit && obedit->type == OB_ARMATURE) {
-               return obedit->data;
+               bArmature *arm = obedit->data;
+               if (arm->edbo != NULL) {
+                       return obedit;
+               }
        }
        return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_armature(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct ArmatureUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       UndoArmature data;
+} ArmatureUndoStep;
+
+static bool armature_undosys_poll(bContext *C)
 {
-       // XXX solve getdata()
-       undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
+       return editarm_object_from_context(C) != NULL;
 }
+
+static bool armature_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+       ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+       us->obedit_ref.ptr = editarm_object_from_context(C);
+       bArmature *arm = us->obedit_ref.ptr->data;
+       undoarm_from_editarm(&us->data, arm);
+       us->step.data_size = us->data.undo_size;
+       return true;
+}
+
+static void armature_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_EDIT);
+       BLI_assert(armature_undosys_poll(C));
+
+       ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       bArmature *arm = obedit->data;
+       undoarm_to_editarm(&us->data, arm);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void armature_undosys_step_free(UndoStep *us_p)
+{
+       ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+       undoarm_free_data(&us->data);
+}
+
+static void armature_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_armature_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit Armature";
+       ut->poll = armature_undosys_poll;
+       ut->step_encode = armature_undosys_step_encode;
+       ut->step_decode = armature_undosys_step_decode;
+       ut->step_free = armature_undosys_step_free;
+
+       ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(ArmatureUndoStep);
+}
+
+/** \} */
index 0dbe526117c320c57dfde2f85a9c258060230af1..ebf2b63bb49406c587cce84d1f9f090cb34088d5 100644 (file)
@@ -1229,7 +1229,10 @@ void ED_curve_editnurb_make(Object *obedit)
 
                if (actkey) {
                        // XXX strcpy(G.editModeTitleExtra, "(Key) ");
+                       /* TODO(campbell): undo_system: investigate why this was needed. */
+#if 0
                        undo_editmode_clear();
+#endif
                }
 
                if (editnurb) {
index f8f96eb3bc9c9977046d60d4fe3d51347eea7586..1bc2d6219b911639310a7d0e5e59d40143ef4676 100644 (file)
 
 #include "BLI_blenlib.h"
 #include "BLI_ghash.h"
+#include "BLI_array_utils.h"
 
 #include "BKE_context.h"
 #include "BKE_curve.h"
 #include "BKE_fcurve.h"
 #include "BKE_library.h"
 #include "BKE_animsys.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_util.h"
 #include "ED_curve.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #include "curve_intern.h"
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct {
        ListBase nubase;
        int actvert;
@@ -49,13 +60,12 @@ typedef struct {
        ListBase fcurves, drivers;
        int actnu;
        int flag;
+       size_t undo_size;
 } UndoCurve;
 
-static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
+static void undocurve_to_editcurve(UndoCurve *ucu, Curve *cu)
 {
-       Curve *cu = cu_v;
-       UndoCurve *undoCurve = ucu;
-       ListBase *undobase = &undoCurve->nubase;
+       ListBase *undobase = &ucu->nubase;
        ListBase *editbase = BKE_curve_editNurbs_get(cu);
        Nurb *nu, *newnu;
        EditNurb *editnurb = cu->editnurb;
@@ -63,19 +73,19 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
 
        BKE_nurbList_free(editbase);
 
-       if (undoCurve->undoIndex) {
+       if (ucu->undoIndex) {
                BKE_curve_editNurb_keyIndex_free(&editnurb->keyindex);
-               editnurb->keyindex = ED_curve_keyindex_hash_duplicate(undoCurve->undoIndex);
+               editnurb->keyindex = ED_curve_keyindex_hash_duplicate(ucu->undoIndex);
        }
 
        if (ad) {
                if (ad->action) {
                        free_fcurves(&ad->action->curves);
-                       copy_fcurves(&ad->action->curves, &undoCurve->fcurves);
+                       copy_fcurves(&ad->action->curves, &ucu->fcurves);
                }
 
                free_fcurves(&ad->drivers);
-               copy_fcurves(&ad->drivers, &undoCurve->drivers);
+               copy_fcurves(&ad->drivers, &ucu->drivers);
        }
 
        /* copy  */
@@ -89,75 +99,149 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
                BLI_addtail(editbase, newnu);
        }
 
-       cu->actvert = undoCurve->actvert;
-       cu->actnu = undoCurve->actnu;
-       cu->flag = undoCurve->flag;
+       cu->actvert = ucu->actvert;
+       cu->actnu = ucu->actnu;
+       cu->flag = ucu->flag;
        ED_curve_updateAnimPaths(cu);
 }
 
-static void *editCurve_to_undoCurve(void *UNUSED(edata), void *cu_v)
+static void undocurve_from_editcurve(UndoCurve *ucu, Curve *cu)
 {
-       Curve *cu = cu_v;
+       BLI_assert(BLI_array_is_zeroed(ucu, 1));
        ListBase *nubase = BKE_curve_editNurbs_get(cu);
-       UndoCurve *undoCurve;
        EditNurb *editnurb = cu->editnurb, tmpEditnurb;
        Nurb *nu, *newnu;
        AnimData *ad = BKE_animdata_from_id(&cu->id);
 
-       undoCurve = MEM_callocN(sizeof(UndoCurve), "undoCurve");
+       /* TODO: include size of fcurve & undoIndex */
+       // ucu->undo_size = 0;
 
        if (editnurb->keyindex) {
-               undoCurve->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
-               tmpEditnurb.keyindex = undoCurve->undoIndex;
+               ucu->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
+               tmpEditnurb.keyindex = ucu->undoIndex;
        }
 
        if (ad) {
                if (ad->action)
-                       copy_fcurves(&undoCurve->fcurves, &ad->action->curves);
+                       copy_fcurves(&ucu->fcurves, &ad->action->curves);
 
-               copy_fcurves(&undoCurve->drivers, &ad->drivers);
+               copy_fcurves(&ucu->drivers, &ad->drivers);
        }
 
        /* copy  */
        for (nu = nubase->first; nu; nu = nu->next) {
                newnu = BKE_nurb_duplicate(nu);
 
-               if (undoCurve->undoIndex) {
+               if (ucu->undoIndex) {
                        ED_curve_keyindex_update_nurb(&tmpEditnurb, nu, newnu);
                }
 
-               BLI_addtail(&undoCurve->nubase, newnu);
+               BLI_addtail(&ucu->nubase, newnu);
+
+               ucu->undo_size += (
+                       (nu->bezt ? (sizeof(BezTriple) * nu->pntsu) : 0) +
+                       (nu->bp ? (sizeof(BPoint) * (nu->pntsu * nu->pntsv)) : 0) +
+                       (nu->knotsu ? (sizeof(float) * KNOTSU(nu)) : 0) +
+                       (nu->knotsv ? (sizeof(float) * KNOTSV(nu)) : 0) +
+                       sizeof(Nurb));
        }
 
-       undoCurve->actvert = cu->actvert;
-       undoCurve->actnu = cu->actnu;
-       undoCurve->flag = cu->flag;
+       ucu->actvert = cu->actvert;
+       ucu->actnu = cu->actnu;
+       ucu->flag = cu->flag;
+}
+
+static void undocurve_free_data(UndoCurve *uc)
+{
+       BKE_nurbList_free(&uc->nubase);
+
+       BKE_curve_editNurb_keyIndex_free(&uc->undoIndex);
 
-       return undoCurve;
+       free_fcurves(&uc->fcurves);
+       free_fcurves(&uc->drivers);
 }
 
-static void free_undoCurve(void *ucv)
+static Object *editcurve_object_from_context(bContext *C)
 {
-       UndoCurve *undoCurve = ucv;
+       Object *obedit = CTX_data_edit_object(C);
+       if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+               Curve *cu = obedit->data;
+               if (BKE_curve_editNurbs_get(cu) != NULL) {
+                       return obedit;
+               }
+       }
+       return NULL;
+}
 
-       BKE_nurbList_free(&undoCurve->nubase);
+/** \} */
 
-       BKE_curve_editNurb_keyIndex_free(&undoCurve->undoIndex);
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-       free_fcurves(&undoCurve->fcurves);
-       free_fcurves(&undoCurve->drivers);
+typedef struct CurveUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       UndoCurve data;
+} CurveUndoStep;
 
-       MEM_freeN(undoCurve);
+static bool curve_undosys_poll(bContext *C)
+{
+       Object *obedit = editcurve_object_from_context(C);
+       return (obedit != NULL);
 }
 
-static void *get_data(bContext *C)
+static bool curve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-       Object *obedit = CTX_data_edit_object(C);
-       return obedit;
+       CurveUndoStep *us = (CurveUndoStep *)us_p;
+       us->obedit_ref.ptr = editcurve_object_from_context(C);
+       undocurve_from_editcurve(&us->data, us->obedit_ref.ptr->data);
+       us->step.data_size = us->data.undo_size;
+       return true;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_curve(bContext *C, const char *name)
+static void curve_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
 {
-       undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_EDIT);
+       BLI_assert(curve_undosys_poll(C));
+
+       CurveUndoStep *us = (CurveUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       undocurve_to_editcurve(&us->data, obedit->data);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
 }
+
+static void curve_undosys_step_free(UndoStep *us_p)
+{
+       CurveUndoStep *us = (CurveUndoStep *)us_p;
+       undocurve_free_data(&us->data);
+}
+
+static void curve_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       CurveUndoStep *us = (CurveUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_curve_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit Curve";
+       ut->poll = curve_undosys_poll;
+       ut->step_encode = curve_undosys_step_encode;
+       ut->step_decode = curve_undosys_step_decode;
+       ut->step_free = curve_undosys_step_free;
+
+       ut->step_foreach_ID_ref = curve_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(CurveUndoStep);
+}
+
+/** \} */
index a61f863b61e9db17ca953677f241d119c74bc93c..3a76d0333f9aebc8f4e1c486d4993fd77fd89af6 100644 (file)
@@ -29,6 +29,8 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_array_utils.h"
+
 
 #include "DNA_curve_types.h"
 #include "DNA_object_types.h"
 
 #include "BKE_context.h"
 #include "BKE_font.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_curve.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #define USE_ARRAY_STORE
 
 #ifdef USE_ARRAY_STORE
 #  define ARRAY_CHUNK_SIZE 32
 #endif
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoFont {
        wchar_t *textbuf;
        struct CharInfo *textbufinfo;
@@ -62,6 +74,8 @@ typedef struct UndoFont {
                BArrayState *textbufinfo;
        } store;
 #endif
+
+       size_t undo_size;
 } UndoFont;
 
 
@@ -202,23 +216,20 @@ static void uf_arraystore_free(UndoFont *uf)
 
                BLI_array_store_at_size_clear(&uf_arraystore.bs_stride);
        }
-
 }
 
 /** \} */
 
 #endif  /* USE_ARRAY_STORE */
 
-static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
+static void undofont_to_editfont(UndoFont *uf, Curve *cu)
 {
-       Curve *cu = (Curve *)ecu;
        EditFont *ef = cu->editfont;
-       const UndoFont *uf = uf_v;
 
        size_t final_size;
 
 #ifdef USE_ARRAY_STORE
-       uf_arraystore_expand(uf_v);
+       uf_arraystore_expand(uf);
 #endif
 
        final_size = sizeof(wchar_t) * (uf->len + 1);
@@ -233,16 +244,17 @@ static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
        ef->selstart = ef->selend = 0;
 
 #ifdef USE_ARRAY_STORE
-       uf_arraystore_expand_clear(uf_v);
+       uf_arraystore_expand_clear(uf);
 #endif
 }
 
-static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
+static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
 {
-       Curve *cu = (Curve *)ecu;
+       BLI_assert(BLI_array_is_zeroed(uf, 1));
+
        EditFont *ef = cu->editfont;
 
-       UndoFont *uf = MEM_callocN(sizeof(*uf), __func__);
+       size_t mem_used_prev = MEM_get_memory_in_use();
 
        size_t final_size;
 
@@ -269,13 +281,15 @@ static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
        }
 #endif
 
+       size_t mem_used_curr = MEM_get_memory_in_use();
+
+       uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
+
        return uf;
 }
 
-static void free_undoFont(void *uf_v)
+static void undofont_free_data(UndoFont *uf)
 {
-       UndoFont *uf = uf_v;
-
 #ifdef USE_ARRAY_STORE
        {
                LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
@@ -291,21 +305,91 @@ static void free_undoFont(void *uf_v)
        if (uf->textbufinfo) {
                MEM_freeN(uf->textbufinfo);
        }
-
-       MEM_freeN(uf);
 }
 
-static void *get_undoFont(bContext *C)
+static Object *editfont_object_from_context(bContext *C)
 {
        Object *obedit = CTX_data_edit_object(C);
        if (obedit && obedit->type == OB_FONT) {
-               return obedit->data;
+               Curve *cu = obedit->data;
+               EditFont *ef = cu->editfont;
+               if (ef != NULL) {
+                       return obedit;
+               }
        }
        return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_font(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct FontUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       UndoFont data;
+} FontUndoStep;
+
+static bool font_undosys_poll(bContext *C)
+{
+       return editfont_object_from_context(C) != NULL;
+}
+
+static bool font_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+       FontUndoStep *us = (FontUndoStep *)us_p;
+       us->obedit_ref.ptr = editfont_object_from_context(C);
+       Curve *cu = us->obedit_ref.ptr->data;
+       undofont_from_editfont(&us->data, cu);
+       us->step.data_size = us->data.undo_size;
+       return true;
+}
+
+static void font_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_EDIT);
+       BLI_assert(font_undosys_poll(C));
+
+       FontUndoStep *us = (FontUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       Curve *cu = obedit->data;
+       undofont_to_editfont(&us->data, cu);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void font_undosys_step_free(UndoStep *us_p)
+{
+       FontUndoStep *us = (FontUndoStep *)us_p;
+       undofont_free_data(&us->data);
+}
+
+static void font_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
 {
-       undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
+       FontUndoStep *us = (FontUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
 }
+
+/* Export for ED_undo_sys. */
+void ED_font_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit Font";
+       ut->poll = font_undosys_poll;
+       ut->step_encode = font_undosys_step_encode;
+       ut->step_decode = font_undosys_step_decode;
+       ut->step_free = font_undosys_step_free;
+
+       ut->step_foreach_ID_ref = font_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(FontUndoStep);
+}
+
+/** \} */
index 91c9a36c31f589e756550384a0e0863f38639199..241bcbea4af27f8e6e74473af0b302e031d30610 100644 (file)
@@ -49,6 +49,7 @@ struct Scene;
 struct ViewContext;
 struct wmKeyConfig;
 struct wmOperator;
+struct UndoType;
 
 typedef struct EditBone {
        struct EditBone *next, *prev;
@@ -139,7 +140,7 @@ bool ED_armature_select_pick(struct bContext *C, const int mval[2], bool extend,
 int join_armature_exec(struct bContext *C, struct wmOperator *op);
 struct Bone *get_indexed_bone(struct Object *ob, int index);
 float ED_rollBoneToVector(EditBone *bone, const float new_up_axis[3], const bool axis_only);
-EditBone *ED_armature_bone_find_name(const ListBase *edbo, const char *name);
+EditBone *ED_armature_bone_find_name(const struct ListBase *edbo, const char *name);
 EditBone *ED_armature_bone_get_mirrored(const struct ListBase *edbo, EditBone *ebo);
 void ED_armature_sync_selection(struct ListBase *edbo);
 void ED_armature_validate_active(struct bArmature *arm);
@@ -178,8 +179,6 @@ void unique_editbone_name(struct ListBase *ebones, char *name, EditBone *bone);
 void ED_armature_bone_rename(struct bArmature *arm, const char *oldnamep, const char *newnamep);
 void ED_armature_bones_flip_names(struct bArmature *arm, struct ListBase *bones_names, const bool do_strip_numbers);
 
-void undo_push_armature(struct bContext *C, const char *name);
-
 /* low level selection functions which handle */
 int  ED_armature_ebone_selectflag_get(const EditBone *ebone);
 void ED_armature_ebone_selectflag_set(EditBone *ebone, int flag);
@@ -187,6 +186,9 @@ void ED_armature_ebone_select_set(EditBone *ebone, bool select);
 void ED_armature_ebone_selectflag_enable(EditBone *ebone, int flag);
 void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag);
 
+/* editarmature_undo.c */
+void ED_armature_undosys_type(struct UndoType *ut);
+
 /* armature_utils.c */
 void ED_armature_ebone_listbase_temp_clear(struct ListBase *lb);
 void ED_armature_ebone_listbase_free(struct ListBase *lb);
index d45e52d4c5a485893e891e1454b12e3cbc9edd1f..da726cb8000e2327e9edb141c45ae125227717ae 100644 (file)
@@ -41,6 +41,7 @@ struct Curve;
 struct EditNurb;
 struct BezTriple;
 struct BPoint;
+struct UndoType;
 
 /* curve_ops.c */
 void    ED_operatortypes_curve(void);
@@ -48,7 +49,7 @@ void    ED_operatormacros_curve(void);
 void    ED_keymap_curve(struct wmKeyConfig *keyconf);
 
 /* editcurve.c */
-ListBase *object_editcurve_get(struct Object *ob);
+struct ListBase *object_editcurve_get(struct Object *ob);
 
 void    ED_curve_editnurb_load(struct Object *obedit);
 void    ED_curve_editnurb_make(struct Object *obedit);
@@ -72,7 +73,7 @@ void ED_curve_select_all(struct EditNurb *editnurb);
 void ED_curve_select_swap(struct EditNurb *editnurb, bool hide_handles);
 
 /* editcurve_undo.c */
-void undo_push_curve(struct bContext *C, const char *name);
+void ED_curve_undosys_type(struct UndoType *ut);
 
 /* editfont.c */
 void    ED_curve_editfont_load(struct Object *obedit);
@@ -91,7 +92,8 @@ bool ED_curve_active_center(struct Curve *cu, float center[3]);
 bool ED_curve_editfont_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
 
 /* editfont_undo.c */
-void    undo_push_font(struct bContext *C, const char *name);
+void ED_font_undosys_type(struct UndoType *ut);
+
 
 #if 0
 /* debug only */
index b652fb4c00b6ea24f6917d65a208e7124d6b0953..b30929f53079d5b495141e5a101b28e6137cb835 100644 (file)
@@ -31,6 +31,8 @@
 #define __ED_LATTICE_H__
 
 struct wmKeyConfig;
+struct UndoType;
+struct Object;
 
 /* lattice_ops.c */
 void ED_operatortypes_lattice(void);
@@ -41,6 +43,6 @@ void ED_lattice_flags_set(struct Object *obedit, int flag);
 bool ED_lattice_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
 
 /* editlattice_undo.c */
-void undo_push_lattice(struct bContext *C, const char *name);
+void ED_lattice_undosys_type(struct UndoType *ut);
 
 #endif  /* __ED_LATTICE_H__ */
index 232d7d1d23425316d32ea259cd99a8ff60909707..9982c87a76449a30506011706eb6a728afc66adf 100644 (file)
@@ -34,6 +34,7 @@
 struct bContext;
 struct Object;
 struct wmKeyConfig;
+struct UndoType;
 
 void ED_operatortypes_metaball(void);
 void ED_operatormacros_metaball(void);
@@ -47,6 +48,7 @@ void ED_mball_editmball_free(struct Object *obedit);
 void ED_mball_editmball_make(struct Object *obedit);
 void ED_mball_editmball_load(struct Object *obedit);
 
-void undo_push_mball(struct bContext *C, const char *name);
+/* editmball_undo.c */
+void ED_mball_undosys_type(struct UndoType *ut);
 
 #endif  /* __ED_MBALL_H__ */
index 40d796aeed1283c4819f2047638434f6e53873e0..349a1944a237b6a220328f85f0116cf82c0527b4 100644 (file)
@@ -62,6 +62,7 @@ struct UvMapVert;
 struct ToolSettings;
 struct Object;
 struct rcti;
+struct UndoType;
 
 /* editmesh_utils.c */
 void           EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em, const int axis,
@@ -98,8 +99,6 @@ void EDBM_selectmode_flush(struct BMEditMesh *em);
 void EDBM_deselect_flush(struct BMEditMesh *em);
 void EDBM_select_flush(struct BMEditMesh *em);
 
-void undo_push_mesh(struct bContext *C, const char *name);
-
 bool EDBM_vert_color_check(struct BMEditMesh *em);
 
 void EDBM_mesh_hide(struct BMEditMesh *em, bool swap);
@@ -130,6 +129,9 @@ void EDBM_flag_disable_all(struct BMEditMesh *em, const char hflag);
 bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e,
                        struct ARegion *ar, struct View3D *v3d, struct Object *obedit);
 
+/* editmesh_undo.c */
+void ED_mesh_undosys_type(struct UndoType *ut);
+
 /* editmesh_select.c */
 void EDBM_select_mirrored(
         struct BMEditMesh *em, const int axis, const bool extend,
index 5b2fdf29dd5312ed824193efd0bdddb311b2d5c1..7faf975ea9132c012d01fc5560e69e9f05ed4924 100644 (file)
@@ -196,6 +196,7 @@ void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Objec
 bool ED_object_mode_compat_test(const struct Object *ob, eObjectMode mode);
 bool ED_object_mode_compat_set(struct bContext *C, struct Object *ob, eObjectMode mode, struct ReportList *reports);
 void ED_object_mode_toggle(struct bContext *C, eObjectMode mode);
+void ED_object_mode_set(struct bContext *C, eObjectMode mode);
 
 /* object_modifier.c */
 enum {
index 79aa0a3a5ed7a1952e029a9a12734ee8fd9b20f3..246419d64aa5d78a7d2e153d843833d33e0f10da 100644 (file)
 struct bContext;
 struct wmKeyConfig;
 struct wmOperator;
+struct ImBuf;
+struct Image;
+struct UndoStep;
+struct UndoType;
 
 /* paint_ops.c */
 void ED_operatortypes_paint(void);
 void ED_operatormacros_paint(void);
 void ED_keymap_paint(struct wmKeyConfig *keyconf);
 
-/* paint_undo.c */
-enum {
-       UNDO_PAINT_IMAGE    = 0,
-       UNDO_PAINT_MESH     = 1,
-};
-
-typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
-typedef void (*UndoFreeCb)(struct ListBase *lb);
-typedef bool (*UndoCleanupCb)(struct bContext *C, struct ListBase *lb);
-
-int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
-void ED_undo_paint_step_num(struct bContext *C, int type, int num);
-const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, bool *r_active);
-void ED_undo_paint_free(void);
-bool ED_undo_paint_is_valid(int type, const char *name);
-bool ED_undo_paint_empty(int type);
-void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup);
-void ED_undo_paint_push_end(int type);
-
 /* paint_image.c */
 void ED_imapaint_clear_partial_redraw(void);
 void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old);
@@ -61,6 +46,14 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperat
 /* paint_image_undo.c */
 void ED_image_undo_push_begin(const char *name);
 void ED_image_undo_push_end(void);
-void ED_image_undo_restore(void);
+void ED_image_undo_restore(struct UndoStep *us);
+
+void ED_image_undosys_type(struct UndoType *ut);
+
+/* paint_curve_undo.c */
+void ED_paintcurve_undo_push_begin(const char *name);
+void ED_paintcurve_undo_push_end(void);
+
+void ED_paintcurve_undosys_type(struct UndoType *ut);
 
 #endif /* __ED_PAINT_H__ */
index 6cb8c0cfb196aa1fb4adbb67b87558c34ea5b6ab..fa5abb8e1ddff84b7fa498e084fe81140d79eb0f 100644 (file)
@@ -38,6 +38,7 @@ struct ParticleEditSettings;
 struct rcti;
 struct PTCacheEdit;
 struct Scene;
+struct UndoType;
 
 /* particle edit mode */
 void PE_free_ptcache_edit(struct PTCacheEdit *edit);
@@ -61,14 +62,10 @@ int PE_circle_select(struct bContext *C, int selecting, const int mval[2], float
 int PE_lasso_select(struct bContext *C, const int mcords[][2], const short moves, bool extend, bool select);
 void PE_deselect_all_visible(struct PTCacheEdit *edit);
 
-/* undo */
+/* particle_edit_undo.c */
+void ED_particle_undosys_type(struct UndoType *ut);
+
 void PE_undo_push(struct Scene *scene, const char *str);
-void PE_undo_step(struct Scene *scene, int step);
-void PE_undo(struct Scene *scene);
-void PE_redo(struct Scene *scene);
-bool PE_undo_is_valid(struct Scene *scene);
-void PE_undo_number(struct Scene *scene, int nr);
-const char *PE_undo_get_name(struct Scene *scene, int nr, bool *r_active);
 
 #endif /* __ED_PARTICLE_H__ */
 
index 6daaac5bb42ec2ea430ca169ab7392ade3a6c6cb..7c17e7b68c3ae6def6a2849d984f8c8c34b6ff76 100644 (file)
@@ -36,6 +36,9 @@ struct Object;
 struct RegionView3D;
 struct ViewContext;
 struct rcti;
+struct UndoStep;
+struct UndoType;
+struct ListBase;
 
 /* sculpt.c */
 void ED_operatortypes_sculpt(void);
@@ -43,4 +46,7 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], struct ARegion *ar,
                                  struct RegionView3D *rv3d, struct Object *ob);
 int  ED_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, const struct rcti *rect, bool select, bool extend);
 
+/* sculpt_undo.c */
+void ED_sculpt_undosys_type(struct UndoType *ut);
+
 #endif /* __ED_SCULPT_H__ */
index 5df7d9cfaef88c285ae25bca6c3f43d0d2fa95ac..5517e50aef439e512bafbecbf8142a4a1850cc40 100644 (file)
 #ifndef __ED_TEXT_H__
 #define __ED_TEXT_H__
 
-struct bContext;
 struct SpaceText;
 struct ARegion;
+struct UndoType;
 
-void ED_text_undo_step(struct bContext *C, int step);
 bool ED_text_region_location_from_cursor(struct SpaceText *st, struct ARegion *ar, const int cursor_co[2], int r_pixel_co[2]);
 
-#endif /* __ED_TEXT_H__ */
+/* text_undo.c */
+void ED_text_undosys_type(struct UndoType *ut);
 
+#endif /* __ED_TEXT_H__ */
index 60c4b3593aa5e07393ca87fd13a336e79c243a90..5a373cebac1bbf845c32bb13f47182ee1eb844ea 100644 (file)
@@ -35,6 +35,9 @@ struct bContext;
 struct SpaceLink;
 struct wmOperator;
 struct wmOperatorType;
+struct UndoStack;
+struct ScrArea;
+struct PackedFile;
 
 /* ed_util.c */
 
@@ -70,16 +73,12 @@ void    ED_undo_operator_repeat_cb_evt(struct bContext *C, void *arg_op, int arg
 
 bool    ED_undo_is_valid(const struct bContext *C, const char *undoname);
 
-/* undo_editmode.c */
-void undo_editmode_push(struct bContext *C, const char *name, 
-                        void * (*getdata)(struct bContext *C),
-                        void (*freedata)(void *),
-                        void (*to_editmode)(void *, void *, void *),
-                        void *(*from_editmode)(void *, void *),
-                        int (*validate_undo)(void *, void *));
+/* undo_system_types.c */
+void ED_undosys_type_init(void);
+void ED_undosys_type_free(void);
 
-
-void    undo_editmode_clear(void);
+/* memfile_undo.c */
+struct MemFile *ED_undosys_stack_memfile_get_active(struct UndoStack *ustack);
 
 /* ************** XXX OLD CRUFT WARNING ************* */
 
index aa817928f92553fe145279775b1882a19340a452..7a7372f5a6a791779f096fee8e98aab61813f9d6 100644 (file)
@@ -34,6 +34,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "BLI_utildefines.h"
+#include "BLI_array_utils.h"
 
 #include "DNA_curve_types.h"
 #include "DNA_lattice_types.h"
 #include "DNA_scene_types.h"
 
 #include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_lattice.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #include "lattice_intern.h"
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoLattice {
        BPoint *def;
        int pntsu, pntsv, pntsw, actbp;
+       size_t undo_size;
 } UndoLattice;
 
-static void undoLatt_to_editLatt(void *data, void *edata, void *UNUSED(obdata))
+static void undolatt_to_editlatt(UndoLattice *ult, EditLatt *editlatt)
 {
-       UndoLattice *ult = (UndoLattice *)data;
-       EditLatt *editlatt = (EditLatt *)edata;
-       int a = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
+       int len = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
 
-       memcpy(editlatt->latt->def, ult->def, a * sizeof(BPoint));
+       memcpy(editlatt->latt->def, ult->def, sizeof(BPoint) * len);
        editlatt->latt->actbp = ult->actbp;
 }
 
-static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
+static void *undolatt_from_editlatt(UndoLattice *ult, EditLatt *editlatt)
 {
-       UndoLattice *ult = MEM_callocN(sizeof(UndoLattice), "UndoLattice");
-       EditLatt *editlatt = (EditLatt *)edata;
+       BLI_assert(BLI_array_is_zeroed(ult, 1));
 
        ult->def = MEM_dupallocN(editlatt->latt->def);
        ult->pntsu = editlatt->latt->pntsu;
@@ -73,17 +82,19 @@ static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
        ult->pntsw = editlatt->latt->pntsw;
        ult->actbp = editlatt->latt->actbp;
 
+       ult->undo_size += sizeof(*ult->def) * ult->pntsu * ult->pntsv * ult->pntsw;
+
        return ult;
 }
 
-static void free_undoLatt(void *data)
+static void undolatt_free_data(UndoLattice *ult)
 {
-       UndoLattice *ult = (UndoLattice *)data;
-
-       if (ult->def) MEM_freeN(ult->def);
-       MEM_freeN(ult);
+       if (ult->def) {
+               MEM_freeN(ult->def);
+       }
 }
 
+#if 0
 static int validate_undoLatt(void *data, void *edata)
 {
        UndoLattice *ult = (UndoLattice *)data;
@@ -93,21 +104,92 @@ static int validate_undoLatt(void *data, void *edata)
                ult->pntsv == editlatt->latt->pntsv &&
                ult->pntsw == editlatt->latt->pntsw);
 }
+#endif
 
-static void *get_editlatt(bContext *C)
+static Object *editlatt_object_from_context(bContext *C)
 {
        Object *obedit = CTX_data_edit_object(C);
-
        if (obedit && obedit->type == OB_LATTICE) {
                Lattice *lt = obedit->data;
-               return lt->editlatt;
+               if (lt->editlatt != NULL) {
+                       return obedit;
+               }
        }
 
        return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_lattice(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct LatticeUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       UndoLattice data;
+} LatticeUndoStep;
+
+static bool lattice_undosys_poll(bContext *C)
+{
+       return editlatt_object_from_context(C) != NULL;
+}
+
+static bool lattice_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-       undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
+       LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+       us->obedit_ref.ptr = editlatt_object_from_context(C);
+       Lattice *lt = us->obedit_ref.ptr->data;
+       undolatt_from_editlatt(&us->data, lt->editlatt);
+       us->step.data_size = us->data.undo_size;
+       return true;
 }
+
+static void lattice_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_EDIT);
+       BLI_assert(lattice_undosys_poll(C));
+
+       LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       Lattice *lt = obedit->data;
+       EditLatt *editlatt = lt->editlatt;
+       undolatt_to_editlatt(&us->data, editlatt);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void lattice_undosys_step_free(UndoStep *us_p)
+{
+       LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+       undolatt_free_data(&us->data);
+}
+
+static void lattice_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       LatticeUndoStep *us = (LatticeUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_lattice_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit Lattice";
+       ut->poll = lattice_undosys_poll;
+       ut->step_encode = lattice_undosys_step_encode;
+       ut->step_decode = lattice_undosys_step_decode;
+       ut->step_free = lattice_undosys_step_free;
+
+       ut->step_foreach_ID_ref = lattice_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(LatticeUndoStep);
+}
+
+/** \} */
index ab91f4b34c772c1c87b33da1913e02ba130e8429..220f9cc0287111032ac2ab0b01d66a8e05aa93e2 100644 (file)
 #include "DNA_key_types.h"
 
 #include "BLI_listbase.h"
+#include "BLI_array_utils.h"
+#include "BLI_alloca.h"
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_context.h"
 #include "BKE_key.h"
 #include "BKE_mesh.h"
 #include "BKE_editmesh.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_mesh.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
 #define USE_ARRAY_STORE
 
 #ifdef USE_ARRAY_STORE
@@ -60,6 +68,9 @@
 #  include "BLI_task.h"
 #endif
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
 
 #ifdef USE_ARRAY_STORE
 
@@ -95,6 +106,8 @@ typedef struct UndoMesh {
                BArrayState *mselect;
        } store;
 #endif  /* USE_ARRAY_STORE */
+
+       size_t undo_size;
 } UndoMesh;
 
 
@@ -474,23 +487,17 @@ static void um_arraystore_free(UndoMesh *um)
 
 /* for callbacks */
 /* undo simply makes copies of a bmesh */
-static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
+static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
 {
-
+       BLI_assert(BLI_array_is_zeroed(um, 1));
 #ifdef USE_ARRAY_STORE_THREAD
        /* changes this waits is low, but must have finished */
        if (um_arraystore.task_pool) {
                BLI_task_pool_work_and_wait(um_arraystore.task_pool);
        }
 #endif
-
-       BMEditMesh *em = emv;
-       Mesh *obme = obdata;
-
-       UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh");
-
        /* make sure shape keys work */
-       um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
+       um->me.key = key ? BKE_key_copy_nolib(key) : NULL;
 
        /* BM_mesh_validate(em->bm); */ /* for troubleshooting */
 
@@ -536,13 +543,12 @@ static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
        return um;
 }
 
-static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
+static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh)
 {
-       BMEditMesh *em = em_v, *em_tmp;
+       BMEditMesh *em_tmp;
        Object *ob = em->ob;
-       UndoMesh *um = um_v;
        BMesh *bm;
-       Key *key = ((Mesh *) obdata)->key;
+       Key *key = obmesh->key;
 
 #ifdef USE_ARRAY_STORE
 #ifdef USE_ARRAY_STORE_THREAD
@@ -615,9 +621,8 @@ static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
 #endif
 }
 
-static void free_undo(void *um_v)
+static void undomesh_free_data(UndoMesh *um)
 {
-       UndoMesh *um = um_v;
        Mesh *me = &um->me;
 
 #ifdef USE_ARRAY_STORE
@@ -644,28 +649,176 @@ static void free_undo(void *um_v)
        }
 
        BKE_mesh_free(me);
-       MEM_freeN(me);
 }
 
-static void *getEditMesh(bContext *C)
+static Object *editmesh_object_from_context(bContext *C)
 {
        Object *obedit = CTX_data_edit_object(C);
        if (obedit && obedit->type == OB_MESH) {
                Mesh *me = obedit->data;
-               return me->edit_btmesh;
+               if (me->edit_btmesh != NULL) {
+                       return obedit;
+               }
        }
        return NULL;
 }
 
-/* and this is all the undo system needs to know */
-void undo_push_mesh(bContext *C, const char *name)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct MeshUndoStep {
+       UndoStep step;
+       /* Use for all ID lookups (can be NULL). */
+       struct UndoIDPtrMap *id_map;
+
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       /* Needed for MTexPoly's image use. */
+       UndoRefID_Object *image_array_ref;
+       UndoMesh data;
+} MeshUndoStep;
+
+static void mesh_undosys_step_encode_store_ids(MeshUndoStep *us)
+{
+       Mesh *me = us->obedit_ref.ptr->data;
+       BMesh *bm = me->edit_btmesh->bm;
+       const int mtex_len = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
+       if (mtex_len != 0) {
+               ID **id_src = BLI_array_alloca(id_src,  mtex_len);
+               memset(id_src, 0x0, sizeof(*id_src) * mtex_len);
+
+               BMIter iter;
+               BMFace *efa;
+
+               if (us->id_map == NULL) {
+                       us->id_map = BKE_undosys_ID_map_create();
+               }
+
+               uint cd_poly_tex_offset_first = CustomData_get_n_offset(&bm->pdata, CD_MTEXPOLY, 0);
+               uint cd_poly_tex_offset_end = cd_poly_tex_offset_first + (sizeof(MTexPoly) * mtex_len);
+               BM_ITER_MESH(efa, &iter, bm, BM_FACES_OF_MESH) {
+                       for (uint cd_poly_tex_offset = cd_poly_tex_offset_first, i = 0;
+                            cd_poly_tex_offset < cd_poly_tex_offset_end;
+                            cd_poly_tex_offset += sizeof(MTexPoly), i++)
+                       {
+                               const MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
+                               if (tf->tpage != NULL) {
+                                       if ((ID *)tf->tpage != id_src[i]) {
+                                               BKE_undosys_ID_map_add(us->id_map, (ID *)tf->tpage);
+                                               id_src[i] = (ID *)tf->tpage;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static void mesh_undosys_step_decode_restore_ids(MeshUndoStep *us)
 {
-       /* em->ob gets out of date and crashes on mesh undo,
-        * this is an easy way to ensure its OK
-        * though we could investigate the matter further. */
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
-       em->ob = obedit;
+       Mesh *me = us->obedit_ref.ptr->data;
+       BMesh *bm = me->edit_btmesh->bm;
+       const int mtex_len = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
+       if (mtex_len != 0 && us->id_map) {
+               BMIter iter;
+               BMFace *efa;
+
+               ID **id_dst = BLI_array_alloca(id_dst,  mtex_len);
+               ID **id_src = BLI_array_alloca(id_src,  mtex_len);
+               memset(id_src, 0x0, sizeof(*id_src) * mtex_len);
+
+               uint cd_poly_tex_offset_first = CustomData_get_n_offset(&bm->pdata, CD_MTEXPOLY, 0);
+               uint cd_poly_tex_offset_end = cd_poly_tex_offset_first + (sizeof(MTexPoly) * mtex_len);
+               BM_ITER_MESH(efa, &iter, bm, BM_FACES_OF_MESH) {
+                       for (uint cd_poly_tex_offset = cd_poly_tex_offset_first, i = 0;
+                            cd_poly_tex_offset < cd_poly_tex_offset_end;
+                            cd_poly_tex_offset += sizeof(MTexPoly), i++)
+                       {
+                               MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset);
+                               if (tf->tpage != NULL) {
+                                       if ((ID *)tf->tpage == id_src[i]) {
+                                               tf->tpage = (Image *)id_dst[i];
+                                       }
+                                       else {
+                                               id_src[i] = (ID *)tf->tpage;
+                                               tf->tpage = (Image *)BKE_undosys_ID_map_lookup(us->id_map, (ID *)tf->tpage);
+                                               id_dst[i] = (ID *)tf->tpage;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+static bool mesh_undosys_poll(bContext *C)
+{
+       return editmesh_object_from_context(C) != NULL;
+}
+
+static bool mesh_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+       MeshUndoStep *us = (MeshUndoStep *)us_p;
+       us->obedit_ref.ptr = editmesh_object_from_context(C);
+       Mesh *me = us->obedit_ref.ptr->data;
+       undomesh_from_editmesh(&us->data, me->edit_btmesh, me->key);
+       mesh_undosys_step_encode_store_ids(us);
+       us->step.data_size = us->data.undo_size;
+       return true;
+}
+
+static void mesh_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_EDIT);
+       BLI_assert(mesh_undosys_poll(C));
+
+       MeshUndoStep *us = (MeshUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       Mesh *me = obedit->data;
+       BMEditMesh *em = me->edit_btmesh;
+       undomesh_to_editmesh(&us->data, em, obedit->data);
+       mesh_undosys_step_decode_restore_ids(us);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void mesh_undosys_step_free(UndoStep *us_p)
+{
+       MeshUndoStep *us = (MeshUndoStep *)us_p;
+       undomesh_free_data(&us->data);
+
+       if (us->id_map != NULL) {
+               BKE_undosys_ID_map_destroy(us->id_map);
+       }
+}
+
+static void mesh_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       MeshUndoStep *us = (MeshUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+       if (us->id_map != NULL) {
+               BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data);
+       }
+}
+
+/* Export for ED_undo_sys. */
+void ED_mesh_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit Mesh";
+       ut->poll = mesh_undosys_poll;
+       ut->step_encode = mesh_undosys_step_encode;
+       ut->step_decode = mesh_undosys_step_decode;
+       ut->step_free = mesh_undosys_step_free;
+
+       ut->step_foreach_ID_ref = mesh_undosys_foreach_ID_ref;
 
-       undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(MeshUndoStep);
 }
+
+/** \} */
index 974bfb237d38bb26baf77392ee5ba3d2cb55eba9..dc64f61a916aaef6ec4987b7fa54fe7b9123e340 100644 (file)
 
 #include "BLI_utildefines.h"
 #include "BLI_listbase.h"
+#include "BLI_array_utils.h"
 
 #include "DNA_defs.h"
 #include "DNA_meta_types.h"
 #include "DNA_object_types.h"
 
 #include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_mball.h"
 #include "ED_util.h"
 
+#include "WM_types.h"
+#include "WM_api.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoMBall {
        ListBase editelems;
        int lastelem_index;
+       size_t undo_size;
 } UndoMBall;
 
 /* free all MetaElems from ListBase */
@@ -58,11 +70,8 @@ static void freeMetaElemlist(ListBase *lb)
        }
 }
 
-static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata))
+static void undomball_to_editmball(UndoMBall *umb, MetaBall *mb)
 {
-       MetaBall *mb = mb_v;
-       UndoMBall *umb = umb_v;
-
        freeMetaElemlist(mb->editelems);
        mb->lastelem = NULL;
 
@@ -75,18 +84,15 @@ static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata)
                        mb->lastelem = ml_edit;
                }
        }
-       
 }
 
-static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
+static void *editmball_from_undomball(UndoMBall *umb, MetaBall *mb)
 {
-       MetaBall *mb = mb_v;
-       UndoMBall *umb;
+       BLI_assert(BLI_array_is_zeroed(umb, 1));
 
        /* allocate memory for undo ListBase */
-       umb = MEM_callocN(sizeof(UndoMBall), __func__);
        umb->lastelem_index = -1;
-       
+
        /* copy contents of current ListBase to the undo ListBase */
        int index = 0;
        for (MetaElem *ml_edit = mb->editelems->first; ml_edit; ml_edit = ml_edit->next, index += 1) {
@@ -95,37 +101,99 @@ static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
                if (ml_edit == mb->lastelem) {
                        umb->lastelem_index = index;
                }
+               umb->undo_size += sizeof(MetaElem);
        }
-       
+
        return umb;
 }
 
 /* free undo ListBase of MetaElems */
-static void free_undoMball(void *umb_v)
+static void undomball_free_data(UndoMBall *umb)
 {
-       UndoMBall *umb = umb_v;
-       
        freeMetaElemlist(&umb->editelems);
-       MEM_freeN(umb);
 }
 
-static MetaBall *metaball_get_obdata(Object *ob)
+static Object *editmball_object_from_context(bContext *C)
 {
-       if (ob && ob->type == OB_MBALL) {
-               return ob->data;
+       Object *obedit = CTX_data_edit_object(C);
+       if (obedit && obedit->type == OB_MBALL) {
+               MetaBall *mb = obedit->data;
+               if (mb->editelems != NULL) {
+                       return obedit;
+               }
        }
        return NULL;
 }
 
+/** \} */
 
-static void *get_data(bContext *C)
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct MBallUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-editmode. */
+       UndoRefID_Object obedit_ref;
+       UndoMBall data;
+} MBallUndoStep;
+
+static bool mball_undosys_poll(bContext *C)
 {
-       Object *obedit = CTX_data_edit_object(C);
-       return metaball_get_obdata(obedit);
+       return editmball_object_from_context(C) != NULL;
 }
 
-/* this is undo system for MetaBalls */
-void undo_push_mball(bContext *C, const char *name)
+static bool mball_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-       undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
+       MBallUndoStep *us = (MBallUndoStep *)us_p;
+       us->obedit_ref.ptr = editmball_object_from_context(C);
+       MetaBall *mb = us->obedit_ref.ptr->data;
+       editmball_from_undomball(&us->data, mb);
+       us->step.data_size = us->data.undo_size;
+       return true;
 }
+
+static void mball_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       ED_object_mode_set(C, OB_MODE_EDIT);
+
+       MBallUndoStep *us = (MBallUndoStep *)us_p;
+       Object *obedit = us->obedit_ref.ptr;
+       MetaBall *mb = obedit->data;
+       undomball_to_editmball(&us->data, mb);
+       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+}
+
+static void mball_undosys_step_free(UndoStep *us_p)
+{
+       MBallUndoStep *us = (MBallUndoStep *)us_p;
+       undomball_free_data(&us->data);
+}
+
+static void mball_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       MBallUndoStep *us = (MBallUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
+}
+
+/* Export for ED_undo_sys. */
+void ED_mball_undosys_type(UndoType *ut)
+{
+       ut->name = "Edit MBall";
+       ut->poll = mball_undosys_poll;
+       ut->step_encode = mball_undosys_step_encode;
+       ut->step_decode = mball_undosys_step_decode;
+       ut->step_free = mball_undosys_step_free;
+
+       ut->step_foreach_ID_ref = mball_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(MBallUndoStep);
+
+}
+
+/** \} */
index f67ecbce1d01b7c22a2128190b4b2edb357ce22b..cbdc9685f2c5d7c8815d21d87eb1231907f4779f 100644 (file)
@@ -85,6 +85,7 @@
 #include "BKE_editlattice.h"
 #include "BKE_editmesh.h"
 #include "BKE_report.h"
+#include "BKE_undo_system.h"
 
 #include "ED_armature.h"
 #include "ED_curve.h"
index fda342f8a4d0eb32bb8d2623fb0283b33e3559eb..d70a69e30f8dce1dbaa99bf073874b48153906cf 100644 (file)
@@ -136,3 +136,43 @@ void ED_object_mode_toggle(bContext *C, eObjectMode mode)
                }
        }
 }
+
+/* Wrapper for operator  */
+void ED_object_mode_set(bContext *C, eObjectMode mode)
+{
+#if 0
+       wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_mode_set", false);
+       PointerRNA ptr;
+
+       WM_operator_properties_create_ptr(&ptr, ot);
+       RNA_enum_set(&ptr, "mode", mode);
+       RNA_boolean_set(&ptr, "toggle", false);
+       WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &ptr);
+       WM_operator_properties_free(&ptr);
+#else
+       Object *ob = CTX_data_active_object(C);
+       if (ob == NULL) {
+               return;
+       }
+       if (ob->mode == mode) {
+               /* pass */
+       }
+       else if (mode != OB_MODE_OBJECT) {
+               if (ob && (ob->mode & mode) == 0) {
+                       /* needed so we don't do undo pushes. */
+                       wmWindowManager *wm = CTX_wm_manager(C);
+                       wm->op_undo_depth++;
+                       ED_object_mode_toggle(C, mode);
+                       wm->op_undo_depth--;
+               }
+       }
+       else {
+               /* needed so we don't do undo pushes. */
+               wmWindowManager *wm = CTX_wm_manager(C);
+               wm->op_undo_depth++;
+               ED_object_mode_toggle(C, ob->mode);
+               wm->op_undo_depth--;
+
+       }
+#endif
+}
index 93bfd1567079fdd5e7cf27cd1b027ee2502d6d23..fce43fa64251ef603778cf8cc67cbae1cf9ed49c 100644 (file)
@@ -126,8 +126,6 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
 
        if (edit==0) return;
 
-       PTCacheUndo_clear(edit);
-
        if (edit->points) {
                LOOP_POINTS {
                        if (point->keys)
@@ -4380,8 +4378,11 @@ void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, Partic
                        recalc_emitter_field(ob, psys);
                PE_update_object(scene, ob, 1);
 
-               PTCacheUndo_clear(edit);
+               /* Causes issues, adding undo pushes while performing undo history.
+                * Seems not to like this isn't needed anyway - Campbell. */
+#if 0
                PE_undo_push(scene, "Original");
+#endif
        }
 }
 
index 288e59a867199bf81227fb397d32f546aee6380d..329658a56e1800ce470ed0781389d53f1c35148a 100644 (file)
@@ -38,6 +38,7 @@
 
 #include "DNA_scene_types.h"
 #include "DNA_meshdata_types.h"
+#include "DNA_windowmanager_types.h"
 
 #include "BLI_listbase.h"
 #include "BLI_string.h"
 #include "BKE_global.h"
 #include "BKE_particle.h"
 #include "BKE_pointcache.h"
+#include "BKE_context.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
 
+#include "ED_object.h"
 #include "ED_particle.h"
+#include "ED_physics.h"
 
 #include "particle_edit_utildefines.h"
 
 #include "physics_intern.h"
 
-static void free_PTCacheUndo(PTCacheUndo *undo)
-{
-       PTCacheEditPoint *point;
-       int i;
-
-       for (i=0, point=undo->points; i<undo->totpoint; i++, point++) {
-               if (undo->particles && (undo->particles + i)->hair)
-                       MEM_freeN((undo->particles + i)->hair);
-               if (point->keys)
-                       MEM_freeN(point->keys);
-       }
-       if (undo->points)
-               MEM_freeN(undo->points);
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
 
-       if (undo->particles)
-               MEM_freeN(undo->particles);
-
-       BKE_ptcache_free_mem(&undo->mem_cache);
-}
-
-static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+static void undoptcache_from_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
 {
        PTCacheEditPoint *point;
        int i;
 
+       size_t mem_used_prev = MEM_get_memory_in_use();
+
        undo->totpoint= edit->totpoint;
 
        if (edit->psys) {
@@ -86,8 +78,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
 
                pa= undo->particles= MEM_dupallocN(edit->psys->particles);
 
-               for (i=0; i<edit->totpoint; i++, pa++)
+               for (i=0; i<edit->totpoint; i++, pa++) {
                        pa->hair= MEM_dupallocN(pa->hair);
+               }
 
                undo->psys_flag = edit->psys->flag;
        }
@@ -98,8 +91,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                pm = undo->mem_cache.first;
 
                for (; pm; pm=pm->next) {
-                       for (i=0; i<BPHYS_TOT_DATA; i++)
+                       for (i=0; i<BPHYS_TOT_DATA; i++) {
                                pm->data[i] = MEM_dupallocN(pm->data[i]);
+                       }
                }
        }
 
@@ -110,9 +104,13 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                point->keys= MEM_dupallocN(point->keys);
                /* no need to update edit key->co & key->time pointers here */
        }
+
+       size_t mem_used_curr = MEM_get_memory_in_use();
+
+       undo->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(PTCacheUndo);
 }
 
-static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
+static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
 {
        ParticleSystem *psys = edit->psys;
        ParticleData *pa;
@@ -120,16 +118,20 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
        POINT_P; KEY_K;
 
        LOOP_POINTS {
-               if (psys && psys->particles[p].hair)
+               if (psys && psys->particles[p].hair) {
                        MEM_freeN(psys->particles[p].hair);
+               }
 
-               if (point->keys)
+               if (point->keys) {
                        MEM_freeN(point->keys);
+               }
        }
-       if (psys && psys->particles)
+       if (psys && psys->particles) {
                MEM_freeN(psys->particles);
-       if (edit->points)
+       }
+       if (edit->points) {
                MEM_freeN(edit->points);
+       }
        if (edit->mirror_cache) {
                MEM_freeN(edit->mirror_cache);
                edit->mirror_cache= NULL;
@@ -171,9 +173,9 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                pm = edit->pid.cache->mem_cache.first;
 
                for (; pm; pm=pm->next) {
-                       for (i=0; i<BPHYS_TOT_DATA; i++)
+                       for (i = 0; i < BPHYS_TOT_DATA; i++) {
                                pm->data[i] = MEM_dupallocN(pm->data[i]);
-
+                       }
                        BKE_ptcache_mem_pointers_init(pm);
 
                        LOOP_POINTS {
@@ -191,150 +193,122 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
        }
 }
 
-void PE_undo_push(Scene *scene, const char *str)
+static void undoptcache_free_data(PTCacheUndo *undo)
 {
-       PTCacheEdit *edit= PE_get_current(scene, OBACT);
-       PTCacheUndo *undo;
-       int nr;
-
-       if (!edit) return;
-
-       /* remove all undos after (also when curundo==NULL) */
-       while (edit->undo.last != edit->curundo) {
-               undo= edit->undo.last;
-               BLI_remlink(&edit->undo, undo);
-               free_PTCacheUndo(undo);
-               MEM_freeN(undo);
-       }
+       PTCacheEditPoint *point;
+       int i;
 
-       /* make new */
-       edit->curundo= undo= MEM_callocN(sizeof(PTCacheUndo), "particle undo file");
-       BLI_strncpy(undo->name, str, sizeof(undo->name));
-       BLI_addtail(&edit->undo, undo);
-       
-       /* and limit amount to the maximum */
-       nr= 0;
-       undo= edit->undo.last;
-       while (undo) {
-               nr++;
-               if (nr==U.undosteps) break;
-               undo= undo->prev;
-       }
-       if (undo) {
-               while (edit->undo.first != undo) {
-                       PTCacheUndo *first= edit->undo.first;
-                       BLI_remlink(&edit->undo, first);
-                       free_PTCacheUndo(first);
-                       MEM_freeN(first);
+       for (i = 0, point=undo->points; i < undo->totpoint; i++, point++) {
+               if (undo->particles && (undo->particles + i)->hair) {
+                       MEM_freeN((undo->particles + i)->hair);
+               }
+               if (point->keys) {
+                       MEM_freeN(point->keys);
                }
        }
-
-       /* copy  */
-       make_PTCacheUndo(edit, edit->curundo);
+       if (undo->points) {
+               MEM_freeN(undo->points);
+       }
+       if (undo->particles) {
+               MEM_freeN(undo->particles);
+       }
+       BKE_ptcache_free_mem(&undo->mem_cache);
 }
 
-void PE_undo_step(Scene *scene, int step)
-{      
-       PTCacheEdit *edit= PE_get_current(scene, OBACT);
+/** \} */
 
-       if (!edit) return;
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-       if (step==0) {
-               get_PTCacheUndo(edit, edit->curundo);
-       }
-       else if (step==1) {
-               
-               if (edit->curundo==NULL || edit->curundo->prev==NULL) {
-                       /* pass */
-               }
-               else {
-                       if (G.debug & G_DEBUG) printf("undo %s\n", edit->curundo->name);
-                       edit->curundo= edit->curundo->prev;
-                       get_PTCacheUndo(edit, edit->curundo);
-               }
-       }
-       else {
-               /* curundo has to remain current situation! */
-               
-               if (edit->curundo==NULL || edit->curundo->next==NULL) {
-                       /* pass */
-               }
-               else {
-                       get_PTCacheUndo(edit, edit->curundo->next);
-                       edit->curundo= edit->curundo->next;
-                       if (G.debug & G_DEBUG) printf("redo %s\n", edit->curundo->name);
-               }
-       }
+typedef struct ParticleUndoStep {
+       UndoStep step;
+       UndoRefID_Scene scene_ref;
+       UndoRefID_Object object_ref;
+       PTCacheUndo data;
+} ParticleUndoStep;
 
-       DAG_id_tag_update(&OBACT->id, OB_RECALC_DATA);
+static bool particle_undosys_poll(struct bContext *C)
+{
+       Scene *scene = CTX_data_scene(C);
+       Object *ob = OBACT;
+       PTCacheEdit *edit = PE_get_current(scene, ob);
+       return (edit != NULL);
 }
 
-bool PE_undo_is_valid(Scene *scene)
+static bool particle_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-       PTCacheEdit *edit= PE_get_current(scene, OBACT);
-       
-       if (edit) {
-               return (edit->undo.last != edit->undo.first);
-       }
-       return 0;
+       ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+       us->scene_ref.ptr = CTX_data_scene(C);
+       us->object_ref.ptr = us->scene_ref.ptr->basact->object;
+       PTCacheEdit *edit = PE_get_current(us->scene_ref.ptr, us->object_ref.ptr);
+       undoptcache_from_editcache(&us->data, edit);
+       return true;
 }
 
-void PTCacheUndo_clear(PTCacheEdit *edit)
+static void particle_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
 {
-       PTCacheUndo *undo;
-
-       if (edit==NULL) return;
-       
-       undo= edit->undo.first;
-       while (undo) {
-               free_PTCacheUndo(undo);
-               undo= undo->next;
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_PARTICLE_EDIT);
+       BLI_assert(particle_undosys_poll(C));
+
+       ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+       Scene *scene = us->scene_ref.ptr;
+       Object *ob = us->object_ref.ptr;
+       PTCacheEdit *edit = PE_get_current(scene, ob);
+       if (edit) {
+               undoptcache_to_editcache(&us->data, edit);
+               DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       }
+       else {
+               BLI_assert(0);
        }
-       BLI_freelistN(&edit->undo);
-       edit->curundo= NULL;
 }
 
-void PE_undo(Scene *scene)
+static void particle_undosys_step_free(UndoStep *us_p)
 {
-       PE_undo_step(scene, 1);
+       ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+       undoptcache_free_data(&us->data);
 }
 
-void PE_redo(Scene *scene)
+static void particle_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
 {
-       PE_undo_step(scene, -1);
+       ParticleUndoStep *us = (ParticleUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref));
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->object_ref));
 }
 
-void PE_undo_number(Scene *scene, int nr)
+/* Export for ED_undo_sys. */
+void ED_particle_undosys_type(UndoType *ut)
 {
-       PTCacheEdit *edit= PE_get_current(scene, OBACT);
-       PTCacheUndo *undo;
-       int a=0;
-       
-       for (undo= edit->undo.first; undo; undo= undo->next, a++) {
-               if (a==nr) break;
-       }
-       edit->curundo= undo;
-       PE_undo_step(scene, 0);
+       ut->name = "Edit Particle";
+       ut->poll = particle_undosys_poll;
+       ut->step_encode = particle_undosys_step_encode;
+       ut->step_decode = particle_undosys_step_decode;
+       ut->step_free = particle_undosys_step_free;
+
+       ut->step_foreach_ID_ref = particle_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(ParticleUndoStep);
 }
 
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
 
-/* get name of undo item, return null if no item with this index */
-/* if active pointer, set it to 1 if true */
-const char *PE_undo_get_name(Scene *scene, int nr, bool *r_active)
+void PE_undo_push(struct Scene *scene, const char *str)
 {
-       PTCacheEdit *edit= PE_get_current(scene, OBACT);
-       PTCacheUndo *undo;
-       
-       if (r_active) *r_active = false;
-       
-       if (edit) {
-               undo= BLI_findlink(&edit->undo, nr);
-               if (undo) {
-                       if (r_active && (undo == edit->curundo)) {
-                               *r_active = true;
-                       }
-                       return undo->name;
-               }
-       }
-       return NULL;
+       wmWindowManager *wm = G.main->wm.first;
+       bContext *C_temp = CTX_create();
+       CTX_data_scene_set(C_temp, scene);
+       BKE_undosys_step_push_with_type(wm->undo_stack, C_temp, str, BKE_UNDOSYS_TYPE_PARTICLE);
+       CTX_free(C_temp);
 }
+
+/** \} */
index d85720f956c139ae14168add6b4616739fdc91aa..63b84df9fce1c3fe0ba7f78babcfc680710dc3cf 100644 (file)
@@ -911,10 +911,7 @@ static void copy_particle_edit(Scene *scene, Object *ob, ParticleSystem *psys, P
        
        edit->emitter_field = NULL;
        edit->emitter_cosnos = NULL;
-       
-       BLI_listbase_clear(&edit->undo);
-       edit->curundo = NULL;
-       
+
        edit->points = MEM_dupallocN(edit_from->points);
        pa = psys->particles;
        LOOP_POINTS {
@@ -943,8 +940,7 @@ static void copy_particle_edit(Scene *scene, Object *ob, ParticleSystem *psys, P
        recalc_lengths(edit);
        recalc_emitter_field(ob, psys);
        PE_update_object(scene, ob, true);
-       
-       PTCacheUndo_clear(edit);
+
        PE_undo_push(scene, "Original");
 }
 
index cb28193663466d637469a7aa9780169ee6a501e4..f3f3697caaa661ff2120ec3ab5b78ada77d643cb 100644 (file)
@@ -68,7 +68,6 @@ void PARTICLE_OT_edited_clear(struct wmOperatorType *ot);
 
 void PARTICLE_OT_unify_length(struct wmOperatorType *ot);
 
-void PTCacheUndo_clear(struct PTCacheEdit *edit);
 void PE_create_particle_edit(struct Scene *scene, struct Object *ob, struct PointCache *cache, struct ParticleSystem *psys);
 void recalc_lengths(struct PTCacheEdit *edit);
 void recalc_emitter_field(struct Object *ob, struct ParticleSystem *psys);
index c27570aabc5c541d468b437071bb92baa37929a6..92fb140243f7d50366037e5f605c49b747086371 100644 (file)
@@ -65,6 +65,7 @@
 #include "BKE_sequencer.h"
 #include "BKE_screen.h"
 #include "BKE_scene.h"
+#include "BKE_undo_system.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -88,6 +89,7 @@
 #include "RNA_access.h"
 #include "RNA_define.h"
 
+#include "BLO_undofile.h"
 
 #include "render_intern.h"
 
@@ -866,7 +868,8 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
        /* get main */
        if (G.debug_value == 101) {
                /* thread-safety experiment, copy main from the undo buffer */
-               mainp = BKE_undo_get_main(&scene);
+               struct MemFile *memfile = ED_undosys_stack_memfile_get_active(CTX_wm_manager(C)->undo_stack);
+               mainp = BLO_memfile_main_get(memfile, CTX_data_main(C), &scene);
        }
        else
                mainp = CTX_data_main(C);
index adead9a8b9e2aac03d026b805a4d8251f0fd2ff7..9527dc4fe830aadf78dbcacb7150a98d93cc8728 100644 (file)
@@ -51,7 +51,6 @@ set(SRC
        paint_mask.c
        paint_ops.c
        paint_stroke.c
-       paint_undo.c
        paint_utils.c
        paint_vertex.c
        paint_vertex_color_ops.c
index 8d9812f41d94f869894459475a6b9ec05b5fae97..120514762f421b03c76611f96b2111f431e226a3 100644 (file)
@@ -40,6 +40,7 @@
 #include "BKE_paint.h"
 
 #include "ED_view3d.h"
+#include "ED_paint.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -203,7 +204,7 @@ static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
                br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
        }
 
-       ED_paintcurve_undo_push(C, op, pc);
+       ED_paintcurve_undo_push_begin(op->type->name);
 
        pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
        add_index = pc->add_index;
@@ -241,6 +242,8 @@ static void paintcurve_point_add(bContext *C,  wmOperator *op, const int loc[2])
                pcp[add_index].bez.h1 = HD_ALIGN;
        }
 
+       ED_paintcurve_undo_push_end();
+
        WM_paint_cursor_tag_redraw(window, ar);
 }
 
@@ -302,7 +305,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
                return OPERATOR_CANCELLED;
        }
 
-       ED_paintcurve_undo_push(C, op, pc);
+       ED_paintcurve_undo_push_begin(op->type->name);
 
 #define DELETE_TAG 2
 
@@ -342,6 +345,8 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
 
 #undef DELETE_TAG
 
+       ED_paintcurve_undo_push_end();
+
        WM_paint_cursor_tag_redraw(window, ar);
 
        return OPERATOR_FINISHED;
@@ -379,7 +384,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
        if (!pc)
                return false;
 
-       ED_paintcurve_undo_push(C, op, pc);
+       ED_paintcurve_undo_push_begin(op->type->name);
 
        if (toggle) {
                PaintCurvePoint *pcp;
@@ -444,10 +449,14 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
                        }
                }
 
-               if (!pcp)
+               if (!pcp) {
+                       ED_paintcurve_undo_push_end();
                        return false;
+               }
        }
 
+       ED_paintcurve_undo_push_end();
+
        WM_paint_cursor_tag_redraw(window, ar);
 
        return true;
@@ -562,9 +571,6 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e
                psd->align = align;
                op->customdata = psd;
 
-               if (do_select)
-                       ED_paintcurve_undo_push(C, op, pc);
-
                /* first, clear all selection from points */
                for (i = 0; i < pc->tot_points; i++)
                        pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
@@ -587,6 +593,8 @@ static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *ev
 
        if (event->type == psd->event && event->val == KM_RELEASE) {
                MEM_freeN(psd);
+               ED_paintcurve_undo_push_begin(op->type->name);
+               ED_paintcurve_undo_push_end();
                return OPERATOR_FINISHED;
        }
 
index 70f92999864238248618606489ade1f07d337442..d5b7496fa3e4d11c5b56dbaa7ee131149993a3b4 100644 (file)
 #include "DNA_space_types.h"
 
 #include "BLI_string.h"
+#include "BLI_array_utils.h"
 
 #include "BKE_context.h"
 #include "BKE_paint.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
 
 #include "ED_paint.h"
 
 
 #include "paint_intern.h"
 
-typedef struct UndoCurve {
-       struct UndoImageTile *next, *prev;
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
 
+typedef struct UndoCurve {
        PaintCurvePoint *points; /* points of curve */
        int tot_points;
-       int active_point;
-
-       char idname[MAX_ID_NAME];  /* name instead of pointer*/
+       int add_index;
 } UndoCurve;
 
-static void paintcurve_undo_restore(bContext *C, ListBase *lb)
+static void undocurve_from_paintcurve(UndoCurve *uc, const PaintCurve *pc)
 {
-       Paint *p = BKE_paint_get_active_from_context(C);
-       UndoCurve *uc;
-       PaintCurve *pc = NULL;
+       BLI_assert(BLI_array_is_zeroed(uc, 1));
+       uc->points = MEM_dupallocN(pc->points);
+       uc->tot_points = pc->tot_points;
+       uc->add_index = pc->add_index;
+}
 
-       if (p->brush) {
-               pc = p->brush->paint_curve;
-       }
+static void undocurve_to_paintcurve(const UndoCurve *uc, PaintCurve *pc)
+{
+       MEM_SAFE_FREE(pc->points);
+       pc->points = MEM_dupallocN(uc->points);
+       pc->tot_points = uc->tot_points;
+       pc->add_index = uc->add_index;
+}
 
-       if (!pc) {
-               return;
-       }
+static void undocurve_free_data(UndoCurve *uc)
+{
+       MEM_SAFE_FREE(uc->points);
+}
 
-       uc = (UndoCurve *)lb->first;
+/** \} */
 
-       if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) {
-               SWAP(PaintCurvePoint *, pc->points, uc->points);
-               SWAP(int, pc->tot_points, uc->tot_points);
-               SWAP(int, pc->add_index, uc->active_point);
-       }
-}
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
 
-static void paintcurve_undo_delete(ListBase *lb)
+typedef struct PaintCurveUndoStep {
+       UndoStep step;
+       PaintCurve *pc;
+       UndoCurve data;
+} PaintCurveUndoStep;
+
+static bool paintcurve_undosys_poll(bContext *C)
 {
-       UndoCurve *uc;
-       uc = (UndoCurve *)lb->first;
+       Paint *p = BKE_paint_get_active_from_context(C);
+       return (p->brush && p->brush->paint_curve);
+}
 
-       if (uc->points)
-               MEM_freeN(uc->points);
-       uc->points = NULL;
+static void paintcurve_undosys_step_encode_init(struct bContext *C, UndoStep *us_p)
+{
+       /* XXX, use to set the undo type only. */
+       UNUSED_VARS(C, us_p);
 }
 
-/**
- * \note This is called before executing steps (not after).
- */
-void ED_paintcurve_undo_push(bContext *C, wmOperator *op, PaintCurve *pc)
+static bool paintcurve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
 {
-       ePaintMode mode = BKE_paintmode_get_active_from_context(C);
-       ListBase *lb = NULL;
-       int undo_stack_id;
-       UndoCurve *uc;
-
-       switch (mode) {
-               case ePaintTexture2D:
-               case ePaintTextureProjective:
-                       undo_stack_id = UNDO_PAINT_IMAGE;
-                       break;
-
-               case ePaintSculpt:
-                       undo_stack_id = UNDO_PAINT_MESH;
-                       break;
-
-               default:
-                       /* do nothing, undo is handled by global */
-                       return;
+       Paint *p = BKE_paint_get_active_from_context(C);
+       PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL;
+       if (pc == NULL) {
+               return false;
        }
 
+       PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+       BLI_assert(us->step.data_size == 0);
 
-       ED_undo_paint_push_begin(undo_stack_id, op->type->name,
-                                paintcurve_undo_restore, paintcurve_undo_delete, NULL);
-       lb = undo_paint_push_get_list(undo_stack_id);
+       us->pc = pc;
+       undocurve_from_paintcurve(&us->data, pc);
 
-       uc = MEM_callocN(sizeof(*uc), "Undo_curve");
+       return true;
+}
 
-       lb->first = uc;
+static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), UndoStep *us_p, int UNUSED(dir))
+{
+       PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+       undocurve_to_paintcurve(&us->data, us->pc);
+}
 
-       BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname));
-       uc->tot_points = pc->tot_points;
-       uc->active_point = pc->add_index;
-       uc->points = MEM_dupallocN(pc->points);
+static void paintcurve_undosys_step_free(UndoStep *us_p)
+{
+       PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
+       undocurve_free_data(&us->data);
+}
+
+/* Export for ED_undo_sys. */
+void ED_paintcurve_undosys_type(UndoType *ut)
+{
+       ut->name = "Paint Curve";
+       /* don't poll for now */
+       ut->poll = paintcurve_undosys_poll;
+       ut->step_encode_init = paintcurve_undosys_step_encode_init;
+       ut->step_encode = paintcurve_undosys_step_encode;
+       ut->step_decode = paintcurve_undosys_step_decode;
+       ut->step_free = paintcurve_undosys_step_free;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = false;
+
+       ut->step_size = sizeof(PaintCurveUndoStep);
+}
+
+/** \} */
 
-       undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points);
 
-       ED_undo_paint_push_end(undo_stack_id);
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+void ED_paintcurve_undo_push_begin(const char *name)
+{
+       bContext *C = NULL; /* special case, we never read from this. */
+       wmWindowManager *wm = G.main->wm.first;
+       BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE);
 }
+
+void ED_paintcurve_undo_push_end(void)
+{
+       wmWindowManager *wm = G.main->wm.first;  /* XXX, avoids adding extra arg. */
+       BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
+}
+
+/** \} */
index c6472b258cab6f584366d4e17a109f4d32956318..5d133ee622fe72b577f6c5667acf41c3caab682b 100644 (file)
@@ -58,6 +58,9 @@
 #include "BKE_material.h"
 #include "BKE_node.h"
 #include "BKE_paint.h"
+#include "BKE_undo_system.h"
+
+#include "BKE_global.h"
 
 #include "UI_interface.h"
 #include "UI_view2d.h"
@@ -145,9 +148,11 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
 
        imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
 
+       ListBase *undo_tiles = ED_image_undo_get_tiles();
+
        for (ty = tiley; ty <= tileh; ty++)
                for (tx = tilex; tx <= tilew; tx++)
-                       image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
+                       image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
 
        ibuf->userflags |= IB_BITMAPDIRTY;
 
@@ -491,7 +496,8 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
                BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
 
        if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
-               ED_image_undo_restore();
+               UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
+               ED_image_undo_restore(ustack->step_init);
        }
 
        if (pop->mode == PAINT_MODE_3D_PROJECT) {
@@ -1174,14 +1180,17 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
 
 void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
 {
+       wmWindowManager *wm = CTX_wm_manager(C);
        SpaceImage *sima = CTX_wm_space_image(C);
        Image *ima = sima->image;
 
+       BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE);
+
        ED_image_undo_push_begin(op->type->name);
 
        paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
 
-       ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
+       BKE_undosys_step_push(wm->undo_stack, C, op->type->name);
 
        DAG_id_tag_update(&ima->id, 0);
 }
index 894277402a70b3a6f314b38f4a7d1e78d79dc6c0..c1dd31ea055b9973f70bd5271e52f5e29e6464af 100644 (file)
@@ -1035,6 +1035,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
        ImBuf tmpbuf;
        IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
 
+       ListBase *undo_tiles = ED_image_undo_get_tiles();
+
        for (int ty = tiley; ty <= tileh; ty++) {
                for (int tx = tilex; tx <= tilew; tx++) {
                        /* retrieve original pixels + mask from undo buffer */
@@ -1043,9 +1045,9 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
                        int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
 
                        if (s->canvas->rect_float)
-                               tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+                               tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
                        else
-                               tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
+                               tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
 
                        IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask,
                                      curveb, texmaskb, mask_max,
index 60fe8555ba2913601d686595d47e09886ddf3675..e5bddae971d3715c3b0384e3331f557edcedf6b3 100644 (file)
@@ -1499,15 +1499,16 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
 
 
        if (generate_tile) {
+               ListBase *undo_tiles = ED_image_undo_get_tiles();
                volatile void *undorect;
                if (tinf->masked) {
                        undorect = image_undo_push_tile(
-                               pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
+                               undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
                                tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false);
                }
                else {
                        undorect = image_undo_push_tile(
-                               pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
+                               undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
                                tx, ty, NULL, &pjIma->valid[tile_index], true, false);
                }
 
index b5b64eb3e163ae0e0d46cd87c8ace49e0430c42a..9d1987943a5bd7ebe6eabce00bdedaa3ed5ca4bc 100644 (file)
 #include "BLI_threads.h"
 
 #include "DNA_image_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_object_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
 
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
@@ -37,6 +41,8 @@
 #include "BKE_depsgraph.h"
 #include "BKE_image.h"
 #include "BKE_main.h"
+#include "BKE_global.h"
+#include "BKE_undo_system.h"
 
 #include "ED_paint.h"
 
 
 #include "paint_intern.h"
 
+/* -------------------------------------------------------------------- */
+/** \name Undo Conversion
+ * \{ */
+
 typedef struct UndoImageTile {
        struct UndoImageTile *next, *prev;
 
-       char idname[MAX_ID_NAME];  /* name instead of pointer*/
+       char idname[MAX_ID_NAME];  /* name instead of pointer */
        char ibufname[IMB_FILENAME_SIZE];
 
        union {
@@ -64,6 +74,8 @@ typedef struct UndoImageTile {
        short source, use_float;
        char gen_type;
        bool valid;
+
+       size_t undo_size;
 } UndoImageTile;
 
 /* this is a static resource for non-globality,
@@ -129,13 +141,14 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, Cop
        }
 }
 
-void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
+void *image_undo_find_tile(
+        ListBase *undo_tiles,
+        Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
        UndoImageTile *tile;
        short use_float = ibuf->rect_float ? 1 : 0;
 
-       for (tile = lb->first; tile; tile = tile->next) {
+       for (tile = undo_tiles->first; tile; tile = tile->next) {
                if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
                        if (tile->use_float == use_float) {
                                if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) {
@@ -161,10 +174,10 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi
 }
 
 void *image_undo_push_tile(
+        ListBase *undo_tiles,
         Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile,
         unsigned short **mask, bool **valid, bool proj, bool find_prev)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
        UndoImageTile *tile;
        int allocsize;
        short use_float = ibuf->rect_float ? 1 : 0;
@@ -174,7 +187,7 @@ void *image_undo_push_tile(
 
        /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
        if (find_prev) {
-               data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true);
+               data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true);
                if (data) {
                        return data;
                }
@@ -214,8 +227,7 @@ void *image_undo_push_tile(
        if (proj) {
                BLI_spin_lock(&undolock);
        }
-       undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize);
-       BLI_addtail(lb, tile);
+       BLI_addtail(undo_tiles, tile);
 
        if (proj) {
                BLI_spin_unlock(&undolock);
@@ -225,10 +237,10 @@ void *image_undo_push_tile(
 
 void image_undo_remove_masks(void)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       ListBase *undo_tiles = ED_image_undo_get_tiles();
        UndoImageTile *tile;
 
-       for (tile = lb->first; tile; tile = tile->next) {
+       for (tile = undo_tiles->first; tile; tile = tile->next) {
                if (tile->mask) {
                        MEM_freeN(tile->mask);
                        tile->mask = NULL;
@@ -346,50 +358,145 @@ static void image_undo_free_list(ListBase *lb)
 
 void ED_image_undo_push_begin(const char *name)
 {
-       ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, name, image_undo_restore_list, image_undo_free_list, NULL);
+       bContext *C = NULL; /* special case, we never read from this. */
+       wmWindowManager *wm = G.main->wm.first;
+       BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
 }
 
 void ED_image_undo_push_end(void)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       wmWindowManager *wm = G.main->wm.first;  /* XXX, avoids adding extra arg. */
+       BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
+}
+
+static void image_undo_invalidate(void)
+{
        UndoImageTile *tile;
-       int deallocsize = 0;
+       ListBase *lb = ED_image_undo_get_tiles();
+
+       for (tile = lb->first; tile; tile = tile->next) {
+               tile->valid = false;
+       }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct ImageUndoStep {
+       UndoStep step;
+       ListBase tiles;
+} ImageUndoStep;
+
+static bool image_undosys_poll(bContext *C)
+{
+       Object *obact = CTX_data_active_object(C);
+
+       ScrArea *sa = CTX_wm_area(C);
+       if (sa && (sa->spacetype == SPACE_IMAGE)) {
+               SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
+               if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
+                       return true;
+               }
+       }
+       else if (sa && (sa->spacetype == SPACE_VIEW3D)) {
+               if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+       ImageUndoStep *us = (ImageUndoStep *)us_p;
+       /* dummy, memory is cleared anyway. */
+       BLI_listbase_clear(&us->tiles);
+}
+
+static bool image_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+       /* dummy, encoding is done along the way by adding tiles
+        * to the current 'ImageUndoStep' added by encode_init. */
+       ImageUndoStep *us = (ImageUndoStep *)us_p;
+
+       BLI_assert(us->step.data_size == 0);
+
        int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
 
        /* first dispose of invalid tiles (may happen due to drag dot for instance) */
-       for (tile = lb->first; tile;) {
+       for (UndoImageTile *tile = us->tiles.first; tile;) {
                if (!tile->valid) {
                        UndoImageTile *tmp_tile = tile->next;
-                       deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
                        MEM_freeN(tile->rect.pt);
-                       BLI_freelinkN(lb, tile);
+                       BLI_freelinkN(&us->tiles, tile);
                        tile = tmp_tile;
                }
                else {
+                       us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
                        tile = tile->next;
                }
        }
 
-       /* don't forget to remove the size of deallocated tiles */
-       undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize);
+       return true;
+}
 
-       ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
+static void image_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       ImageUndoStep *us = (ImageUndoStep *)us_p;
+       image_undo_restore_list(C, &us->tiles);
 }
 
-static void image_undo_invalidate(void)
+static void image_undosys_step_free(UndoStep *us_p)
 {
-       UndoImageTile *tile;
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       ImageUndoStep *us = (ImageUndoStep *)us_p;
+       image_undo_free_list(&us->tiles);
+}
 
-       for (tile = lb->first; tile; tile = tile->next) {
-               tile->valid = false;
-       }
+/* Export for ED_undo_sys. */
+void ED_image_undosys_type(UndoType *ut)
+{
+       ut->name = "Image";
+       ut->poll = image_undosys_poll;
+       ut->step_encode_init = image_undosys_step_encode_init;
+       ut->step_encode = image_undosys_step_encode;
+       ut->step_decode = image_undosys_step_decode;
+       ut->step_free = image_undosys_step_free;
+
+       ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(ImageUndoStep);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p)
+{
+       ImageUndoStep *us = (ImageUndoStep *)us_p;
+       return &us->tiles;
+}
+
+ListBase *ED_image_undo_get_tiles(void)
+{
+       wmWindowManager *wm = G.main->wm.first;  /* XXX, avoids adding extra arg. */
+       UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_IMAGE);
+       return ED_image_undosys_step_get_tiles(us);
 }
 
 /* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
-void ED_image_undo_restore(void)
+void ED_image_undo_restore(UndoStep *us)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       ListBase *lb = ED_image_undosys_step_get_tiles(us);
        image_undo_restore_runtime(lb);
        image_undo_invalidate();
 }
+
+/** \} */
index 52be4be4c0db957c5caecf198da606228fa286d9..63a12b3c145cc770b60dc6989357e7eae9d8de33 100644 (file)
@@ -55,6 +55,7 @@ struct wmOperator;
 struct wmOperatorType;
 struct wmWindowManager;
 struct DMCoNo;
+struct UndoStep;
 enum ePaintMode;
 
 /* paint_stroke.c */
@@ -221,15 +222,20 @@ void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot);
 
 /* paint_image_undo.c */
 void *image_undo_find_tile(
+        ListBase *undo_tiles,
         struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile,
         unsigned short **mask, bool validate);
 void *image_undo_push_tile(
+        ListBase *undo_tiles,
         struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile,
         unsigned short **, bool **valid, bool proj, bool find_prev);
 void image_undo_remove_masks(void);
 void image_undo_init_locks(void);
 void image_undo_end_locks(void);
 
+struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p);
+struct ListBase *ED_image_undo_get_tiles(void);
+
 /* sculpt_uv.c */
 int uv_sculpt_poll(struct bContext *C);
 int uv_sculpt_keymap_poll(struct bContext *C);
@@ -304,10 +310,6 @@ typedef enum {
 void set_brush_rc_props(struct PointerRNA *ptr, const char *paint, const char *prop, const char *secondary_prop,
                         RCFlags flags);
 
-/* paint_undo.c */
-struct ListBase *undo_paint_push_get_list(int type);
-void undo_paint_push_count_alloc(int type, int size);
-
 /* paint_hide.c */
 
 typedef enum {
diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c
deleted file mode 100644 (file)
index 27d3f66..0000000
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * 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.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/editors/sculpt_paint/paint_undo.c
- *  \ingroup edsculpt
- *  \brief Undo system for painting and sculpting.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_listbase.h"
-#include "BLI_utildefines.h"
-#include "BLI_string.h"
-
-#include "DNA_userdef_types.h"
-
-#include "BKE_blender_undo.h"
-#include "BKE_context.h"
-#include "BKE_global.h"
-
-#include "ED_paint.h"
-
-#include "paint_intern.h"
-
-typedef struct UndoElem {
-       struct UndoElem *next, *prev;
-       char name[BKE_UNDO_STR_MAX];
-       uintptr_t undosize;
-
-       ListBase elems;
-
-       UndoRestoreCb restore;
-       UndoFreeCb free;
-       UndoCleanupCb cleanup;
-} UndoElem;
-
-typedef struct UndoStack {
-       int type;
-       ListBase elems;
-       UndoElem *current;
-} UndoStack;
-
-static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL};
-static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL};
-
-/* Generic */
-
-static void undo_restore(bContext *C, UndoStack *UNUSED(stack), UndoElem *uel)
-{
-       if (uel && uel->restore)
-               uel->restore(C, &uel->elems);
-}
-
-static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel)
-{
-       if (uel && uel->free) {
-               uel->free(&uel->elems);
-               BLI_freelistN(&uel->elems);
-       }
-}
-
-static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
-{
-       UndoElem *uel;
-       int nr;
-       
-       /* Undo push is split up in begin and end, the reason is that as painting
-        * happens more tiles/nodes are added to the list, and at the very end we
-        * know how much memory the undo used to remove old undo elements */
-
-       /* remove all undos after (also when stack->current==NULL) */
-       while (stack->elems.last != stack->current) {
-               uel = stack->elems.last;
-               undo_elem_free(stack, uel);
-               BLI_freelinkN(&stack->elems, uel);
-       }
-       
-       /* make new */
-       stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file");
-       uel->restore = restore;
-       uel->free = free;
-       uel->cleanup = cleanup;
-       BLI_addtail(&stack->elems, uel);
-
-       /* name can be a dynamic string */
-       BLI_strncpy(uel->name, name, sizeof(uel->name));
-       
-       /* limit amount to the maximum amount*/
-       nr = 0;
-       uel = stack->elems.last;
-       while (uel) {
-               nr++;
-               if (nr == U.undosteps) break;
-               uel = uel->prev;
-       }
-       if (uel) {
-               while (stack->elems.first != uel) {
-                       UndoElem *first = stack->elems.first;
-                       undo_elem_free(stack, first);
-                       BLI_freelinkN(&stack->elems, first);
-               }
-       }
-}
-
-static void undo_stack_push_end(UndoStack *stack)
-{
-       UndoElem *uel;
-       uintptr_t totmem, maxmem;
-       int totundo = 0;
-
-       /* first limit to undo steps */
-       uel = stack->elems.last;
-
-       while (uel) {
-               totundo++;
-               if (totundo > U.undosteps) break;
-               uel = uel->prev;
-       }
-
-       if (uel) {
-               UndoElem *first;
-
-               /* in case the undo steps are zero, the current pointer will be invalid */
-               if (uel == stack->current)
-                       stack->current = NULL;
-
-               do {
-                       first = stack->elems.first;
-                       undo_elem_free(stack, first);
-                       BLI_freelinkN(&stack->elems, first);
-               } while (first != uel);
-       }
-
-       if (U.undomemory != 0) {
-               /* limit to maximum memory (afterwards, we can't know in advance) */
-               totmem = 0;
-               maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
-
-               uel = stack->elems.last;
-               while (uel) {
-                       totmem += uel->undosize;
-                       if (totmem > maxmem) break;
-                       uel = uel->prev;
-               }
-
-               if (uel) {
-                       while (stack->elems.first != uel) {
-                               UndoElem *first = stack->elems.first;
-                               undo_elem_free(stack, first);
-                               BLI_freelinkN(&stack->elems, first);
-                       }
-               }
-       }
-}
-
-static void undo_stack_cleanup(UndoStack *stack, bContext *C)
-{
-       UndoElem *uel = stack->elems.first;
-       bool stack_reset = false;
-
-       while (uel) {
-               if (uel->cleanup && uel->cleanup(C, &uel->elems)) {
-                       UndoElem *uel_tmp = uel->next;
-                       if (stack->current == uel) {
-                               stack->current = NULL;
-                               stack_reset = true;
-                       }
-                       undo_elem_free(stack, uel);
-                       BLI_freelinkN(&stack->elems, uel);
-                       uel = uel_tmp;
-               }
-               else
-                       uel = uel->next;
-       }
-       if (stack_reset) {
-               stack->current = stack->elems.last;
-       }
-
-}
-
-static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name)
-{
-       UndoElem *undo;
-
-       /* first cleanup any old undo steps that may belong to invalid data */
-       undo_stack_cleanup(stack, C);
-
-       if (step == 1) {
-               if (stack->current == NULL) {
-                       /* pass */
-               }
-               else {
-                       if (!name || STREQ(stack->current->name, name)) {
-                               if (G.debug & G_DEBUG_WM) {
-                                       printf("%s: undo '%s'\n", __func__, stack->current->name);
-                               }
-                               undo_restore(C, stack, stack->current);
-                               stack->current = stack->current->prev;
-                               return 1;
-                       }
-               }
-       }
-       else if (step == -1) {
-               if ((stack->current != NULL && stack->current->next == NULL) || BLI_listbase_is_empty(&stack->elems)) {
-                       /* pass */
-               }
-               else {
-                       if (!name || STREQ(stack->current->name, name)) {
-                               undo = (stack->current && stack->current->next) ? stack->current->next : stack->elems.first;
-                               undo_restore(C, stack, undo);
-                               stack->current = undo;
-                               if (G.debug & G_DEBUG_WM) {
-                                       printf("%s: redo %s\n", __func__, undo->name);
-                               }
-                               return 1;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static void undo_stack_free(UndoStack *stack)
-{
-       UndoElem *uel;
-       
-       for (uel = stack->elems.first; uel; uel = uel->next)
-               undo_elem_free(stack, uel);
-
-       BLI_freelistN(&stack->elems);
-       stack->current = NULL;
-}
-
-/* Exported Functions */
-
-void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
-{
-       if (type == UNDO_PAINT_IMAGE)
-               undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup);
-       else if (type == UNDO_PAINT_MESH)
-               undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup);
-}
-
-ListBase *undo_paint_push_get_list(int type)
-{
-       if (type == UNDO_PAINT_IMAGE) {
-               if (ImageUndoStack.current) {
-                       return &ImageUndoStack.current->elems;
-               }
-       }
-       else if (type == UNDO_PAINT_MESH) {
-               if (MeshUndoStack.current) {
-                       return &MeshUndoStack.current->elems;
-               }
-       }
-       
-       return NULL;
-}
-
-void undo_paint_push_count_alloc(int type, int size)
-{
-       if (type == UNDO_PAINT_IMAGE)
-               ImageUndoStack.current->undosize += size;
-       else if (type == UNDO_PAINT_MESH)
-               MeshUndoStack.current->undosize += size;
-}
-
-void ED_undo_paint_push_end(int type)
-{
-       if (type == UNDO_PAINT_IMAGE)
-               undo_stack_push_end(&ImageUndoStack);
-       else if (type == UNDO_PAINT_MESH)
-               undo_stack_push_end(&MeshUndoStack);
-}
-
-int ED_undo_paint_step(bContext *C, int type, int step, const char *name)
-{
-       if (type == UNDO_PAINT_IMAGE)
-               return undo_stack_step(C, &ImageUndoStack, step, name);
-       else if (type == UNDO_PAINT_MESH)
-               return undo_stack_step(C, &MeshUndoStack, step, name);
-       
-       return 0;
-}
-
-static void undo_step_num(bContext *C, UndoStack *stack, int step)
-{
-       UndoElem *uel;
-       int a = 0;
-       int curnum = BLI_findindex(&stack->elems, stack->current);
-
-       for (uel = stack->elems.first; uel; uel = uel->next, a++) {
-               if (a == step) break;
-       }
-
-       if (curnum > a) {
-               while (a++ != curnum)
-                       undo_stack_step(C, stack, 1, NULL);
-       }
-       else if (curnum < a) {
-               while (a-- != curnum)
-                       undo_stack_step(C, stack, -1, NULL);
-       }
-}
-
-void ED_undo_paint_step_num(bContext *C, int type, int step)
-{
-       if (type == UNDO_PAINT_IMAGE)
-               undo_step_num(C, &ImageUndoStack, step);
-       else if (type == UNDO_PAINT_MESH)
-               undo_step_num(C, &MeshUndoStack, step);
-}
-
-static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active)
-{
-       UndoElem *uel;
-
-       if (r_active) *r_active = false;
-
-       uel = BLI_findlink(&stack->elems, nr);
-       if (uel) {
-               if (r_active && (uel == stack->current)) {
-                       *r_active = true;
-               }
-               return uel->name;
-       }
-
-       return NULL;
-}
-
-const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active)
-{
-
-       if (type == UNDO_PAINT_IMAGE) {
-               undo_stack_cleanup(&ImageUndoStack, C);
-               return undo_stack_get_name(&ImageUndoStack, nr, r_active);
-       }
-       else if (type == UNDO_PAINT_MESH) {
-               undo_stack_cleanup(&MeshUndoStack, C);
-               return undo_stack_get_name(&MeshUndoStack, nr, r_active);
-       }
-       return NULL;
-}
-
-bool ED_undo_paint_empty(int type)
-{
-       UndoStack *stack;
-
-       if (type == UNDO_PAINT_IMAGE)
-               stack = &ImageUndoStack;
-       else if (type == UNDO_PAINT_MESH)
-               stack = &MeshUndoStack;
-       else
-               return true;
-
-       if (stack->current == NULL) {
-               return true;
-       }
-
-       return false;
-}
-
-bool ED_undo_paint_is_valid(int type, const char *name)
-{
-       UndoStack *stack;
-       
-       if (type == UNDO_PAINT_IMAGE)
-               stack = &ImageUndoStack;
-       else if (type == UNDO_PAINT_MESH)
-               stack = &MeshUndoStack;
-       else 
-               return 0;
-       
-       if (stack->current == NULL) {
-               /* pass */
-       }
-       else {
-               if (name && STREQ(stack->current->name, name))
-                       return 1;
-               else
-                       return stack->elems.first != stack->elems.last;
-       }
-       return 0;
-}
-
-void ED_undo_paint_free(void)
-{
-       undo_stack_free(&ImageUndoStack);
-       undo_stack_free(&MeshUndoStack);
-}
index cf1937f14d491cd39b9e80b88bd6177321f619f8..e46760258e1c9bfaa5c9595bc22f3cc0463e7ef8 100644 (file)
@@ -122,6 +122,8 @@ typedef struct SculptUndoNode {
 
        /* shape keys */
        char shapeName[sizeof(((KeyBlock *)0))->name];
+
+       size_t undo_size;
 } SculptUndoNode;
 
 /* Factor of brush to have rake point following behind
index 794ac14483a813072f3667e7839d8426a130c891..4e2bcab9f369af9cba20075b557b0f1c778df424 100644 (file)
@@ -48,6 +48,8 @@
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 #include "DNA_mesh_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
 
 #include "BKE_ccg.h"
 #include "BKE_context.h"
@@ -57,6 +59,9 @@
 #include "BKE_key.h"
 #include "BKE_mesh.h"
 #include "BKE_subsurf.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_undo_system.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
 #include "GPU_buffers.h"
 
 #include "ED_paint.h"
+#include "ED_object.h"
+#include "ED_sculpt.h"
 
 #include "bmesh.h"
 #include "paint_intern.h"
 #include "sculpt_intern.h"
 
-/************************** Undo *************************/
+
+typedef struct UndoSculpt {
+       ListBase nodes;
+
+       size_t undo_size;
+} UndoSculpt;
+
+static UndoSculpt *sculpt_undo_get_nodes(void);
 
 static void update_cb(PBVHNode *node, void *rebuild)
 {
@@ -454,7 +468,7 @@ static int sculpt_undo_bmesh_restore(bContext *C,
        return false;
 }
 
-static void sculpt_undo_restore(bContext *C, ListBase *lb)
+static void sculpt_undo_restore_list(bContext *C, ListBase *lb)
 {
        Scene *scene = CTX_data_scene(C);
        Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
@@ -574,7 +588,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
        }
 }
 
-static void sculpt_undo_free(ListBase *lb)
+static void sculpt_undo_free_list(ListBase *lb)
 {
        SculptUndoNode *unode;
        int i;
@@ -617,6 +631,8 @@ static void sculpt_undo_free(ListBase *lb)
        }
 }
 
+/* Most likely we don't need this. */
+#if 0
 static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
 {
        Object *ob = CTX_data_active_object(C);
@@ -633,16 +649,17 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
 
        return false;
 }
+#endif
 
 SculptUndoNode *sculpt_undo_get_node(PBVHNode *node)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+       UndoSculpt *usculpt = sculpt_undo_get_nodes();
 
-       if (!lb) {
+       if (usculpt == NULL) {
                return NULL;
        }
 
-       return BLI_findptr(lb, node, offsetof(SculptUndoNode, node));
+       return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
 }
 
 static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
@@ -668,10 +685,11 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
        }
 }
 
-static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
-                                              SculptUndoType type)
+static SculptUndoNode *sculpt_undo_alloc_node(
+        Object *ob, PBVHNode *node,
+        SculptUndoType type)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+       UndoSculpt *usculpt = sculpt_undo_get_nodes();
        SculptUndoNode *unode;
        SculptSession *ss = ob->sculpt;
        int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
@@ -696,23 +714,23 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
        /* general TODO, fix count_alloc */
        switch (type) {
                case SCULPT_UNDO_COORDS:
-                       unode->co = MEM_mapallocN(sizeof(float) * 3 * allvert, "SculptUndoNode.co");
-                       unode->no = MEM_mapallocN(sizeof(short) * 3 * allvert, "SculptUndoNode.no");
-                       undo_paint_push_count_alloc(UNDO_PAINT_MESH,
-                                                   (sizeof(float) * 3 +
-                                                    sizeof(short) * 3 +
-                                                    sizeof(int)) * allvert);
+                       unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co");
+                       unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no");
+
+                       usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert;
                        break;
                case SCULPT_UNDO_HIDDEN:
                        if (maxgrid)
                                sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode);
                        else
                                unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden");
-               
+       
                        break;
                case SCULPT_UNDO_MASK:
                        unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask");
-                       undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert);
+
+                       usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
+
                        break;
                case SCULPT_UNDO_DYNTOPO_BEGIN:
                case SCULPT_UNDO_DYNTOPO_END:
@@ -721,7 +739,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
                        break;
        }
        
-       BLI_addtail(lb, unode);
+       BLI_addtail(&usculpt->nodes, unode);
 
        if (maxgrid) {
                /* multires */
@@ -798,12 +816,13 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
                                               PBVHNode *node,
                                               SculptUndoType type)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
-       SculptUndoNode *unode = lb->first;
+       UndoSculpt *usculpt = sculpt_undo_get_nodes();
        SculptSession *ss = ob->sculpt;
        PBVHVertexIter vd;
 
-       if (!lb->first) {
+       SculptUndoNode *unode = usculpt->nodes.first;
+
+       if (unode == NULL) {
                unode = MEM_callocN(sizeof(*unode), __func__);
 
                BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
@@ -842,7 +861,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
                        unode->bm_entry = BM_log_entry_add(ss->bm_log);
                }
 
-               BLI_addtail(lb, unode);
+               BLI_addtail(&usculpt->nodes, unode);
        }
 
        if (node) {
@@ -883,8 +902,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
        return unode;
 }
 
-SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
-                                      SculptUndoType type)
+SculptUndoNode *sculpt_undo_push_node(
+        Object *ob, PBVHNode *node,
+        SculptUndoType type)
 {
        SculptSession *ss = ob->sculpt;
        SculptUndoNode *unode;
@@ -954,17 +974,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
 
 void sculpt_undo_push_begin(const char *name)
 {
-       ED_undo_paint_push_begin(UNDO_PAINT_MESH, name,
-                                sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup);
+       bContext *C = NULL; /* special case, we never read from this. */
+       wmWindowManager *wm = G.main->wm.first;
+       BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_SCULPT);
 }
 
 void sculpt_undo_push_end(void)
 {
-       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
+       UndoSculpt *usculpt = sculpt_undo_get_nodes();
        SculptUndoNode *unode;
 
        /* we don't need normals in the undo stack */
-       for (unode = lb->first; unode; unode = unode->next) {
+       for (unode = usculpt->nodes.first; unode; unode = unode->next) {
                if (unode->no) {
                        MEM_freeN(unode->no);
                        unode->no = NULL;
@@ -974,7 +995,97 @@ void sculpt_undo_push_end(void)
                        BKE_pbvh_node_layer_disp_free(unode->node);
        }
 
-       ED_undo_paint_push_end(UNDO_PAINT_MESH);
+       wmWindowManager *wm = G.main->wm.first;  /* XXX, avoids adding extra arg. */
+       BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct SculptUndoStep {
+       UndoStep step;
+       /* note: will split out into list for multi-object-sculpt-mode. */
+       UndoSculpt data;
+} SculptUndoStep;
+
+static bool sculpt_undosys_poll(bContext *C)
+{
+       ScrArea *sa = CTX_wm_area(C);
+       if (sa && (sa->spacetype == SPACE_VIEW3D)) {
+               Object *obact = CTX_data_active_object(C);
+               if (obact && (obact->mode & OB_MODE_SCULPT)) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+       SculptUndoStep *us = (SculptUndoStep *)us_p;
+       /* dummy, memory is cleared anyway. */
+       BLI_listbase_clear(&us->data.nodes);
+}
+
+static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
+{
+       /* dummy, encoding is done along the way by adding tiles
+        * to the current 'SculptUndoStep' added by encode_init. */
+       SculptUndoStep *us = (SculptUndoStep *)us_p;
+       us->step.data_size = us->data.undo_size;
+       return true;
+}
+
+static void sculpt_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* TODO(campbell): undo_system: use low-level API to set mode. */
+       ED_object_mode_set(C, OB_MODE_SCULPT);
+       BLI_assert(sculpt_undosys_poll(C));
+
+       SculptUndoStep *us = (SculptUndoStep *)us_p;
+       sculpt_undo_restore_list(C, &us->data.nodes);
+}
 
-       WM_file_tag_modified();
+static void sculpt_undosys_step_free(UndoStep *us_p)
+{
+       SculptUndoStep *us = (SculptUndoStep *)us_p;
+       sculpt_undo_free_list(&us->data.nodes);
 }
+
+/* Export for ED_undo_sys. */
+void ED_sculpt_undosys_type(UndoType *ut)
+{
+       ut->name = "Sculpt";
+       ut->poll = sculpt_undosys_poll;
+       ut->step_encode_init = sculpt_undosys_step_encode_init;
+       ut->step_encode = sculpt_undosys_step_encode;
+       ut->step_decode = sculpt_undosys_step_decode;
+       ut->step_free = sculpt_undosys_step_free;
+
+       ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(SculptUndoStep);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p)
+{
+       SculptUndoStep *us = (SculptUndoStep *)us_p;
+       return &us->data;
+}
+
+static UndoSculpt *sculpt_undo_get_nodes(void)
+{
+       wmWindowManager *wm = G.main->wm.first;  /* XXX, avoids adding extra arg. */
+       UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_SCULPT);
+       return sculpt_undosys_step_get_nodes(us);
+}
+
+/** \} */
index 9740a62d4f03a467e7ada1681a4c369911933549..c1586964b1f804829dcf6b6c5b108e7b78cb2d72 100644 (file)
@@ -2658,8 +2658,9 @@ static int image_invert_exec(bContext *C, wmOperator *op)
        if (ibuf->mipmap[0])
                ibuf->userflags |= IB_MIPMAP_INVALID;
 
-       if (support_undo)
-               ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
+       if (support_undo) {
+               ED_image_undo_push_end();
+       }
 
        /* force GPU reupload, all image is invalid */
        GPU_free_image(ima);
index 39b48f5b52c11a971c24ea59929862821f22cc53..91420a5d63aa5f5fe22388655fbf90572e21e903 100644 (file)
@@ -48,6 +48,7 @@ set(SRC
        text_format_py.c
        text_header.c
        text_ops.c
+       text_undo.c
 
        text_format.h
        text_intern.h
index f75cd129f673a36e8b8d6d4c0a702da656b7fe22..7580f22321bf5e9066dd7f958f65fdb686311f94 100644 (file)
@@ -756,7 +756,10 @@ void TEXT_OT_paste(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_paste_exec;
        ot->poll = text_edit_poll;
-       
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
        /* properties */
        RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied (X11 only)");
 }
@@ -785,10 +788,13 @@ void TEXT_OT_duplicate_line(wmOperatorType *ot)
        ot->name = "Duplicate Line";
        ot->idname = "TEXT_OT_duplicate_line";
        ot->description = "Duplicate the current line";
-       
+
        /* api callbacks */
        ot->exec = text_duplicate_line_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* copy operator *********************/
@@ -860,6 +866,9 @@ void TEXT_OT_cut(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_cut_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* indent operator *********************/
@@ -895,6 +904,9 @@ void TEXT_OT_indent(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_indent_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* unindent operator *********************/
@@ -926,6 +938,9 @@ void TEXT_OT_unindent(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_unindent_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* line break operator *********************/
@@ -970,10 +985,13 @@ void TEXT_OT_line_break(wmOperatorType *ot)
        ot->name = "Line Break";
        ot->idname = "TEXT_OT_line_break";
        ot->description = "Insert line break at cursor position";
-       
+
        /* api callbacks */
        ot->exec = text_line_break_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* comment operator *********************/
@@ -1003,10 +1021,13 @@ void TEXT_OT_comment(wmOperatorType *ot)
        ot->name = "Comment";
        ot->idname = "TEXT_OT_comment";
        ot->description = "Convert selected text to comment";
-       
+
        /* api callbacks */
        ot->exec = text_comment_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* uncomment operator *********************/
@@ -1041,6 +1062,9 @@ void TEXT_OT_uncomment(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_uncomment_exec;
        ot->poll = text_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* convert whitespace operator *********************/
@@ -1174,6 +1198,9 @@ void TEXT_OT_convert_whitespace(wmOperatorType *ot)
        ot->exec = text_convert_whitespace_exec;
        ot->poll = text_edit_poll;
 
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
        /* properties */
        RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "Type", "Type of whitespace to convert to");
 }
@@ -1295,6 +1322,9 @@ void TEXT_OT_move_lines(wmOperatorType *ot)
        ot->exec = move_lines_exec;
        ot->poll = text_edit_poll;
 
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
        /* properties */
        RNA_def_enum(ot->srna, "direction", direction_items, 1, "Direction", "");
 }
@@ -2919,6 +2949,9 @@ void TEXT_OT_insert(wmOperatorType *ot)
        ot->invoke = text_insert_invoke;
        ot->poll = text_edit_poll;
 
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
+
        /* properties */
        prop = RNA_def_string(ot->srna, "text", NULL, 0, "Text", "Text to insert at the cursor position");
        RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -3024,6 +3057,9 @@ void TEXT_OT_replace(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_replace_exec;
        ot->poll = text_space_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /******************* find set selected *********************/
@@ -3081,6 +3117,9 @@ void TEXT_OT_replace_set_selected(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = text_replace_set_selected_exec;
        ot->poll = text_space_edit_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_UNDO;
 }
 
 /****************** resolve conflict operator ******************/
@@ -3201,26 +3240,3 @@ void TEXT_OT_to_3d_object(wmOperatorType *ot)
        /* properties */
        RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text");
 }
-
-
-/************************ undo ******************************/
-
-void ED_text_undo_step(bContext *C, int step)
-{
-       Text *text = CTX_data_edit_text(C);
-
-       if (!text)
-               return;
-
-       if (step == 1)
-               txt_do_undo(text);
-       else if (step == -1)
-               txt_do_redo(text);
-
-       text_update_edited(text);
-
-       text_update_cursor_moved(C);
-       text_drawcache_tag_update(CTX_wm_space_text(C), 1);
-       WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
-}
-
diff --git a/source/blender/editors/space_text/text_undo.c b/source/blender/editors/space_text/text_undo.c
new file mode 100644 (file)
index 0000000..6bac00f
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_text/text_undo.c
+ *  \ingroup sptext
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_text_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_array_utils.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_text.h"
+#include "BKE_undo_system.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_text.h"
+#include "ED_curve.h"
+#include "ED_screen.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "text_intern.h"
+#include "text_format.h"
+
+/* TODO(campbell): undo_system: move text undo out of text block. */
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+typedef struct TextUndoBuf {
+       char *buf;
+       int len;
+       int pos;
+} TextUndoBuf;
+
+typedef struct TextUndoStep {
+       UndoStep step;
+       UndoRefID_Text text_ref;
+       TextUndoBuf data;
+} TextUndoStep;
+
+static bool text_undosys_poll(bContext *C)
+{
+       Text *text = CTX_data_edit_text(C);
+       if (text == NULL) {
+               return false;
+       }
+       if (ID_IS_LINKED(text)) {
+               return false;
+       }
+       return true;
+}
+
+static bool text_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+       TextUndoStep *us = (TextUndoStep *)us_p;
+       Text *text = CTX_data_edit_text(C);
+       us->text_ref.ptr = text;
+
+       BLI_assert(BLI_array_is_zeroed(&us->data, 1));
+
+       us->data.buf = text->undo_buf;
+       us->data.pos = text->undo_pos;
+       us->data.len = text->undo_len;
+
+       text->undo_buf = NULL;
+       text->undo_len = 0;
+       text->undo_pos = -1;
+
+       us->step.data_size = text->undo_len;
+
+       return true;
+}
+
+static void text_undosys_step_decode(struct bContext *C, UndoStep *us_p, int dir)
+{
+       TextUndoStep *us = (TextUndoStep *)us_p;
+       Text *text = us->text_ref.ptr;
+
+       /* TODO(campbell): undo_system: move undo out of Text data block. */
+       text->undo_buf = us->data.buf;
+       text->undo_len = us->data.len;
+       if (dir < 0) {
+               text->undo_pos = us->data.pos;
+               txt_do_undo(text);
+       }
+       else {
+               text->undo_pos = -1;
+               txt_do_redo(text);
+       }
+       text->undo_buf = NULL;
+       text->undo_len = 0;
+       text->undo_pos = -1;
+
+       text_update_edited(text);
+       text_update_cursor_moved(C);
+       text_drawcache_tag_update(CTX_wm_space_text(C), 1);
+       WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
+}
+
+static void text_undosys_step_free(UndoStep *us_p)
+{
+       TextUndoStep *us = (TextUndoStep *)us_p;
+       MEM_SAFE_FREE(us->data.buf);
+}
+
+static void text_undosys_foreach_ID_ref(
+        UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
+{
+       TextUndoStep *us = (TextUndoStep *)us_p;
+       foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->text_ref));
+}
+
+/* Export for ED_undo_sys. */
+
+void ED_text_undosys_type(UndoType *ut)
+{
+       ut->name = "Text";
+       ut->poll = text_undosys_poll;
+       ut->step_encode = text_undosys_step_encode;
+       ut->step_decode = text_undosys_step_decode;
+       ut->step_free = text_undosys_step_free;
+
+       ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref;
+
+       ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(TextUndoStep);
+}
+
+/** \} */
index a8225bb64d118970d4631d83def004f805452fb3..b2c0f6ad3094c652aa0eba01e57783db39ae73d5 100644 (file)
@@ -41,9 +41,10 @@ set(INC_SYS
 set(SRC
        ed_transverts.c
        ed_util.c
-       editmode_undo.c
+       memfile_undo.c
        numinput.c
        undo.c
+       undo_system_types.c
 
        util_intern.h
        # general includes
index 57ff8b6db01d17dd3f46f2aca3a558e1d96d39e1..03d0b4a8d48504e9ea05dd36c13e07e2fe497448 100644 (file)
@@ -58,6 +58,7 @@
 #include "BKE_packedFile.h"
 #include "BKE_paint.h"
 #include "BKE_screen.h"
+#include "BKE_undo_system.h"
 
 #include "ED_armature.h"
 #include "ED_buttons.h"
@@ -89,6 +90,10 @@ void ED_editors_init(bContext *C)
        Object *ob, *obact = (sce && sce->basact) ? sce->basact->object : NULL;
        ID *data;
 
+       if (wm->undo_stack == NULL) {
+               wm->undo_stack = BKE_undosys_stack_create();
+       }
+
        /* This is called during initialization, so we don't want to store any reports */
        ReportList *reports = CTX_wm_reports(C);
        int reports_flag_prev = reports->flag & ~RPT_STORE;
@@ -128,9 +133,15 @@ void ED_editors_exit(bContext *C)
                return;
        
        /* frees all editmode undos */
-       undo_editmode_clear();
-       ED_undo_paint_free();
-       
+       if (G.main->wm.first) {
+               wmWindowManager *wm = G.main->wm.first;
+               /* normally we don't check for NULL undo stack, do here since it may run in different context. */
+               if (wm->undo_stack) {
+                       BKE_undosys_stack_destroy(wm->undo_stack);
+                       wm->undo_stack = NULL;
+               }
+       }
+
        for (sce = bmain->scene.first; sce; sce = sce->id.next) {
                if (sce->obedit) {
                        Object *ob = sce->obedit;
diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c
deleted file mode 100644 (file)
index fa697a0..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * 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.
- *
- * The Original Code is Copyright (C) 2004 Blender Foundation
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): none yet.
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/editors/util/editmode_undo.c
- *  \ingroup edutil
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_object_types.h"
-
-#include "BLI_blenlib.h"
-#include "BLI_utildefines.h"
-
-#include "BKE_blender_undo.h"
-#include "BKE_context.h"
-#include "BKE_depsgraph.h"
-#include "BKE_global.h"
-
-#include "ED_util.h"
-#include "ED_mesh.h"
-
-#include "util_intern.h"
-
-/* ****** XXX ***** */
-static void error(const char *UNUSED(arg)) {}
-/* ****** XXX ***** */
-
-typedef struct UndoElem {
-       struct UndoElem *next, *prev;
-       /** copy of edit-mode object ID */
-       ID id;
-       /** pointer to edited object */
-       Object *ob;
-       /** type of edited object */
-       int type;
-       void *undodata;
-       uintptr_t undosize;
-       char name[BKE_UNDO_STR_MAX];
-
-       /** Use context to retrieve current edit-data. */
-       void * (*getdata)(bContext * C);
-       /** Pointer to function freeing data. */
-       void (*freedata)(void *);
-        /** Data to edit-mode conversion. */
-       void (*to_editmode)(void *, void *, void *);
-       /** Edit-mode to data conversion. */
-       void * (*from_editmode)(void *, void *);
-       /** Check if undo data is still valid. */
-       int (*validate_undo)(void *, void *);
-} UndoElem;
-
-static ListBase g_undobase = {NULL, NULL};
-static UndoElem *g_curundo = NULL;
-
-static void undo_restore(UndoElem *undo, void *editdata, void *obdata)
-{
-       if (undo) {
-               undo->to_editmode(undo->undodata, editdata, obdata);
-       }
-}
-
-/**
- * name can be a dynamic string
- * See #UndoElem for callbacks docs.
- * */
-void undo_editmode_push(
-        bContext *C, const char *name,
-        void * (*getdata)(bContext * C),
-        void (*freedata)(void *),
-        void (*to_editmode)(void *, void *, void *),
-        void *(*from_editmode)(void *, void *),
-        int (*validate_undo)(void *, void *))
-{
-       UndoElem *uel;
-       Object *obedit = CTX_data_edit_object(C);
-       void *editdata;
-       int nr;
-       uintptr_t mem_used, mem_total, mem_max;
-
-       /* at first here was code to prevent an "original" key to be inserted twice
-        * this was giving conflicts for example when mesh changed due to keys or apply */
-       
-       /* remove all undos after (also when g_curundo == NULL) */
-       while (g_undobase.last != g_curundo) {
-               uel = g_undobase.last;
-               uel->freedata(uel->undodata);
-               BLI_freelinkN(&g_undobase, uel);
-       }
-       
-       /* make new */
-       g_curundo = uel = MEM_callocN(sizeof(UndoElem), "undo editmode");
-       BLI_strncpy(uel->name, name, sizeof(uel->name));
-       BLI_addtail(&g_undobase, uel);
-       
-       uel->getdata = getdata;
-       uel->freedata = freedata;
-       uel->to_editmode = to_editmode;
-       uel->from_editmode = from_editmode;
-       uel->validate_undo = validate_undo;
-       
-       /* limit amount to the maximum amount*/
-       nr = 0;
-       uel = g_undobase.last;
-       while (uel) {
-               nr++;
-               if (nr == U.undosteps) {
-                       break;
-               }
-               uel = uel->prev;
-       }
-       if (uel) {
-               while (g_undobase.first != uel) {
-                       UndoElem *first = g_undobase.first;
-                       first->freedata(first->undodata);
-                       BLI_freelinkN(&g_undobase, first);
-               }
-       }
-
-       /* copy  */
-       mem_used = MEM_get_memory_in_use();
-       editdata = getdata(C);
-       g_curundo->undodata = g_curundo->from_editmode(editdata, obedit->data);
-       g_curundo->undosize = MEM_get_memory_in_use() - mem_used;
-       g_curundo->ob = obedit;
-       g_curundo->id = obedit->id;
-       g_curundo->type = obedit->type;
-
-       if (U.undomemory != 0) {
-               /* limit to maximum memory (afterwards, we can't know in advance) */
-               mem_total = 0;
-               mem_max = ((uintptr_t)U.undomemory) * 1024 * 1024;
-
-               uel = g_undobase.last;
-               while (uel && uel->prev) {
-                       mem_total += uel->undosize;
-                       if (mem_total > mem_max) {
-                               break;
-                       }
-                       uel = uel->prev;
-               }
-
-               if (uel) {
-                       if (uel->prev && uel->prev->prev) {
-                               uel = uel->prev;
-                       }
-                       while (g_undobase.first != uel) {
-                               UndoElem *first = g_undobase.first;
-                               first->freedata(first->undodata);
-                               BLI_freelinkN(&g_undobase, first);
-                       }
-               }
-       }
-}
-
-/* helper to remove clean other objects from undo stack */
-static void undo_clean_stack(bContext *C)
-{
-       UndoElem *uel;
-       Object *obedit = CTX_data_edit_object(C);
-
-       /* global undo changes pointers, so we also allow identical names */
-       /* side effect: when deleting/renaming object and start editing new one with same name */
-
-       uel = g_undobase.first;
-       while (uel) {
-               void *editdata = uel->getdata(C);
-               bool is_valid = false;
-               UndoElem *uel_next = uel->next;
-
-               /* for when objects are converted, renamed, or global undo changes pointers... */
-               if (uel->type == obedit->type) {
-                       if (STREQ(uel->id.name, obedit->id.name)) {
-                               if (uel->validate_undo == NULL) {
-                                       is_valid = true;
-                               }
-                               else if (uel->validate_undo(uel->undodata, editdata)) {
-                                       is_valid = true;
-                               }
-                       }
-               }
-               if (is_valid) {
-                       uel->ob = obedit;
-               }
-               else {
-                       if (uel == g_curundo) {
-                               g_curundo = NULL;
-                       }
-
-                       uel->freedata(uel->undodata);
-                       BLI_freelinkN(&g_undobase, uel);
-               }
-
-               uel = uel_next;
-       }
-
-       if (g_curundo == NULL) {
-               g_curundo = g_undobase.last;
-       }
-}
-
-/**
- * 1 = an undo, -1 is a redo.
- * we have to make sure 'g_curundo' remains at current situation
- */
-void undo_editmode_step(bContext *C, int step)
-{
-       Object *obedit = CTX_data_edit_object(C);
-
-       /* prevent undo to happen on wrong object, stack can be a mix */
-       undo_clean_stack(C);
-
-       if (step == 0) {
-               undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
-       }
-       else if (step == 1) {
-               if (g_curundo == NULL || g_curundo->prev == NULL) {
-                       error("No more steps to undo");
-               }
-               else {
-                       if (G.debug & G_DEBUG) printf("undo %s\n", g_curundo->name);
-                       g_curundo = g_curundo->prev;
-                       undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
-               }
-       }
-       else {
-               /* g_curundo has to remain current situation! */
-               if (g_curundo == NULL || g_curundo->next == NULL) {
-                       error("No more steps to redo");
-               }
-               else {
-                       undo_restore(g_curundo->next, g_curundo->getdata(C), obedit->data);
-                       g_curundo = g_curundo->next;
-                       if (G.debug & G_DEBUG) printf("redo %s\n", g_curundo->name);
-               }
-       }
-
-       /* special case for editmesh, mode must be copied back to the scene */
-       if (obedit->type == OB_MESH) {
-               EDBM_selectmode_to_scene(C);
-       }
-
-       DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
-
-       /* XXX notifiers */
-}
-
-void undo_editmode_clear(void)
-{
-       UndoElem *uel;
-
-       uel = g_undobase.first;
-       while (uel) {
-               uel->freedata(uel->undodata);
-               uel = uel->next;
-       }
-       BLI_freelistN(&g_undobase);
-       g_curundo = NULL;
-}
-
-/* based on index nr it does a restore */
-void undo_editmode_number(bContext *C, int nr)
-{
-       UndoElem *uel;
-       int a = 1;
-
-       for (uel = g_undobase.first; uel; uel = uel->next, a++) {
-               if (a == nr) {
-                       break;
-               }
-       }
-       g_curundo = uel;
-       undo_editmode_step(C, 0);
-}
-
-void undo_editmode_name(bContext *C, const char *undoname)
-{
-       UndoElem *uel;
-
-       for (uel = g_undobase.last; uel; uel = uel->prev) {
-               if (STREQ(undoname, uel->name)) {
-                       break;
-               }
-       }
-       if (uel && uel->prev) {
-               g_curundo = uel->prev;
-               undo_editmode_step(C, 0);
-       }
-}
-
-/**
- * \a undoname is optional, when NULL it just checks for existing undo steps
- */
-bool undo_editmode_is_valid(const char *undoname)
-{
-       if (undoname) {
-               UndoElem *uel;
-
-               for (uel = g_undobase.last; uel; uel = uel->prev) {
-                       if (STREQ(undoname, uel->name)) {
-                               break;
-                       }
-               }
-               return uel != NULL;
-       }
-       return g_undobase.last != g_undobase.first;
-}
-
-
-/**
- * Get name of undo item, return null if no item with this index.
- *
- * if active pointer, set it to 1 if true
- */
-const char *undo_editmode_get_name(bContext *C, int nr, bool *r_active)
-{
-       UndoElem *uel;
-
-       /* prevent wrong numbers to be returned */
-       undo_clean_stack(C);
-
-       if (r_active) {
-               *r_active = false;
-       }
-
-       uel = BLI_findlink(&g_undobase, nr);
-       if (uel) {
-               if (r_active && (uel == g_curundo)) {
-                       *r_active = true;
-               }
-               return uel->name;
-       }
-       return NULL;
-}
-
-
-void *undo_editmode_get_prev(Object *ob)
-{
-       UndoElem *ue = g_undobase.last;
-       if (ue && ue->prev && ue->prev->ob == ob) {
-               return ue->prev->undodata;
-       }
-       return NULL;
-}
diff --git a/source/blender/editors/util/memfile_undo.c b/source/blender/editors/util/memfile_undo.c
new file mode 100644 (file)
index 0000000..95af0c4
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/util/memfile_undo.c
+ *  \ingroup edutil
+ *
+ * Wrapper between 'BKE_undo.h' and 'BKE_undo_system.h'
+ */
+
+#include "BLI_utildefines.h"
+#include "BLI_sys_types.h"
+
+#include "DNA_object_enums.h"
+
+#include "BKE_blender_undo.h"
+#include "BKE_context.h"
+#include "BKE_undo_system.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_util.h"
+#include "ED_render.h"
+
+
+#include "../blenloader/BLO_undofile.h"
+
+#include "util_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Implements ED Undo System
+ * \{ */
+
+typedef struct MemFileUndoStep {
+       UndoStep step;
+       MemFileUndoData *data;
+} MemFileUndoStep;
+
+static bool memfile_undosys_poll(bContext *UNUSED(C))
+{
+       /* other poll functions must run first, this is a catch-all. */
+
+       if ((U.uiflag & USER_GLOBALUNDO) == 0) {
+               return false;
+       }
+       return true;
+}
+
+static bool memfile_undosys_step_encode(struct bContext *C, UndoStep *us_p)
+{
+       MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+
+       /* Important we only use 'main' from the context (see: BKE_undosys_stack_init_from_main). */
+       struct Main *bmain = CTX_data_main(C);
+
+       /* can be NULL, use when set. */
+       MemFileUndoStep *us_prev = (MemFileUndoStep *)BKE_undosys_step_same_type_prev(us_p);
+       us->data = BKE_memfile_undo_encode(bmain, us_prev ? us_prev->data : NULL);
+       us->step.data_size = us->data->undo_size;
+       return true;
+}
+
+static void memfile_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
+{
+       /* Loading the content will correctly switch into compatible non-object modes. */
+       ED_object_mode_set(C, OB_MODE_OBJECT);
+
+       /* This is needed so undoing/redoing doesn't crash with threaded previews going */
+       ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true);
+       MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+       BKE_memfile_undo_decode(us->data, C);
+
+       WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
+}
+
+static void memfile_undosys_step_free(UndoStep *us_p)
+{
+       /* To avoid unnecessary slow down, free backwards (so we don't need to merge when clearing all). */
+       MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+       if (us_p->next != NULL) {
+               UndoStep *us_next_p = BKE_undosys_step_same_type_next(us_p);
+               if (us_next_p != NULL) {
+                       MemFileUndoStep *us_next = (MemFileUndoStep *)us_next_p;
+                       BLO_memfile_merge(&us->data->memfile, &us_next->data->memfile);
+               }
+       }
+
+       BKE_memfile_undo_free(us->data);
+}
+
+/* Export for ED_undo_sys. */
+void ED_memfile_undosys_type(UndoType *ut)
+{
+       ut->name = "Global Undo";
+       ut->poll = memfile_undosys_poll;
+       ut->step_encode = memfile_undosys_step_encode;
+       ut->step_decode = memfile_undosys_step_decode;
+       ut->step_free = memfile_undosys_step_free;
+
+       ut->mode = BKE_UNDOTYPE_MODE_STORE;
+       ut->use_context = true;
+
+       ut->step_size = sizeof(MemFileUndoStep);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utilities
+ * \{ */
+
+/**
+ * Ideally we wouldn't need to export global undo internals, there are some cases where it's needed though.
+ */
+static struct MemFile *ed_undosys_step_get_memfile(UndoStep *us_p)
+{
+       MemFileUndoStep *us = (MemFileUndoStep *)us_p;
+       return &us->data->memfile;
+}
+
+struct MemFile *ED_undosys_stack_memfile_get_active(UndoStack *ustack)
+{
+       UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_MEMFILE);
+       if (us) {
+               return ed_undosys_step_get_memfile(us);
+       }
+       return NULL;
+}
+
+
+/** \} */
index d791818331e1cdbd27f542d5b86a36beecd77252..fea3360a39c80cde703c8d0177697d22c40002fc 100644 (file)
@@ -47,6 +47,7 @@
 #include "BKE_global.h"
 #include "BKE_main.h"
 #include "BKE_screen.h"
+#include "BKE_undo_system.h"
 
 #include "ED_armature.h"
 #include "ED_particle.h"
 
 void ED_undo_push(bContext *C, const char *str)
 {
-       Object *obedit = CTX_data_edit_object(C);
-       Object *obact = CTX_data_active_object(C);
-
-       if (G.debug & G_DEBUG)
+       if (G.debug & G_DEBUG) {
                printf("%s: %s\n", __func__, str);
-
-       if (obedit) {
-               if (U.undosteps == 0) return;
-               
-               if (obedit->type == OB_MESH)
-                       undo_push_mesh(C, str);
-               else if (ELEM(obedit->type, OB_CURVE, OB_SURF))
-                       undo_push_curve(C, str);
-               else if (obedit->type == OB_FONT)
-                       undo_push_font(C, str);
-               else if (obedit->type == OB_MBALL)
-                       undo_push_mball(C, str);
-               else if (obedit->type == OB_LATTICE)
-                       undo_push_lattice(C, str);
-               else if (obedit->type == OB_ARMATURE)
-                       undo_push_armature(C, str);
        }
-       else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
-               if (U.undosteps == 0) return;
+       const int steps = U.undosteps;
 
-               PE_undo_push(CTX_data_scene(C), str);
+       if (steps <= 0) {
+               return;
        }
-       else if (obact && obact->mode & OB_MODE_SCULPT) {
-               /* do nothing for now */
+
+       wmWindowManager *wm = CTX_wm_manager(C);
+
+       /* Only apply limit if this is the last undo step. */
+       if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) {
+               BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0);
        }
-       else {
-               BKE_undo_write(C, str);
+
+       BKE_undosys_step_push(wm->undo_stack, C, str);
+
+       if (U.undomemory != 0) {
+               const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024;
+               BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, 0, memory_limit);
        }
 
        WM_file_tag_modified();
@@ -119,11 +109,8 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
 {
        wmWindowManager *wm = CTX_wm_manager(C);
        wmWindow *win = CTX_wm_window(C);
-       Main *bmain = CTX_data_main(C);
+       // Main *bmain = CTX_data_main(C);
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
-       Object *obact = CTX_data_active_object(C);
-       ScrArea *sa = CTX_wm_area(C);
 
        /* undo during jobs are running can easily lead to freeing data using by jobs,
         * or they can just lead to freezing job in some other cases */
@@ -131,100 +118,45 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
                return OPERATOR_CANCELLED;
        }
 
+       /* TODO(campbell): undo_system: use undo system */
        /* grease pencil can be can be used in plenty of spaces, so check it first */
        if (ED_gpencil_session_active()) {
                return ED_undo_gpencil_step(C, step, undoname);
        }
 
-       if (sa && (sa->spacetype == SPACE_IMAGE)) {
-               SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-               
-               if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-                       if (!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) {
-                               if (U.uiflag & USER_GLOBALUNDO) {
-                                       ED_viewport_render_kill_jobs(wm, bmain, true);
-                                       BKE_undo_name(C, undoname);
-                               }
-                       }
-                       
-                       WM_event_add_notifier(C, NC_WINDOW, NULL);
-                       return OPERATOR_FINISHED;
+       /* Undo System */
+       {
+               if (undoname) {
+                       UndoStep *step_data = BKE_undosys_step_find_by_name(wm->undo_stack, undoname);
+                       BKE_undosys_step_undo_with_data(wm->undo_stack, C, step_data);
                }
-       }
-
-       if (sa && (sa->spacetype == SPACE_TEXT)) {
-               ED_text_undo_step(C, step);
-       }
-       else if (obedit) {
-               if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-                       if (undoname)
-                               undo_editmode_name(C, undoname);
-                       else
-                               undo_editmode_step(C, step);
-                       
-                       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+               else {
+                       BKE_undosys_step_undo_compat_only(wm->undo_stack, C, step);
                }
        }
-       else {
-               /* Note: we used to do a fall-through here where if the
-                * mode-specific undo system had no more steps to undo (or
-                * redo), the global undo would run.
-                *
-                * That was inconsistent with editmode, and also makes for
-                * unecessarily tricky interaction with the other undo
-                * systems. */
-               if (obact && obact->mode & OB_MODE_TEXTURE_PAINT) {
-                       ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname);
-               }
-               else if (obact && obact->mode & OB_MODE_SCULPT) {
-                       ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname);
-               }
-               else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
-                       if (step == 1)
-                               PE_undo(scene);
-                       else
-                               PE_redo(scene);
-               }
-               else if (U.uiflag & USER_GLOBALUNDO) {
-                       // note python defines not valid here anymore.
-                       //#ifdef WITH_PYTHON
-                       // XXX          BPY_scripts_clear_pyobjects();
-                       //#endif
-                       
-                       /* for global undo/redo we should just clear the editmode stack */
-                       /* for example, texface stores image pointers */
-                       undo_editmode_clear();
-                       
-                       ED_viewport_render_kill_jobs(wm, bmain, true);
-
-                       if (undoname)
-                               BKE_undo_name(C, undoname);
-                       else
-                               BKE_undo_step(C, step);
 
-                       scene = CTX_data_scene(C);
-                               
-                       WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
-               }
-       }
-       
        WM_event_add_notifier(C, NC_WINDOW, NULL);
        WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL);
 
        if (win) {
                win->addmousemove = true;
        }
-       
+
        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();
+       {
+               wmWindowManager *wm = CTX_wm_manager(C);
+               if (wm->undo_stack->steps.last) {
+                       const UndoStep *us = wm->undo_stack->steps.last;
+                       if (STREQ(str, us->name)) {
+                               return;
+                       }
+               }
 
-       if (last_undo && STREQ(str, last_undo)) {
-               return;
        }
 
        /* push as usual */
@@ -265,47 +197,8 @@ void ED_undo_pop_op(bContext *C, wmOperator *op)
 /* name optionally, function used to check for operator redo panel */
 bool ED_undo_is_valid(const bContext *C, const char *undoname)
 {
-       Object *obedit = CTX_data_edit_object(C);
-       Object *obact = CTX_data_active_object(C);
-       ScrArea *sa = CTX_wm_area(C);
-       
-       if (sa && sa->spacetype == SPACE_IMAGE) {
-               SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-               
-               if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-                       return 1;
-               }
-       }
-       
-       if (sa && (sa->spacetype == SPACE_TEXT)) {
-               return 1;
-       }
-       else if (obedit) {
-               if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-                       return undo_editmode_is_valid(undoname);
-               }
-       }
-       else {
-               
-               /* if below tests fail, global undo gets executed */
-               
-               if (obact && obact->mode & OB_MODE_TEXTURE_PAINT) {
-                       if (ED_undo_paint_is_valid(UNDO_PAINT_IMAGE, undoname))
-                               return 1;
-               }
-               else if (obact && obact->mode & OB_MODE_SCULPT) {
-                       if (ED_undo_paint_is_valid(UNDO_PAINT_MESH, undoname))
-                               return 1;
-               }
-               else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
-                       return PE_undo_is_valid(CTX_data_scene(C));
-               }
-               
-               if (U.uiflag & USER_GLOBALUNDO) {
-                       return BKE_undo_is_valid(undoname);
-               }
-       }
-       return 0;
+       wmWindowManager *wm = CTX_wm_manager(C);
+       return BKE_undosys_stack_has_undo(wm->undo_stack, undoname);
 }
 
 static int ed_undo_exec(bContext *C, wmOperator *UNUSED(op))
@@ -483,111 +376,45 @@ void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_ev
 
 /* ************************** */
 
-enum {
-       UNDOSYSTEM_GLOBAL   = 1,
-       UNDOSYSTEM_EDITMODE = 2,
-       UNDOSYSTEM_PARTICLE = 3,
-       UNDOSYSTEM_IMAPAINT = 4,
-       UNDOSYSTEM_SCULPT   = 5,
-};
-
-static int get_undo_system(bContext *C)
-{
-       Object *obact = CTX_data_active_object(C);
-       Object *obedit = CTX_data_edit_object(C);
-       ScrArea *sa = CTX_wm_area(C);
-
-       /* first check for editor undo */
-       if (sa && (sa->spacetype == SPACE_IMAGE)) {
-               SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
-
-               if ((obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
-                       if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE))
-                               return UNDOSYSTEM_IMAPAINT;
-               }
-       }
-       /* find out which undo system */
-       if (obedit) {
-               if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) {
-                       return UNDOSYSTEM_EDITMODE;
-               }
-       }
-       else {
-               if (obact) {
-                       if (obact->mode & OB_MODE_PARTICLE_EDIT)
-                               return UNDOSYSTEM_PARTICLE;
-                       else if (obact->mode & OB_MODE_TEXTURE_PAINT) {
-                               if (!ED_undo_paint_empty(UNDO_PAINT_IMAGE))
-                                       return UNDOSYSTEM_IMAPAINT;
-                       }
-                       else if (obact->mode & OB_MODE_SCULPT) {
-                               if (!ED_undo_paint_empty(UNDO_PAINT_MESH))
-                                       return UNDOSYSTEM_SCULPT;
-                       }
-               }
-               if (U.uiflag & USER_GLOBALUNDO)
-                       return UNDOSYSTEM_GLOBAL;
-       }
-       
-       return 0;
-}
-
 /* create enum based on undo items */
-static const EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem)
+static const EnumPropertyItem *rna_undo_itemf(bContext *C, int *totitem)
 {
        EnumPropertyItem item_tmp = {0}, *item = NULL;
        int i = 0;
-       bool active;
-       
-       while (true) {
-               const char *name = NULL;
-               
-               if (undosys == UNDOSYSTEM_PARTICLE) {
-                       name = PE_undo_get_name(CTX_data_scene(C), i, &active);
-               }
-               else if (undosys == UNDOSYSTEM_EDITMODE) {
-                       name = undo_editmode_get_name(C, i, &active);
-               }
-               else if (undosys == UNDOSYSTEM_IMAPAINT) {
-                       name = ED_undo_paint_get_name(C, UNDO_PAINT_IMAGE, i, &active);
-               }
-               else if (undosys == UNDOSYSTEM_SCULPT) {
-                       name = ED_undo_paint_get_name(C, UNDO_PAINT_MESH, i, &active);
-               }
-               else {
-                       name = BKE_undo_get_name(i, &active);
-               }
-               
-               if (name) {
-                       item_tmp.identifier = name;
+
+       wmWindowManager *wm = CTX_wm_manager(C);
+       if (wm->undo_stack == NULL) {
+               return NULL;
+       }
+
+       for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) {
+               if (us->skip == false) {
+                       item_tmp.identifier = us->name;
                        /* XXX This won't work with non-default contexts (e.g. operators) :/ */
-                       item_tmp.name = IFACE_(name);
-                       if (active)
+                       item_tmp.name = IFACE_(us->name);
+                       if (us == wm->undo_stack->step_active) {
                                item_tmp.icon = ICON_RESTRICT_VIEW_OFF;
-                       else 
+                       }
+                       else {
                                item_tmp.icon = ICON_NONE;
-                       item_tmp.value = i++;
+                       }
+                       item_tmp.value = i;
                        RNA_enum_item_add(&item, totitem, &item_tmp);
                }
-               else
-                       break;
        }
-       
        RNA_enum_item_end(&item, totitem);
-       
+
        return item;
 }
 
 
 static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 {
-       int undosys, totitem = 0;
-       
-       undosys = get_undo_system(C);
-       
-       if (undosys) {
-               const EnumPropertyItem *item = rna_undo_itemf(C, undosys, &totitem);
-               
+       int totitem = 0;
+
+       {
+               const EnumPropertyItem *item = rna_undo_itemf(C, &totitem);
+
                if (totitem > 0) {
                        uiPopupMenu *pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
                        uiLayout *layout = UI_popup_menu_layout(pup);
@@ -621,30 +448,12 @@ static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
 /* note: also check ed_undo_step() in top if you change notifiers */
 static int undo_history_exec(bContext *C, wmOperator *op)
 {
-       if (RNA_struct_property_is_set(op->ptr, "item")) {
-               int undosys = get_undo_system(C);
-               int item = RNA_int_get(op->ptr, "item");
-               
-               if (undosys == UNDOSYSTEM_PARTICLE) {
-                       PE_undo_number(CTX_data_scene(C), item);
-               }
-               else if (undosys == UNDOSYSTEM_EDITMODE) {
-                       undo_editmode_number(C, item + 1);
-                       WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
-               }
-               else if (undosys == UNDOSYSTEM_IMAPAINT) {
-                       ED_undo_paint_step_num(C, UNDO_PAINT_IMAGE, item);
-               }
-               else if (undosys == UNDOSYSTEM_SCULPT) {
-                       ED_undo_paint_step_num(C, UNDO_PAINT_MESH, item);
-               }
-               else {
-                       ED_viewport_render_kill_jobs(CTX_wm_manager(C), CTX_data_main(C), true);
-                       BKE_undo_number(C, item);
-                       WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
-               }
+       PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item");
+       if (RNA_property_is_set(op->ptr, prop)) {
+               int item = RNA_property_int_get(op->ptr, prop);
+               wmWindowManager *wm = CTX_wm_manager(C);
+               BKE_undosys_step_undo_from_index(wm->undo_stack, C, item);
                WM_event_add_notifier(C, NC_WINDOW, NULL);
-               
                return OPERATOR_FINISHED;
        }
        return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/util/undo_system_types.c b/source/blender/editors/util/undo_system_types.c
new file mode 100644 (file)
index 0000000..a326d9e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/util/undo_system_types.c
+ *  \ingroup edutil
+ */
+
+#include <string.h>
+
+#include "BLI_utildefines.h"
+
+
+#include "ED_armature.h"
+#include "ED_curve.h"
+#include "ED_lattice.h"
+#include "ED_mball.h"
+#include "ED_mesh.h"
+#include "ED_paint.h"
+#include "ED_particle.h"
+#include "ED_sculpt.h"
+#include "ED_text.h"
+#include "ED_util.h"
+#include "util_intern.h"
+
+/* Keep last */
+#include "BKE_undo_system.h"
+
+void ED_undosys_type_init(void)
+{
+       /* Edit Modes */
+       BKE_undosys_type_append(ED_armature_undosys_type);
+       BKE_undosys_type_append(ED_curve_undosys_type);
+       BKE_undosys_type_append(ED_font_undosys_type);
+       BKE_undosys_type_append(ED_lattice_undosys_type);
+       BKE_undosys_type_append(ED_mball_undosys_type);
+       BKE_undosys_type_append(ED_mesh_undosys_type);
+
+       /* Paint Modes */
+       BKE_UNDOSYS_TYPE_IMAGE = BKE_undosys_type_append(ED_image_undosys_type);
+
+       BKE_UNDOSYS_TYPE_SCULPT = BKE_undosys_type_append(ED_sculpt_undosys_type);
+
+       BKE_UNDOSYS_TYPE_PARTICLE = BKE_undosys_type_append(ED_particle_undosys_type);
+
+       BKE_UNDOSYS_TYPE_PAINTCURVE = BKE_undosys_type_append(ED_paintcurve_undosys_type);
+
+       /* Text editor */
+       BKE_undosys_type_append(ED_text_undosys_type);
+
+       /* Keep global undo last (as a fallback). */
+       BKE_UNDOSYS_TYPE_MEMFILE = BKE_undosys_type_append(ED_memfile_undosys_type);
+}
+
+void ED_undosys_type_free(void)
+{
+       BKE_undosys_type_free_all();
+}
index 0f65033095155ae1e93dae242838ab37fdb1adf4..6eda3900e91eaf8ca4e8124a41bd8ef4ccf0ead7 100644 (file)
 
 /* internal exports only */
 
-/* editmode_undo.c */
-void        undo_editmode_name(struct bContext *C, const char *undoname);
-bool        undo_editmode_is_valid(const char *undoname);
-const char *undo_editmode_get_name(struct bContext *C, int nr, bool *r_active);
-void       *undo_editmode_get_prev(struct Object *ob);
-void        undo_editmode_step(struct bContext *C, int step);
-void        undo_editmode_number(struct bContext *C, int nr);
+struct UndoType;
+struct Main;
+struct Scene;
 
-#endif /* __UTIL_INTERN_H__ */
+/* memfile_undo.c */
+void ED_memfile_undosys_type(struct UndoType *ut);
 
+#endif /* __UTIL_INTERN_H__ */
index 687a883f7928e3a2558aeb6c3c63977e763691fe..8d8bc4d7d764fcb7312cd54ef8aa7d1720971bb0 100644 (file)
@@ -58,6 +58,7 @@ struct ReportList;
 struct Report;
 struct uiLayout;
 struct Stereo3dFormat;
+struct UndoStep;
 
 #define OP_MAX_TYPENAME 64
 #define KMAP_MAX_NAME   64
@@ -154,6 +155,8 @@ typedef struct wmWindowManager {
        ListBase timers;                  /* active timers */
        struct wmTimer *autosavetimer;    /* timer for auto save */
 
+       struct UndoStack *undo_stack;     /* all undo history (runtime only). */
+
        char is_interface_locked;               /* indicates whether interface is locked for user interaction */
        char par[7];
 } wmWindowManager;
@@ -313,7 +316,6 @@ typedef struct wmKeyMap {
        short kmi_id;     /* last kmi id */
 
        /* runtime */
-       /** Verify if enabled in the current context, use #WM_keymap_poll instead of direct calls. */
        int (*poll)(struct bContext *);
        /** For modal, #EnumPropertyItem for now. */
        const void *modal_items;
diff --git a/source/blender/windowmanager/WM_undo.h b/source/blender/windowmanager/WM_undo.h
new file mode 100644 (file)
index 0000000..e69de29
index 9e877a83b3e3f0e25e207bc5c41cc34c59893551..8be7555b34a3025abb13239c0098312a5202a482 100644 (file)
@@ -61,6 +61,7 @@
 #include "wm.h"
 
 #include "ED_screen.h"
+#include "BKE_undo_system.h"
 
 #ifdef WITH_PYTHON
 #include "BPY_extern.h"
@@ -485,7 +486,12 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
        WM_drag_free_list(&wm->drags);
        
        wm_reports_free(wm);
-       
+
+       if (wm->undo_stack) {
+               BKE_undosys_stack_destroy(wm->undo_stack);
+               wm->undo_stack = NULL;
+       }
+
        if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL);
 }
 
index e0b57effbe9a5ca7c030ad4cc19a5f5ee9e94597..1d96d291dc45ffebe32b00694a2e60a6f978d780 100644 (file)
 #include "BKE_sound.h"
 #include "BKE_scene.h"
 #include "BKE_screen.h"
+#include "BKE_undo_system.h"
 
 #include "BLO_readfile.h"
 #include "BLO_writefile.h"
+#include "BLO_undofile.h"  /* to save from an undo memfile */
 
 #include "RNA_access.h"
 #include "RNA_define.h"
@@ -522,9 +524,13 @@ static void wm_file_read_post(bContext *C, const bool is_startup_file, const boo
        }
 
        if (!G.background) {
-//             undo_editmode_clear();
-               BKE_undo_reset();
-               BKE_undo_write(C, "original");  /* save current state */
+               if (wm->undo_stack == NULL) {
+                       wm->undo_stack = BKE_undosys_stack_create();
+               }
+               else {
+                       BKE_undosys_stack_clear(wm->undo_stack);
+               }
+               BKE_undosys_stack_init_from_main(wm->undo_stack, CTX_data_main(C));
        }
 }
 
@@ -596,10 +602,6 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
 
                success = true;
        }
-#if 0
-       else if (retval == BKE_READ_EXOTIC_OK_OTHER)
-               BKE_undo_write(C, "Import file");
-#endif
        else if (retval == BKE_READ_EXOTIC_FAIL_OPEN) {
                BKE_reportf(reports, RPT_ERROR, "Cannot read file '%s': %s", filepath,
                            errno ? strerror(errno) : TIP_("unable to open the file"));
@@ -1271,7 +1273,10 @@ void wm_autosave_timer(const bContext *C, wmWindowManager *wm, wmTimer *UNUSED(w
 
        if (U.uiflag & USER_GLOBALUNDO) {
                /* fast save of last undobuffer, now with UI */
-               BKE_undo_save_file(filepath);
+               struct MemFile *memfile = ED_undosys_stack_memfile_get_active(wm->undo_stack);
+               if (memfile) {
+                       BLO_memfile_write_file(memfile, filepath);
+               }
        }
        else {
                /*  save as regular blend file */
index 2743216ee07cfc33a50a94335db5258a1d29c39c..b9ac05e0c57582180e5057b197c329fb89c8fb00 100644 (file)
@@ -55,6 +55,7 @@
 #include "BLI_utildefines.h"
 
 #include "BLO_writefile.h"
+#include "BLO_undofile.h"
 
 #include "BKE_blender.h"
 #include "BKE_blender_undo.h"
@@ -149,11 +150,6 @@ static void wm_free_reports(bContext *C)
        BKE_reports_clear(reports);
 }
 
-static void wm_undo_kill_callback(bContext *C)
-{
-       WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C));
-}
-
 bool wm_start_with_console = false; /* used in creator.c */
 
 /* only called once, for startup */
@@ -172,7 +168,7 @@ void WM_init(bContext *C, int argc, const char **argv)
        WM_menutype_init();
        WM_uilisttype_init();
 
-       BKE_undo_callback_wm_kill_jobs_set(wm_undo_kill_callback);
+       ED_undosys_type_init();
 
        BKE_library_callback_free_window_manager_set(wm_close_and_free);   /* library.c */
        BKE_library_callback_free_notifier_reference_set(WM_main_remove_notifier_reference);   /* library.c */
@@ -478,7 +474,8 @@ void WM_exit_ext(bContext *C, const bool do_python)
                wmWindow *win;
 
                if (!G.background) {
-                       if ((U.uiflag2 & USER_KEEP_SESSION) || BKE_undo_is_valid(NULL)) {
+                       struct MemFile *undo_memfile = wm->undo_stack ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : NULL;
+                       if ((U.uiflag2 & USER_KEEP_SESSION) || (undo_memfile != NULL)) {
                                /* save the undo state as quit.blend */
                                char filename[FILE_MAX];
                                bool has_edited;
@@ -489,7 +486,7 @@ void WM_exit_ext(bContext *C, const bool do_python)
                                has_edited = ED_editors_flush_edits(C, false);
 
                                if ((has_edited && BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL)) ||
-                                   BKE_undo_save_file(filename))
+                                   (undo_memfile && BLO_memfile_write_file(undo_memfile, filename)))
                                {
                                        printf("Saved session recovery to '%s'\n", filename);
                                }
@@ -512,11 +509,13 @@ void WM_exit_ext(bContext *C, const bool do_python)
        wm_dropbox_free();
        WM_menutype_free();
        WM_uilisttype_free();
-       
+
        /* all non-screen and non-space stuff editors did, like editmode */
        if (C)
                ED_editors_exit(C);
 
+       ED_undosys_type_free();
+
 //     XXX     
 //     BIF_GlobalReebFree();
 //     BIF_freeRetarget();
@@ -594,8 +593,6 @@ void WM_exit_ext(bContext *C, const bool do_python)
                GPU_exit();
        }
 
-       BKE_undo_reset();
-       
        ED_file_exit(); /* for fsmenu */
 
        UI_exit();
index 81e6178c5025fff0daac0afd93503a8c2393b1ca..feb108da289c43c586562acf8ff56c07cddd3042 100644 (file)
@@ -64,6 +64,7 @@
 #include "BKE_main.h"
 #include "BKE_report.h"
 
+
 /* for passing information between creator and gameengine */
 #ifdef WITH_GAMEENGINE
 #  include "BL_System.h"
 
 #include "creator_intern.h"  /* own include */
 
+// #define USE_WRITE_CRASH_BLEND
+#ifdef USE_WRITE_CRASH_BLEND
+#  include "BKE_undo_system.h"
+#  include "BLO_undofile.h"
+#endif
+
 /* set breakpoints here when running in debug mode, useful to catch floating point errors */
 #if defined(__linux__) || defined(_WIN32) || defined(OSX_SSE_FPE)
 static void sig_handle_fpe(int UNUSED(sig))
@@ -110,29 +117,32 @@ static void sig_handle_crash_backtrace(FILE *fp)
 
 static void sig_handle_crash(int signum)
 {
+       wmWindowManager *wm = G.main->wm.first;
 
-#if 0
-       {
-               char fname[FILE_MAX];
+#ifdef USE_WRITE_CRASH_BLEND
+       if (wm->undo_stack) {
+               struct MemFile *memfile = BKE_undosys_stack_memfile_get_active(wm->undo_stack);
+               if (memfile) {
+                       char fname[FILE_MAX];
 
-               if (!G.main->name[0]) {
-                       BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
-               }
-               else {
-                       BLI_strncpy(fname, G.main->name, sizeof(fname));
-                       BLI_replace_extension(fname, sizeof(fname), ".crash.blend");
-               }
+                       if (!G.main->name[0]) {
+                               BLI_make_file_string("/", fname, BKE_tempdir_base(), "crash.blend");
+                       }
+                       else {
+                               BLI_strncpy(fname, G.main->name, sizeof(fname));
+                               BLI_replace_extension(fname, sizeof(fname), ".crash.blend");
+                       }
 
-               printf("Writing: %s\n", fname);
-               fflush(stdout);
+                       printf("Writing: %s\n", fname);
+                       fflush(stdout);
 
-               BKE_undo_save_file(fname);
+                       BLO_memfile_write_file(memfile, fname);
+               }
        }
 #endif
 
        FILE *fp;
        char header[512];
-       wmWindowManager *wm = G.main->wm.first;
 
        char fname[FILE_MAX];
 
@@ -338,4 +348,4 @@ void main_signal_setup_fpe(void)
 #endif
 }
 
-#endif  /* WITH_PYTHON_MODULE */
\ No newline at end of file
+#endif  /* WITH_PYTHON_MODULE */