soc-2008-mxcurioni: foundations for Freestyle as a render layer - new UI buttons...
[blender.git] / source / blender / src / 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) 2001-2002 by NaN Holding BV.
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 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include "MEM_guardedalloc.h"
40
41 #include "DNA_mesh_types.h"
42 #include "DNA_meshdata_types.h"
43 #include "DNA_object_types.h"
44 #include "DNA_screen_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_userdef_types.h"
47
48 #include "BKE_depsgraph.h"
49 #include "BKE_global.h"
50 #include "BKE_object.h"
51
52 #include "BLI_blenlib.h"
53 #include "BLI_dynstr.h"
54
55 #include "BKE_utildefines.h"
56
57 #include "BIF_editmesh.h"
58 #include "BIF_interface.h"
59 #include "BIF_screen.h"
60 #include "BIF_resources.h"
61 #include "BIF_toolbox.h"
62 #include "BIF_space.h"
63
64 #include "BDR_editcurve.h"
65
66 #include "BSE_edit.h"
67
68 #include "mydevice.h"
69
70 /* ***************** generic editmode undo system ********************* */
71 /*
72
73 Add this in your local code:
74
75 void undo_editmode_push(char *name, 
76                 void (*freedata)(void *),                       // pointer to function freeing data
77                 void (*to_editmode)(void *),        // data to editmode conversion
78                 void * (*from_editmode)(void))      // editmode to data conversion
79                 int  (*validate_undo)(void *))      // check if undo data is still valid
80
81
82 Further exported for UI is:
83
84 void undo_editmode_step(int step);                      // undo and redo
85 void undo_editmode_clear(void)                          // free & clear all data
86 void undo_editmode_menu(void)                           // history menu
87
88
89 */
90 /* ********************************************************************* */
91
92 /* local prototypes --------------- */
93  void undo_editmode_step(int step);     // undo and redo
94  void undo_editmode_clear(void);                // free & clear all data
95  void undo_editmode_menu(void);         // history menu
96  void undo_editmode_push(char *name, void (*freedata)(void *), 
97                                                 void (*to_editmode)(void *),  void *(*from_editmode)(void),
98                                                 int (*validate_undo)(void *)); 
99  struct uiBlock *editmode_undohistorymenu(void *arg_unused);
100
101
102
103 #define MAXUNDONAME     64
104 typedef struct UndoElem {
105         struct UndoElem *next, *prev;
106         ID id;                  // copy of editmode object ID
107         Object *ob;             // pointer to edited object
108         int type;               // type of edited object
109         void *undodata;
110         char name[MAXUNDONAME];
111         void (*freedata)(void *);
112         void (*to_editmode)(void *);
113         void * (*from_editmode)(void);
114         int (*validate_undo)(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                 int (*validate_undo)(void *))
138 {
139         UndoElem *uel;
140         int nr;
141
142         /* at first here was code to prevent an "original" key to be insterted twice
143            this was giving conflicts for example when mesh changed due to keys or apply */
144         
145         /* remove all undos after (also when curundo==NULL) */
146         while(undobase.last != curundo) {
147                 uel= undobase.last;
148                 BLI_remlink(&undobase, uel);
149                 uel->freedata(uel->undodata);
150                 MEM_freeN(uel);
151         }
152         
153         /* make new */
154         curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
155         strncpy(uel->name, name, MAXUNDONAME-1);
156         BLI_addtail(&undobase, uel);
157         
158         uel->freedata= freedata;
159         uel->to_editmode= to_editmode;
160         uel->from_editmode= from_editmode;
161         uel->validate_undo= validate_undo;
162         
163         /* and limit amount to the maximum */
164         nr= 0;
165         uel= undobase.last;
166         while(uel) {
167                 nr++;
168                 if(nr==U.undosteps) break;
169                 uel= uel->prev;
170         }
171         if(uel) {
172                 while(undobase.first!=uel) {
173                         UndoElem *first= undobase.first;
174                         BLI_remlink(&undobase, first);
175                         first->freedata(first->undodata);
176                         MEM_freeN(first);
177                 }
178         }
179
180         /* copy  */
181         curundo->undodata= curundo->from_editmode();
182         curundo->ob= G.obedit;
183         curundo->id= G.obedit->id;
184         curundo->type= G.obedit->type;
185 }
186
187
188 /* helper to remove clean other objects from undo stack */
189 static void undo_clean_stack(void)
190 {
191         UndoElem *uel, *next;
192         int mixed= 0;
193         
194         /* global undo changes pointers, so we also allow identical names */
195         /* side effect: when deleting/renaming object and start editing new one with same name */
196         
197         uel= undobase.first; 
198         while(uel) {
199                 next= uel->next;
200                 
201                 /* for when objects are converted, renamed, or global undo changes pointers... */
202                 if(uel->type==G.obedit->type && strcmp(uel->id.name, G.obedit->id.name)==0 &&
203                    (!uel->validate_undo || uel->validate_undo(uel->undodata))) {
204                         uel->ob= G.obedit;
205                 }
206                 else {
207                         mixed= 1;
208                         BLI_remlink(&undobase, uel);
209                         uel->freedata(uel->undodata);
210                         MEM_freeN(uel);
211                 }
212
213                 uel= next;
214         }
215         
216         if(mixed) curundo= undobase.last;
217 }
218
219 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
220 void undo_editmode_step(int step)
221 {
222         
223         /* prevent undo to happen on wrong object, stack can be a mix */
224         undo_clean_stack();
225         
226         if(step==0) {
227                 undo_restore(curundo);
228         }
229         else if(step==1) {
230                 
231                 if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo");
232                 else {
233                         if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
234                         curundo= curundo->prev;
235                         undo_restore(curundo);
236                 }
237         }
238         else {
239                 /* curundo has to remain current situation! */
240                 
241                 if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
242                 else {
243                         undo_restore(curundo->next);
244                         curundo= curundo->next;
245                         if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
246                 }
247         }
248
249         DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
250         allqueue(REDRAWVIEW3D, 1);
251         allqueue(REDRAWBUTSEDIT, 0);
252         allqueue(REDRAWIMAGE, 0);
253 }
254
255 void undo_editmode_clear(void)
256 {
257         UndoElem *uel;
258         
259         uel= undobase.first;
260         while(uel) {
261                 uel->freedata(uel->undodata);
262                 uel= uel->next;
263         }
264         BLI_freelistN(&undobase);
265         curundo= NULL;
266 }
267
268 /* based on index nr it does a restore */
269 static void undo_number(int nr)
270 {
271         UndoElem *uel;
272         int a=1;
273         
274         for(uel= undobase.first; uel; uel= uel->next, a++) {
275                 if(a==nr) break;
276         }
277         curundo= uel;
278         undo_editmode_step(0);
279 }
280
281 /* ************** for interaction with menu/pullown */
282
283 void undo_editmode_menu(void)
284 {
285         UndoElem *uel;
286         DynStr *ds= BLI_dynstr_new();
287         short event;
288         char *menu;
289
290         undo_clean_stack();     // removes other objects from it
291         
292         BLI_dynstr_append(ds, "Editmode Undo History %t");
293         
294         for(uel= undobase.first; uel; uel= uel->next) {
295                 BLI_dynstr_append(ds, "|");
296                 BLI_dynstr_append(ds, uel->name);
297         }
298         
299         menu= BLI_dynstr_get_cstring(ds);
300         BLI_dynstr_free(ds);
301         
302         event= pupmenu_col(menu, 20);
303         MEM_freeN(menu);
304         
305         if(event>0) undo_number(event);
306 }
307
308 static void do_editmode_undohistorymenu(void *arg, int event)
309 {
310         
311         if(G.obedit==NULL || event<1) return;
312
313         if (event==1) {
314                 if(G.obedit->type==OB_MESH) remake_editMesh();
315         }
316         else undo_number(event-1);
317         
318         allqueue(REDRAWVIEW3D, 0);
319 }
320
321 uiBlock *editmode_undohistorymenu(void *arg_unused)
322 {
323         uiBlock *block;
324         UndoElem *uel;
325         short yco = 20, menuwidth = 120;
326         short item=2;
327         
328         undo_clean_stack();     // removes other objects from it
329
330         block= uiNewBlock(&curarea->uiblocks, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
331         uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
332         
333         uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Undo All Changes", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
334         
335         for(uel= undobase.first; uel; uel= uel->next, item++) {
336                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
337                 uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, uel->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, (float)item, "");
338                 if (uel==curundo) uiDefBut(block, SEPR, 0, "",          0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
339         }
340         
341         uiBlockSetDirection(block, UI_RIGHT);
342         uiTextBoundsBlock(block, 60);
343         return block;
344 }
345
346 void *undo_editmode_get_prev(Object *ob)
347 {
348         UndoElem *ue= undobase.last;
349         if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata;
350         return NULL;
351 }