05813b72ad0b4349b068dd6e193cdcc7b4322c8a
[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(char *name, 
65                 void (*freedata)(void *),                       // pointer to function freeing data
66                 void (*to_editmode)(void *),        // data to editmode conversion
67                 void * (*from_editmode)(void))      // editmode to data conversion
68                 int  (*validate_undo)(void *))      // check if undo data is still valid
69
70
71 Further exported for UI is:
72
73 void undo_editmode_step(int step);                      // undo and redo
74 void undo_editmode_clear(void)                          // free & clear all data
75 void undo_editmode_menu(void)                           // history menu
76
77
78 */
79 /* ********************************************************************* */
80
81 /* ****** XXX ***** */
82 void error() {}
83 /* ****** XXX ***** */
84
85
86 #define MAXUNDONAME     64
87 typedef struct UndoElem {
88         struct UndoElem *next, *prev;
89         ID id;                  // copy of editmode object ID
90         Object *ob;             // pointer to edited object
91         int type;               // type of edited object
92         void *undodata;
93         uintptr_t undosize;
94         char name[MAXUNDONAME];
95         void (*freedata)(void *);
96         void (*to_editmode)(void *);
97         void * (*from_editmode)(void);
98         int (*validate_undo)(void *);
99 } UndoElem;
100
101 static ListBase undobase={NULL, NULL};
102 static UndoElem *curundo= NULL;
103
104
105 /* ********************* xtern api calls ************* */
106
107 static void undo_restore(UndoElem *undo)
108 {
109         if(undo) {
110                 undo->to_editmode(undo->undodata);      
111         }
112 }
113
114 /* name can be a dynamic string */
115 void undo_editmode_push(char *name, void (*freedata)(void *), 
116                 void (*to_editmode)(void *),  void *(*from_editmode)(void),
117                 int (*validate_undo)(void *))
118 {
119         UndoElem *uel;
120         int nr;
121         uintptr_t memused, totmem, maxmem;
122
123         /* at first here was code to prevent an "original" key to be insterted twice
124            this was giving conflicts for example when mesh changed due to keys or apply */
125         
126         /* remove all undos after (also when curundo==NULL) */
127         while(undobase.last != curundo) {
128                 uel= undobase.last;
129                 uel->freedata(uel->undodata);
130                 BLI_freelinkN(&undobase, uel);
131         }
132         
133         /* make new */
134         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
135         strncpy(uel->name, name, MAXUNDONAME-1);
136         BLI_addtail(&undobase, uel);
137         
138         uel->freedata= freedata;
139         uel->to_editmode= to_editmode;
140         uel->from_editmode= from_editmode;
141         uel->validate_undo= validate_undo;
142         
143         /* limit amount to the maximum amount*/
144         nr= 0;
145         uel= undobase.last;
146         while(uel) {
147                 nr++;
148                 if(nr==U.undosteps) break;
149                 uel= uel->prev;
150         }
151         if(uel) {
152                 while(undobase.first!=uel) {
153                         UndoElem *first= undobase.first;
154                         first->freedata(first->undodata);
155                         BLI_freelinkN(&undobase, first);
156                 }
157         }
158
159         /* copy  */
160         memused= MEM_get_memory_in_use();
161         curundo->undodata= curundo->from_editmode();
162         curundo->undosize= MEM_get_memory_in_use() - memused;
163         curundo->ob= G.obedit;
164         curundo->id= G.obedit->id;
165         curundo->type= G.obedit->type;
166
167         if(U.undomemory != 0) {
168                 /* limit to maximum memory (afterwards, we can't know in advance) */
169                 totmem= 0;
170                 maxmem= ((uintptr_t)U.undomemory)*1024*1024;
171
172                 uel= undobase.last;
173                 while(uel && uel->prev) {
174                         totmem+= uel->undosize;
175                         if(totmem>maxmem) break;
176                         uel= uel->prev;
177                 }
178
179                 if(uel) {
180                         if(uel->prev && uel->prev->prev)
181                                 uel= uel->prev;
182
183                         while(undobase.first!=uel) {
184                                 UndoElem *first= undobase.first;
185                                 first->freedata(first->undodata);
186                                 BLI_freelinkN(&undobase, first);
187                         }
188                 }
189         }
190 }
191
192 /* helper to remove clean other objects from undo stack */
193 static void undo_clean_stack(void)
194 {
195         UndoElem *uel, *next;
196         int mixed= 0;
197         
198         /* global undo changes pointers, so we also allow identical names */
199         /* side effect: when deleting/renaming object and start editing new one with same name */
200         
201         uel= undobase.first; 
202         while(uel) {
203                 next= uel->next;
204                 
205                 /* for when objects are converted, renamed, or global undo changes pointers... */
206                 if(uel->type==G.obedit->type && strcmp(uel->id.name, G.obedit->id.name)==0 &&
207                    (!uel->validate_undo || uel->validate_undo(uel->undodata))) {
208                         uel->ob= G.obedit;
209                 }
210                 else {
211                         mixed= 1;
212                         uel->freedata(uel->undodata);
213                         BLI_freelinkN(&undobase, uel);
214                 }
215
216                 uel= next;
217         }
218         
219         if(mixed) curundo= undobase.last;
220 }
221
222 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
223 void undo_editmode_step(int step)
224 {
225         
226         /* prevent undo to happen on wrong object, stack can be a mix */
227         undo_clean_stack();
228         
229         if(step==0) {
230                 undo_restore(curundo);
231         }
232         else if(step==1) {
233                 
234                 if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo");
235                 else {
236                         if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
237                         curundo= curundo->prev;
238                         undo_restore(curundo);
239                 }
240         }
241         else {
242                 /* curundo has to remain current situation! */
243                 
244                 if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
245                 else {
246                         undo_restore(curundo->next);
247                         curundo= curundo->next;
248                         if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
249                 }
250         }
251
252 //      DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
253         /* XXX notifiers */
254 }
255
256 void undo_editmode_clear(void)
257 {
258         UndoElem *uel;
259         
260         uel= undobase.first;
261         while(uel) {
262                 uel->freedata(uel->undodata);
263                 uel= uel->next;
264         }
265         BLI_freelistN(&undobase);
266         curundo= NULL;
267 }
268
269 /* based on index nr it does a restore */
270 static void undo_number(int nr)
271 {
272         UndoElem *uel;
273         int a=1;
274         
275         for(uel= undobase.first; uel; uel= uel->next, a++) {
276                 if(a==nr) break;
277         }
278         curundo= uel;
279         undo_editmode_step(0);
280 }
281
282 /* ************** for interaction with menu/pullown */
283
284 void undo_editmode_menu(void)
285 {
286         UndoElem *uel;
287         DynStr *ds= BLI_dynstr_new();
288         short event;
289         char *menu;
290
291         undo_clean_stack();     // removes other objects from it
292         
293         BLI_dynstr_append(ds, "Editmode Undo History %t");
294         
295         for(uel= undobase.first; uel; uel= uel->next) {
296                 BLI_dynstr_append(ds, "|");
297                 BLI_dynstr_append(ds, uel->name);
298         }
299         
300         menu= BLI_dynstr_get_cstring(ds);
301         BLI_dynstr_free(ds);
302         
303 // XXX  event= pupmenu_col(menu, 20);
304         MEM_freeN(menu);
305         
306         if(event>0) undo_number(event);
307 }
308
309 static void do_editmode_undohistorymenu(bContext *C, void *arg, int event)
310 {
311         
312         if(G.obedit==NULL || event<1) return;
313
314         undo_number(event-1);
315         
316 }
317
318 uiBlock *editmode_undohistorymenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused)
319 {
320         uiBlock *block;
321         UndoElem *uel;
322         short yco = 20, menuwidth = 120;
323         short item= 1;
324         
325         undo_clean_stack();     // removes other objects from it
326
327         block= uiBeginBlock(C, handle->region, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP, UI_HELV);
328         uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
329         
330         for(uel= undobase.first; uel; uel= uel->next, item++) {
331                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
332                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, uel->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, (float)item, "");
333                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
334         }
335         
336         uiBlockSetDirection(block, UI_RIGHT);
337         uiTextBoundsBlock(block, 60);
338         return block;
339 }
340
341 void *undo_editmode_get_prev(Object *ob)
342 {
343         UndoElem *ue= undobase.last;
344         if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata;
345         return NULL;
346 }