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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2016-04-24 15:42:41 +0300
committerCampbell Barton <ideasman42@gmail.com>2016-04-25 12:27:45 +0300
commitbeaa57d2699ae945c06a895d41ec5ddfeabc373a (patch)
tree205db121401f06eb1d51a98cd2480b46ae52c582 /source/blender/blenkernel/intern/blender_undo.c
parent935998312c44300385588c466ef31fa9e85d385f (diff)
Refactor BKE_blender into separate headers
- BKE_blender_version.h (only version defines & versionstr). - BKE_blender_copybuffer.h (currently only used for view3d copy/paste). - BKE_blender_undo.h (global undo functions). - BKE_blendfile.h (high level blend file read/write API).
Diffstat (limited to 'source/blender/blenkernel/intern/blender_undo.c')
-rw-r--r--source/blender/blenkernel/intern/blender_undo.c393
1 files changed, 393 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/blender_undo.c b/source/blender/blenkernel/intern/blender_undo.c
new file mode 100644
index 00000000000..ca0a1b91cea
--- /dev/null
+++ b/source/blender/blenkernel/intern/blender_undo.c
@@ -0,0 +1,393 @@
+/*
+ * ***** 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/blenkernel/intern/blender_undo.c
+ * \ingroup bke
+ *
+ * Blend file undo (known as 'Global Undo').
+ * DNA level diffing for undo.
+ */
+
+#ifndef _WIN32
+# include <unistd.h> // for read close
+#else
+# include <io.h> // for open close read
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h> /* for open */
+#include <errno.h>
+
+#include "MEM_guardedalloc.h"
+
+#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"
+
+#include "WM_api.h" // XXXXX BAD, very BAD dependency (bad level call) - remove asap, elubie
+
+
+/* -------------------------------------------------------------------- */
+
+/** \name Global Undo
+ * \{ */
+
+#define UNDO_DISK 0
+
+typedef struct UndoElem {
+ struct UndoElem *next, *prev;
+ char str[FILE_MAX];
+ char name[BKE_UNDO_STR_MAX];
+ MemFile memfile;
+ uintptr_t undosize;
+} UndoElem;
+
+static ListBase undobase = {NULL, NULL};
+static UndoElem *curundo = NULL;
+
+
+static int read_undosave(bContext *C, UndoElem *uel)
+{
+ char mainstr[sizeof(G.main->name)];
+ int success = 0, fileflags;
+
+ /* This is needed so undoing/redoing doesn't crash with threaded previews going */
+ WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(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->str, NULL) != BKE_BLENDFILE_READ_FAIL);
+ else
+ success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL);
+
+ /* restore */
+ BLI_strncpy(G.main->name, mainstr, sizeof(G.main->name)); /* restore */
+ G.fileflags = fileflags;
+
+ if (success) {
+ /* important not to update time here, else non keyed tranforms are lost */
+ DAG_on_visible_update(G.main, false);
+ }
+
+ return success;
+}
+
+/* name can be a dynamic string */
+void BKE_undo_write(bContext *C, const char *name)
+{
+ uintptr_t maxmem, totmem, memused;
+ 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);
+ }
+ }
+
+
+ /* disk save version */
+ if (UNDO_DISK) {
+ static int counter = 0;
+ char filepath[FILE_MAX];
+ char numstr[32];
+ int fileflags = G.fileflags & ~(G_FILE_HISTORY); /* don't do file history on undo */
+
+ /* calculate current filepath */
+ counter++;
+ counter = counter % U.undosteps;
+
+ BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
+ BLI_make_file_string("/", filepath, BKE_tempdir_session(), numstr);
+
+ /* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filepath, fileflags, NULL, NULL);
+
+ BLI_strncpy(curundo->str, filepath, sizeof(curundo->str));
+ }
+ else {
+ MemFile *prevfile = NULL;
+
+ if (curundo->prev) prevfile = &(curundo->prev->memfile);
+
+ memused = MEM_get_memory_in_use();
+ /* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags);
+ curundo->undosize = MEM_get_memory_in_use() - memused;
+ }
+
+ if (U.undomemory != 0) {
+ /* limit to maximum memory (afterwards, we can't know in advance) */
+ totmem = 0;
+ maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
+
+ /* keep at least two (original + other) */
+ uel = undobase.last;
+ while (uel && uel->prev) {
+ totmem += uel->undosize;
+ 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;
+ }
+
+ 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);
+
+ if (r_active) *r_active = false;
+
+ if (uel) {
+ if (r_active && (uel == curundo)) {
+ *r_active = true;
+ }
+ return uel->name;
+ }
+ return 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);
+
+ if (bfd) {
+ mainp = bfd->main;
+ if (r_scene) {
+ *r_scene = bfd->curscene;
+ }
+
+ MEM_freeN(bfd);
+ }
+
+ return mainp;
+}
+
+/** \} */