2dcfe949f3f27f54f4fd30b0141a8effe24b7196
[blender-staging.git] / source / blender / editors / util / editmode_undo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2004 Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/util/editmode_undo.c
29  *  \ingroup edutil
30  */
31
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "DNA_object_types.h"
39
40 #include "BLI_blenlib.h"
41 #include "BLI_utildefines.h"
42
43 #include "BKE_blender_undo.h"
44 #include "BKE_context.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_global.h"
47
48 #include "ED_util.h"
49 #include "ED_mesh.h"
50
51 #include "util_intern.h"
52
53 /* ****** XXX ***** */
54 static void error(const char *UNUSED(arg)) {}
55 /* ****** XXX ***** */
56
57 typedef struct UndoElem {
58         struct UndoElem *next, *prev;
59         /** copy of edit-mode object ID */
60         ID id;
61         /** pointer to edited object */
62         Object *ob;
63         /** type of edited object */
64         int type;
65         void *undodata;
66         uintptr_t undosize;
67         char name[BKE_UNDO_STR_MAX];
68
69         /** Use context to retrieve current edit-data. */
70         void * (*getdata)(bContext * C);
71         /** Pointer to function freeing data. */
72         void (*freedata)(void *);
73          /** Data to edit-mode conversion. */
74         void (*to_editmode)(void *, void *, void *);
75         /** Edit-mode to data conversion. */
76         void * (*from_editmode)(void *, void *);
77         /** Check if undo data is still valid. */
78         int (*validate_undo)(void *, void *);
79 } UndoElem;
80
81 static ListBase g_undobase = {NULL, NULL};
82 static UndoElem *g_curundo = NULL;
83
84 static void undo_restore(UndoElem *undo, void *editdata, void *obdata)
85 {
86         if (undo) {
87                 undo->to_editmode(undo->undodata, editdata, obdata);
88         }
89 }
90
91 /**
92  * name can be a dynamic string
93  * See #UndoElem for callbacks docs.
94  * */
95 void undo_editmode_push(
96         bContext *C, const char *name,
97         void * (*getdata)(bContext * C),
98         void (*freedata)(void *),
99         void (*to_editmode)(void *, void *, void *),
100         void *(*from_editmode)(void *, void *),
101         int (*validate_undo)(void *, void *))
102 {
103         UndoElem *uel;
104         Object *obedit = CTX_data_edit_object(C);
105         void *editdata;
106         int nr;
107         uintptr_t mem_used, mem_total, mem_max;
108
109         /* at first here was code to prevent an "original" key to be inserted twice
110          * this was giving conflicts for example when mesh changed due to keys or apply */
111         
112         /* remove all undos after (also when g_curundo == NULL) */
113         while (g_undobase.last != g_curundo) {
114                 uel = g_undobase.last;
115                 uel->freedata(uel->undodata);
116                 BLI_freelinkN(&g_undobase, uel);
117         }
118         
119         /* make new */
120         g_curundo = uel = MEM_callocN(sizeof(UndoElem), "undo editmode");
121         BLI_strncpy(uel->name, name, sizeof(uel->name));
122         BLI_addtail(&g_undobase, uel);
123         
124         uel->getdata = getdata;
125         uel->freedata = freedata;
126         uel->to_editmode = to_editmode;
127         uel->from_editmode = from_editmode;
128         uel->validate_undo = validate_undo;
129         
130         /* limit amount to the maximum amount*/
131         nr = 0;
132         uel = g_undobase.last;
133         while (uel) {
134                 nr++;
135                 if (nr == U.undosteps) {
136                         break;
137                 }
138                 uel = uel->prev;
139         }
140         if (uel) {
141                 while (g_undobase.first != uel) {
142                         UndoElem *first = g_undobase.first;
143                         first->freedata(first->undodata);
144                         BLI_freelinkN(&g_undobase, first);
145                 }
146         }
147
148         /* copy  */
149         mem_used = MEM_get_memory_in_use();
150         editdata = getdata(C);
151         g_curundo->undodata = g_curundo->from_editmode(editdata, obedit->data);
152         g_curundo->undosize = MEM_get_memory_in_use() - mem_used;
153         g_curundo->ob = obedit;
154         g_curundo->id = obedit->id;
155         g_curundo->type = obedit->type;
156
157         if (U.undomemory != 0) {
158                 /* limit to maximum memory (afterwards, we can't know in advance) */
159                 mem_total = 0;
160                 mem_max = ((uintptr_t)U.undomemory) * 1024 * 1024;
161
162                 uel = g_undobase.last;
163                 while (uel && uel->prev) {
164                         mem_total += uel->undosize;
165                         if (mem_total > mem_max) {
166                                 break;
167                         }
168                         uel = uel->prev;
169                 }
170
171                 if (uel) {
172                         if (uel->prev && uel->prev->prev) {
173                                 uel = uel->prev;
174                         }
175                         while (g_undobase.first != uel) {
176                                 UndoElem *first = g_undobase.first;
177                                 first->freedata(first->undodata);
178                                 BLI_freelinkN(&g_undobase, first);
179                         }
180                 }
181         }
182 }
183
184 /** \} */
185
186
187 /* helper to remove clean other objects from undo stack */
188 static void undo_clean_stack(bContext *C)
189 {
190         UndoElem *uel;
191         Object *obedit = CTX_data_edit_object(C);
192
193         /* global undo changes pointers, so we also allow identical names */
194         /* side effect: when deleting/renaming object and start editing new one with same name */
195
196         uel = g_undobase.first;
197         while (uel) {
198                 void *editdata = uel->getdata(C);
199                 bool is_valid = false;
200                 UndoElem *uel_next = uel->next;
201
202                 /* for when objects are converted, renamed, or global undo changes pointers... */
203                 if (uel->type == obedit->type) {
204                         if (STREQ(uel->id.name, obedit->id.name)) {
205                                 if (uel->validate_undo == NULL) {
206                                         is_valid = true;
207                                 }
208                                 else if (uel->validate_undo(uel->undodata, editdata)) {
209                                         is_valid = true;
210                                 }
211                         }
212                 }
213                 if (is_valid) {
214                         uel->ob = obedit;
215                 }
216                 else {
217                         if (uel == g_curundo) {
218                                 g_curundo = NULL;
219                         }
220
221                         uel->freedata(uel->undodata);
222                         BLI_freelinkN(&g_undobase, uel);
223                 }
224
225                 uel = uel_next;
226         }
227
228         if (g_curundo == NULL) {
229                 g_curundo = g_undobase.last;
230         }
231 }
232
233 /**
234  * 1 = an undo, -1 is a redo.
235  * we have to make sure 'g_curundo' remains at current situation
236  */
237 void undo_editmode_step(bContext *C, int step)
238 {
239         Object *obedit = CTX_data_edit_object(C);
240
241         /* prevent undo to happen on wrong object, stack can be a mix */
242         undo_clean_stack(C);
243
244         if (step == 0) {
245                 undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
246         }
247         else if (step == 1) {
248                 if (g_curundo == NULL || g_curundo->prev == NULL) {
249                         error("No more steps to undo");
250                 }
251                 else {
252                         if (G.debug & G_DEBUG) printf("undo %s\n", g_curundo->name);
253                         g_curundo = g_curundo->prev;
254                         undo_restore(g_curundo, g_curundo->getdata(C), obedit->data);
255                 }
256         }
257         else {
258                 /* g_curundo has to remain current situation! */
259                 if (g_curundo == NULL || g_curundo->next == NULL) {
260                         error("No more steps to redo");
261                 }
262                 else {
263                         undo_restore(g_curundo->next, g_curundo->getdata(C), obedit->data);
264                         g_curundo = g_curundo->next;
265                         if (G.debug & G_DEBUG) printf("redo %s\n", g_curundo->name);
266                 }
267         }
268
269         /* special case for editmesh, mode must be copied back to the scene */
270         if (obedit->type == OB_MESH) {
271                 EDBM_selectmode_to_scene(C);
272         }
273
274         DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
275
276         /* XXX notifiers */
277 }
278
279 void undo_editmode_clear(void)
280 {
281         UndoElem *uel;
282
283         uel = g_undobase.first;
284         while (uel) {
285                 uel->freedata(uel->undodata);
286                 uel = uel->next;
287         }
288         BLI_freelistN(&g_undobase);
289         g_curundo = NULL;
290 }
291
292 /* based on index nr it does a restore */
293 void undo_editmode_number(bContext *C, int nr)
294 {
295         UndoElem *uel;
296         int a = 1;
297
298         for (uel = g_undobase.first; uel; uel = uel->next, a++) {
299                 if (a == nr) {
300                         break;
301                 }
302         }
303         g_curundo = uel;
304         undo_editmode_step(C, 0);
305 }
306
307 void undo_editmode_name(bContext *C, const char *undoname)
308 {
309         UndoElem *uel;
310
311         for (uel = g_undobase.last; uel; uel = uel->prev) {
312                 if (STREQ(undoname, uel->name)) {
313                         break;
314                 }
315         }
316         if (uel && uel->prev) {
317                 g_curundo = uel->prev;
318                 undo_editmode_step(C, 0);
319         }
320 }
321
322 /**
323  * \a undoname is optional, when NULL it just checks for existing undo steps
324  */
325 bool undo_editmode_is_valid(const char *undoname)
326 {
327         if (undoname) {
328                 UndoElem *uel;
329
330                 for (uel = g_undobase.last; uel; uel = uel->prev) {
331                         if (STREQ(undoname, uel->name)) {
332                                 break;
333                         }
334                 }
335                 return uel != NULL;
336         }
337         return g_undobase.last != g_undobase.first;
338 }
339
340
341 /**
342  * Get name of undo item, return null if no item with this index.
343  *
344  * if active pointer, set it to 1 if true
345  */
346 const char *undo_editmode_get_name(bContext *C, int nr, bool *r_active)
347 {
348         UndoElem *uel;
349
350         /* prevent wrong numbers to be returned */
351         undo_clean_stack(C);
352
353         if (r_active) {
354                 *r_active = false;
355         }
356
357         uel = BLI_findlink(&g_undobase, nr);
358         if (uel) {
359                 if (r_active && (uel == g_curundo)) {
360                         *r_active = true;
361                 }
362                 return uel->name;
363         }
364         return NULL;
365 }
366
367
368 void *undo_editmode_get_prev(Object *ob)
369 {
370         UndoElem *ue = g_undobase.last;
371         if (ue && ue->prev && ue->prev->ob == ob) {
372                 return ue->prev->undodata;
373         }
374         return NULL;
375 }