code cleanup: favor braces when blocks have mixed brace use.
[blender.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 #include "DNA_screen_types.h"
40
41 #include "BLI_blenlib.h"
42 #include "BLI_dynstr.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_blender.h"
46 #include "BKE_context.h"
47 #include "BKE_depsgraph.h"
48 #include "BKE_global.h"
49
50 #include "ED_util.h"
51 #include "ED_mesh.h"
52
53 #include "UI_interface.h"
54 #include "UI_resources.h"
55
56 #include "util_intern.h"
57
58 /* ***************** generic editmode undo system ********************* */
59 /*
60  * Add this in your local code:
61  *
62  * void undo_editmode_push(bContext *C, const char *name, 
63  *      void * (*getdata)(bContext *C),     // use context to retrieve current editdata
64  *      void (*freedata)(void *),           // pointer to function freeing data
65  *      void (*to_editmode)(void *, void *),        // data to editmode conversion
66  *      void * (*from_editmode)(void *))      // editmode to data conversion
67  *      int  (*validate_undo)(void *, void *))      // check if undo data is still valid
68  *
69  *
70  * Further exported for UI is:
71  *
72  * void undo_editmode_step(bContext *C, int step);       // undo and redo
73  * void undo_editmode_clear(void)                               // free & clear all data
74  * void undo_editmode_menu(void)                                // history menu
75  */
76
77 /* ********************************************************************* */
78
79 /* ****** XXX ***** */
80 static void error(const char *UNUSED(arg)) {}
81 /* ****** XXX ***** */
82
83 typedef struct UndoElem {
84         struct UndoElem *next, *prev;
85         ID id;          // copy of editmode object ID
86         Object *ob;     // pointer to edited object
87         int type;       // type of edited object
88         void *undodata;
89         uintptr_t undosize;
90         char name[BKE_UNDO_STR_MAX];
91         void * (*getdata)(bContext * C);
92         void (*freedata)(void *);
93         void (*to_editmode)(void *, void *, void *);
94         void * (*from_editmode)(void *, void *);
95         int (*validate_undo)(void *, void *);
96 } UndoElem;
97
98 static ListBase undobase = {NULL, NULL};
99 static UndoElem *curundo = NULL;
100
101
102 /* ********************* xtern api calls ************* */
103
104 static void undo_restore(UndoElem *undo, void *editdata, void *obdata)
105 {
106         if (undo) {
107                 undo->to_editmode(undo->undodata, editdata, obdata);
108         }
109 }
110
111 /* name can be a dynamic string */
112 void undo_editmode_push(bContext *C, const char *name, 
113                         void * (*getdata)(bContext * C),
114                         void (*freedata)(void *),
115                         void (*to_editmode)(void *, void *, void *),
116                         void *(*from_editmode)(void *, void *),
117                         int (*validate_undo)(void *, void *))
118 {
119         UndoElem *uel;
120         Object *obedit = CTX_data_edit_object(C);
121         void *editdata;
122         int nr;
123         uintptr_t memused, totmem, maxmem;
124
125         /* at first here was code to prevent an "original" key to be inserted twice
126          * this was giving conflicts for example when mesh changed due to keys or apply */
127         
128         /* remove all undos after (also when curundo == NULL) */
129         while (undobase.last != curundo) {
130                 uel = undobase.last;
131                 uel->freedata(uel->undodata);
132                 BLI_freelinkN(&undobase, uel);
133         }
134         
135         /* make new */
136         curundo = uel = MEM_callocN(sizeof(UndoElem), "undo editmode");
137         BLI_strncpy(uel->name, name, sizeof(uel->name));
138         BLI_addtail(&undobase, uel);
139         
140         uel->getdata = getdata;
141         uel->freedata = freedata;
142         uel->to_editmode = to_editmode;
143         uel->from_editmode = from_editmode;
144         uel->validate_undo = validate_undo;
145         
146         /* limit amount to the maximum amount*/
147         nr = 0;
148         uel = undobase.last;
149         while (uel) {
150                 nr++;
151                 if (nr == U.undosteps) break;
152                 uel = uel->prev;
153         }
154         if (uel) {
155                 while (undobase.first != uel) {
156                         UndoElem *first = undobase.first;
157                         first->freedata(first->undodata);
158                         BLI_freelinkN(&undobase, first);
159                 }
160         }
161
162         /* copy  */
163         memused = MEM_get_memory_in_use();
164         editdata = getdata(C);
165         curundo->undodata = curundo->from_editmode(editdata, obedit->data);
166         curundo->undosize = MEM_get_memory_in_use() - memused;
167         curundo->ob = obedit;
168         curundo->id = obedit->id;
169         curundo->type = obedit->type;
170
171         if (U.undomemory != 0) {
172                 /* limit to maximum memory (afterwards, we can't know in advance) */
173                 totmem = 0;
174                 maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
175
176                 uel = undobase.last;
177                 while (uel && uel->prev) {
178                         totmem += uel->undosize;
179                         if (totmem > maxmem) break;
180                         uel = uel->prev;
181                 }
182
183                 if (uel) {
184                         if (uel->prev && uel->prev->prev)
185                                 uel = uel->prev;
186
187                         while (undobase.first != uel) {
188                                 UndoElem *first = undobase.first;
189                                 first->freedata(first->undodata);
190                                 BLI_freelinkN(&undobase, first);
191                         }
192                 }
193         }
194 }
195
196 /* helper to remove clean other objects from undo stack */
197 static void undo_clean_stack(bContext *C)
198 {
199         UndoElem *uel, *next;
200         Object *obedit = CTX_data_edit_object(C);
201         
202         /* global undo changes pointers, so we also allow identical names */
203         /* side effect: when deleting/renaming object and start editing new one with same name */
204         
205         uel = undobase.first;
206         while (uel) {
207                 void *editdata = uel->getdata(C);
208                 int is_valid = FALSE;
209                 next = uel->next;
210                 
211                 /* for when objects are converted, renamed, or global undo changes pointers... */
212                 if (uel->type == obedit->type) {
213                         if (strcmp(uel->id.name, obedit->id.name) == 0) {
214                                 if (uel->validate_undo == NULL)
215                                         is_valid = TRUE;
216                                 else if (uel->validate_undo(uel->undodata, editdata))
217                                         is_valid = TRUE;
218                         }
219                 }
220                 if (is_valid)
221                         uel->ob = obedit;
222                 else {
223                         if (uel == curundo)
224                                 curundo = NULL;
225
226                         uel->freedata(uel->undodata);
227                         BLI_freelinkN(&undobase, uel);
228                 }
229                 
230                 uel = next;
231         }
232         
233         if (curundo == NULL) curundo = undobase.last;
234 }
235
236 /* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
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(curundo, curundo->getdata(C), obedit->data);
246         }
247         else if (step == 1) {
248                 
249                 if (curundo == NULL || curundo->prev == NULL) {
250                         error("No more steps to undo");
251                 }
252                 else {
253                         if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
254                         curundo = curundo->prev;
255                         undo_restore(curundo, curundo->getdata(C), obedit->data);
256                 }
257         }
258         else {
259                 /* curundo has to remain current situation! */
260                 
261                 if (curundo == NULL || curundo->next == NULL) {
262                         error("No more steps to redo");
263                 }
264                 else {
265                         undo_restore(curundo->next, curundo->getdata(C), obedit->data);
266                         curundo = curundo->next;
267                         if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
268                 }
269         }
270         
271         /* special case for editmesh, mode must be copied back to the scene */
272         if (obedit->type == OB_MESH) {
273                 EDBM_selectmode_to_scene(C);
274         }
275
276         DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
277
278         /* XXX notifiers */
279 }
280
281 void undo_editmode_clear(void)
282 {
283         UndoElem *uel;
284         
285         uel = undobase.first;
286         while (uel) {
287                 uel->freedata(uel->undodata);
288                 uel = uel->next;
289         }
290         BLI_freelistN(&undobase);
291         curundo = NULL;
292 }
293
294 /* based on index nr it does a restore */
295 void undo_editmode_number(bContext *C, int nr)
296 {
297         UndoElem *uel;
298         int a = 1;
299         
300         for (uel = undobase.first; uel; uel = uel->next, a++) {
301                 if (a == nr) break;
302         }
303         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 = undobase.last; uel; uel = uel->prev) {
312                 if (strcmp(undoname, uel->name) == 0)
313                         break;
314         }
315         if (uel && uel->prev) {
316                 curundo = uel->prev;
317                 undo_editmode_step(C, 0);
318         }
319 }
320
321 /* undoname optionally, if NULL it just checks for existing undo steps */
322 int undo_editmode_valid(const char *undoname)
323 {
324         if (undoname) {
325                 UndoElem *uel;
326                 
327                 for (uel = undobase.last; uel; uel = uel->prev) {
328                         if (strcmp(undoname, uel->name) == 0)
329                                 break;
330                 }
331                 return uel != NULL;
332         }
333         return undobase.last != undobase.first;
334 }
335
336
337 /* get name of undo item, return null if no item with this index */
338 /* if active pointer, set it to 1 if true */
339 const char *undo_editmode_get_name(bContext *C, int nr, int *active)
340 {
341         UndoElem *uel;
342         
343         /* prevent wrong numbers to be returned */
344         undo_clean_stack(C);
345         
346         if (active) *active = 0;
347         
348         uel = BLI_findlink(&undobase, nr);
349         if (uel) {
350                 if (active && uel == curundo)
351                         *active = 1;
352                 return uel->name;
353         }
354         return NULL;
355 }
356
357
358 void *undo_editmode_get_prev(Object *ob)
359 {
360         UndoElem *ue = undobase.last;
361         if (ue && ue->prev && ue->prev->ob == ob) return ue->prev->undodata;
362         return NULL;
363 }