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