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