2.5
[blender-staging.git] / source / blender / editors / util / editmode_undo.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2004 Blender Foundation
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <math.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_mesh_types.h"
38 #include "DNA_meshdata_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_screen_types.h"
41 #include "DNA_scene_types.h"
42 #include "DNA_userdef_types.h"
43
44 #include "BKE_context.h"
45 #include "BKE_depsgraph.h"
46 #include "BKE_global.h"
47 #include "BKE_object.h"
48
49 #include "BLI_blenlib.h"
50 #include "BLI_dynstr.h"
51
52 #include "BKE_utildefines.h"
53
54 #include "ED_util.h"
55
56 #include "UI_interface.h"
57 #include "UI_resources.h"
58
59 /* ***************** generic editmode undo system ********************* */
60 /*
61
62 Add this in your local code:
63
64 void undo_editmode_push(bContext *C, char *name, 
65                 void * (*getdata)(bContext *C),     // use context to retrieve current editdata
66                 void (*freedata)(void *),                       // pointer to function freeing data
67                 void (*to_editmode)(void *, void *),        // data to editmode conversion
68                 void * (*from_editmode)(void *))      // editmode to data conversion
69                 int  (*validate_undo)(void *, void *))      // check if undo data is still valid
70
71
72 Further exported for UI is:
73
74 void undo_editmode_step(bContext *C, int step);  // undo and redo
75 void undo_editmode_clear(void)                          // free & clear all data
76 void undo_editmode_menu(void)                           // history menu
77
78
79 */
80 /* ********************************************************************* */
81
82 /* ****** XXX ***** */
83 void error() {}
84 /* ****** XXX ***** */
85
86
87 #define MAXUNDONAME     64
88 typedef struct UndoElem {
89         struct UndoElem *next, *prev;
90         ID id;                  // copy of editmode object ID
91         Object *ob;             // pointer to edited object
92         int type;               // type of edited object
93         void *undodata;
94         uintptr_t undosize;
95         char name[MAXUNDONAME];
96         void * (*getdata)(bContext *C);
97         void (*freedata)(void *);
98         void (*to_editmode)(void *, void *);
99         void * (*from_editmode)(void *);
100         int (*validate_undo)(void *, void *);
101 } UndoElem;
102
103 static ListBase undobase={NULL, NULL};
104 static UndoElem *curundo= NULL;
105
106
107 /* ********************* xtern api calls ************* */
108
109 static void undo_restore(UndoElem *undo, void *editdata)
110 {
111         if(undo) {
112                 undo->to_editmode(undo->undodata, editdata);    
113         }
114 }
115
116 /* name can be a dynamic string */
117 void undo_editmode_push(bContext *C, char *name, 
118                                                 void * (*getdata)(bContext *C),
119                                                 void (*freedata)(void *), 
120                                                 void (*to_editmode)(void *, void *),  
121                                                 void *(*from_editmode)(void *),
122                                                 int (*validate_undo)(void *, void *))
123 {
124         UndoElem *uel;
125         void *editdata;
126         int nr;
127         uintptr_t memused, totmem, maxmem;
128
129         /* at first here was code to prevent an "original" key to be insterted twice
130            this was giving conflicts for example when mesh changed due to keys or apply */
131         
132         /* remove all undos after (also when curundo==NULL) */
133         while(undobase.last != curundo) {
134                 uel= undobase.last;
135                 uel->freedata(uel->undodata);
136                 BLI_freelinkN(&undobase, uel);
137         }
138         
139         /* make new */
140         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo editmode");
141         strncpy(uel->name, name, MAXUNDONAME-1);
142         BLI_addtail(&undobase, uel);
143         
144         uel->getdata= getdata;
145         uel->freedata= freedata;
146         uel->to_editmode= to_editmode;
147         uel->from_editmode= from_editmode;
148         uel->validate_undo= validate_undo;
149         
150         /* limit amount to the maximum amount*/
151         nr= 0;
152         uel= undobase.last;
153         while(uel) {
154                 nr++;
155                 if(nr==U.undosteps) break;
156                 uel= uel->prev;
157         }
158         if(uel) {
159                 while(undobase.first!=uel) {
160                         UndoElem *first= undobase.first;
161                         first->freedata(first->undodata);
162                         BLI_freelinkN(&undobase, first);
163                 }
164         }
165
166         /* copy  */
167         memused= MEM_get_memory_in_use();
168         editdata= getdata(C);
169         curundo->undodata= curundo->from_editmode(editdata);
170         curundo->undosize= MEM_get_memory_in_use() - memused;
171         curundo->ob= G.obedit;
172         curundo->id= G.obedit->id;
173         curundo->type= G.obedit->type;
174
175         if(U.undomemory != 0) {
176                 /* limit to maximum memory (afterwards, we can't know in advance) */
177                 totmem= 0;
178                 maxmem= ((uintptr_t)U.undomemory)*1024*1024;
179
180                 uel= undobase.last;
181                 while(uel && uel->prev) {
182                         totmem+= uel->undosize;
183                         if(totmem>maxmem) break;
184                         uel= uel->prev;
185                 }
186
187                 if(uel) {
188                         if(uel->prev && uel->prev->prev)
189                                 uel= uel->prev;
190
191                         while(undobase.first!=uel) {
192                                 UndoElem *first= undobase.first;
193                                 first->freedata(first->undodata);
194                                 BLI_freelinkN(&undobase, first);
195                         }
196                 }
197         }
198 }
199
200 /* helper to remove clean other objects from undo stack */
201 static void undo_clean_stack(bContext *C)
202 {
203         UndoElem *uel, *next;
204         int mixed= 0;
205         
206         /* global undo changes pointers, so we also allow identical names */
207         /* side effect: when deleting/renaming object and start editing new one with same name */
208         
209         uel= undobase.first; 
210         while(uel) {
211                 void *editdata= uel->getdata(C);
212                 int isvalid= 0;
213                 next= uel->next;
214                 
215                 /* for when objects are converted, renamed, or global undo changes pointers... */
216                 if(uel->type==G.obedit->type) {
217                         if(strcmp(uel->id.name, G.obedit->id.name)==0) {
218                                 if(uel->validate_undo==NULL)
219                                         isvalid= 1;
220                                 else if(uel->validate_undo(uel->undodata, editdata))
221                                         isvalid= 1;
222                         }
223                 }
224                 if(isvalid) 
225                         uel->ob= G.obedit;
226                 else {
227                         mixed= 1;
228                         uel->freedata(uel->undodata);
229                         BLI_freelinkN(&undobase, uel);
230                 }
231                 
232                 uel= next;
233         }
234         
235         if(mixed) curundo= undobase.last;
236 }
237
238 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
239 void undo_editmode_step(bContext *C, int step)
240 {
241         
242         /* prevent undo to happen on wrong object, stack can be a mix */
243         undo_clean_stack(C);
244         
245         if(step==0) {
246                 undo_restore(curundo, curundo->getdata(C));
247         }
248         else if(step==1) {
249                 
250                 if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo");
251                 else {
252                         if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
253                         curundo= curundo->prev;
254                         undo_restore(curundo, curundo->getdata(C));
255                 }
256         }
257         else {
258                 /* curundo has to remain current situation! */
259                 
260                 if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
261                 else {
262                         undo_restore(curundo->next, curundo->getdata(C));
263                         curundo= curundo->next;
264                         if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
265                 }
266         }
267
268 //      DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
269         /* XXX notifiers */
270 }
271
272 void undo_editmode_clear(void)
273 {
274         UndoElem *uel;
275         
276         uel= undobase.first;
277         while(uel) {
278                 uel->freedata(uel->undodata);
279                 uel= uel->next;
280         }
281         BLI_freelistN(&undobase);
282         curundo= NULL;
283 }
284
285 /* based on index nr it does a restore */
286 static void undo_number(bContext *C, int nr)
287 {
288         UndoElem *uel;
289         int a=1;
290         
291         for(uel= undobase.first; uel; uel= uel->next, a++) {
292                 if(a==nr) break;
293         }
294         curundo= uel;
295         undo_editmode_step(C, 0);
296 }
297
298 /* ************** for interaction with menu/pullown */
299
300 void undo_editmode_menu(bContext *C)
301 {
302         UndoElem *uel;
303         DynStr *ds= BLI_dynstr_new();
304         short event;
305         char *menu;
306
307         undo_clean_stack(C);    // removes other objects from it
308         
309         BLI_dynstr_append(ds, "Editmode Undo History %t");
310         
311         for(uel= undobase.first; uel; uel= uel->next) {
312                 BLI_dynstr_append(ds, "|");
313                 BLI_dynstr_append(ds, uel->name);
314         }
315         
316         menu= BLI_dynstr_get_cstring(ds);
317         BLI_dynstr_free(ds);
318         
319 // XXX  event= pupmenu_col(menu, 20);
320         MEM_freeN(menu);
321         
322         if(event>0) undo_number(C, event);
323 }
324
325 static void do_editmode_undohistorymenu(bContext *C, void *arg, int event)
326 {
327         
328         if(G.obedit==NULL || event<1) return;
329
330         undo_number(C, event-1);
331         
332 }
333
334 uiBlock *editmode_undohistorymenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused)
335 {
336         uiBlock *block;
337         UndoElem *uel;
338         short yco = 20, menuwidth = 120;
339         short item= 1;
340         
341         undo_clean_stack(C);    // removes other objects from it
342
343         block= uiBeginBlock(C, handle->region, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP, UI_HELV);
344         uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
345         
346         for(uel= undobase.first; uel; uel= uel->next, item++) {
347                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
348                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, uel->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, (float)item, "");
349                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
350         }
351         
352         uiBlockSetDirection(block, UI_RIGHT);
353         uiTextBoundsBlock(block, 60);
354         return block;
355 }
356
357 void *undo_editmode_get_prev(Object *ob)
358 {
359         UndoElem *ue= undobase.last;
360         if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata;
361         return NULL;
362 }