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