|
Blender
V2.59
|
00001 /* 00002 * $Id: editmode_undo.c 37185 2011-06-04 17:03:46Z ton $ 00003 * 00004 * ***** BEGIN GPL LICENSE BLOCK ***** 00005 * 00006 * This program is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU General Public License 00008 * as published by the Free Software Foundation; either version 2 00009 * of the License, or (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program; if not, write to the Free Software Foundation, 00018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 * 00020 * The Original Code is Copyright (C) 2004 Blender Foundation 00021 * All rights reserved. 00022 * 00023 * The Original Code is: all of this file. 00024 * 00025 * Contributor(s): none yet. 00026 * 00027 * ***** END GPL LICENSE BLOCK ***** 00028 */ 00029 00036 #include <stdlib.h> 00037 #include <string.h> 00038 #include <math.h> 00039 00040 #include "MEM_guardedalloc.h" 00041 00042 #include "DNA_object_types.h" 00043 #include "DNA_screen_types.h" 00044 00045 #include "BLI_blenlib.h" 00046 #include "BLI_dynstr.h" 00047 #include "BLI_utildefines.h" 00048 00049 00050 #include "BKE_context.h" 00051 #include "BKE_depsgraph.h" 00052 #include "BKE_global.h" 00053 00054 #include "ED_util.h" 00055 #include "ED_mesh.h" 00056 00057 #include "UI_interface.h" 00058 #include "UI_resources.h" 00059 00060 #include "util_intern.h" 00061 00062 /* ***************** generic editmode undo system ********************* */ 00063 /* 00064 00065 Add this in your local code: 00066 00067 void undo_editmode_push(bContext *C, const char *name, 00068 void * (*getdata)(bContext *C), // use context to retrieve current editdata 00069 void (*freedata)(void *), // pointer to function freeing data 00070 void (*to_editmode)(void *, void *), // data to editmode conversion 00071 void * (*from_editmode)(void *)) // editmode to data conversion 00072 int (*validate_undo)(void *, void *)) // check if undo data is still valid 00073 00074 00075 Further exported for UI is: 00076 00077 void undo_editmode_step(bContext *C, int step); // undo and redo 00078 void undo_editmode_clear(void) // free & clear all data 00079 void undo_editmode_menu(void) // history menu 00080 00081 00082 */ 00083 /* ********************************************************************* */ 00084 00085 /* ****** XXX ***** */ 00086 static void error(const char *UNUSED(arg)) {} 00087 /* ****** XXX ***** */ 00088 00089 00090 #define MAXUNDONAME 64 00091 typedef struct UndoElem { 00092 struct UndoElem *next, *prev; 00093 ID id; // copy of editmode object ID 00094 Object *ob; // pointer to edited object 00095 int type; // type of edited object 00096 void *undodata; 00097 uintptr_t undosize; 00098 char name[MAXUNDONAME]; 00099 void * (*getdata)(bContext *C); 00100 void (*freedata)(void *); 00101 void (*to_editmode)(void *, void *); 00102 void * (*from_editmode)(void *); 00103 int (*validate_undo)(void *, void *); 00104 } UndoElem; 00105 00106 static ListBase undobase={NULL, NULL}; 00107 static UndoElem *curundo= NULL; 00108 00109 00110 /* ********************* xtern api calls ************* */ 00111 00112 static void undo_restore(UndoElem *undo, void *editdata) 00113 { 00114 if(undo) { 00115 undo->to_editmode(undo->undodata, editdata); 00116 } 00117 } 00118 00119 /* name can be a dynamic string */ 00120 void undo_editmode_push(bContext *C, const char *name, 00121 void * (*getdata)(bContext *C), 00122 void (*freedata)(void *), 00123 void (*to_editmode)(void *, void *), 00124 void *(*from_editmode)(void *), 00125 int (*validate_undo)(void *, void *)) 00126 { 00127 UndoElem *uel; 00128 Object *obedit= CTX_data_edit_object(C); 00129 void *editdata; 00130 int nr; 00131 uintptr_t memused, totmem, maxmem; 00132 00133 /* at first here was code to prevent an "original" key to be insterted twice 00134 this was giving conflicts for example when mesh changed due to keys or apply */ 00135 00136 /* remove all undos after (also when curundo==NULL) */ 00137 while(undobase.last != curundo) { 00138 uel= undobase.last; 00139 uel->freedata(uel->undodata); 00140 BLI_freelinkN(&undobase, uel); 00141 } 00142 00143 /* make new */ 00144 curundo= uel= MEM_callocN(sizeof(UndoElem), "undo editmode"); 00145 strncpy(uel->name, name, MAXUNDONAME-1); 00146 BLI_addtail(&undobase, uel); 00147 00148 uel->getdata= getdata; 00149 uel->freedata= freedata; 00150 uel->to_editmode= to_editmode; 00151 uel->from_editmode= from_editmode; 00152 uel->validate_undo= validate_undo; 00153 00154 /* limit amount to the maximum amount*/ 00155 nr= 0; 00156 uel= undobase.last; 00157 while(uel) { 00158 nr++; 00159 if(nr==U.undosteps) break; 00160 uel= uel->prev; 00161 } 00162 if(uel) { 00163 while(undobase.first!=uel) { 00164 UndoElem *first= undobase.first; 00165 first->freedata(first->undodata); 00166 BLI_freelinkN(&undobase, first); 00167 } 00168 } 00169 00170 /* copy */ 00171 memused= MEM_get_memory_in_use(); 00172 editdata= getdata(C); 00173 curundo->undodata= curundo->from_editmode(editdata); 00174 curundo->undosize= MEM_get_memory_in_use() - memused; 00175 curundo->ob= obedit; 00176 curundo->id= obedit->id; 00177 curundo->type= obedit->type; 00178 00179 if(U.undomemory != 0) { 00180 /* limit to maximum memory (afterwards, we can't know in advance) */ 00181 totmem= 0; 00182 maxmem= ((uintptr_t)U.undomemory)*1024*1024; 00183 00184 uel= undobase.last; 00185 while(uel && uel->prev) { 00186 totmem+= uel->undosize; 00187 if(totmem>maxmem) break; 00188 uel= uel->prev; 00189 } 00190 00191 if(uel) { 00192 if(uel->prev && uel->prev->prev) 00193 uel= uel->prev; 00194 00195 while(undobase.first!=uel) { 00196 UndoElem *first= undobase.first; 00197 first->freedata(first->undodata); 00198 BLI_freelinkN(&undobase, first); 00199 } 00200 } 00201 } 00202 } 00203 00204 /* helper to remove clean other objects from undo stack */ 00205 static void undo_clean_stack(bContext *C) 00206 { 00207 UndoElem *uel, *next; 00208 Object *obedit= CTX_data_edit_object(C); 00209 00210 /* global undo changes pointers, so we also allow identical names */ 00211 /* side effect: when deleting/renaming object and start editing new one with same name */ 00212 00213 uel= undobase.first; 00214 while(uel) { 00215 void *editdata= uel->getdata(C); 00216 int isvalid= 0; 00217 next= uel->next; 00218 00219 /* for when objects are converted, renamed, or global undo changes pointers... */ 00220 if(uel->type==obedit->type) { 00221 if(strcmp(uel->id.name, obedit->id.name)==0) { 00222 if(uel->validate_undo==NULL) 00223 isvalid= 1; 00224 else if(uel->validate_undo(uel->undodata, editdata)) 00225 isvalid= 1; 00226 } 00227 } 00228 if(isvalid) 00229 uel->ob= obedit; 00230 else { 00231 if(uel == curundo) 00232 curundo= NULL; 00233 00234 uel->freedata(uel->undodata); 00235 BLI_freelinkN(&undobase, uel); 00236 } 00237 00238 uel= next; 00239 } 00240 00241 if(curundo == NULL) curundo= undobase.last; 00242 } 00243 00244 /* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */ 00245 void undo_editmode_step(bContext *C, int step) 00246 { 00247 Object *obedit= CTX_data_edit_object(C); 00248 00249 /* prevent undo to happen on wrong object, stack can be a mix */ 00250 undo_clean_stack(C); 00251 00252 if(step==0) { 00253 undo_restore(curundo, curundo->getdata(C)); 00254 } 00255 else if(step==1) { 00256 00257 if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo"); 00258 else { 00259 if(G.f & G_DEBUG) printf("undo %s\n", curundo->name); 00260 curundo= curundo->prev; 00261 undo_restore(curundo, curundo->getdata(C)); 00262 } 00263 } 00264 else { 00265 /* curundo has to remain current situation! */ 00266 00267 if(curundo==NULL || curundo->next==NULL) error("No more steps to redo"); 00268 else { 00269 undo_restore(curundo->next, curundo->getdata(C)); 00270 curundo= curundo->next; 00271 if(G.f & G_DEBUG) printf("redo %s\n", curundo->name); 00272 } 00273 } 00274 00275 /* special case for editmesh, mode must be copied back to the scene */ 00276 if(obedit->type == OB_MESH) { 00277 EM_selectmode_to_scene(CTX_data_scene(C), obedit); 00278 } 00279 00280 DAG_id_tag_update(&obedit->id, OB_RECALC_DATA); 00281 00282 /* XXX notifiers */ 00283 } 00284 00285 void undo_editmode_clear(void) 00286 { 00287 UndoElem *uel; 00288 00289 uel= undobase.first; 00290 while(uel) { 00291 uel->freedata(uel->undodata); 00292 uel= uel->next; 00293 } 00294 BLI_freelistN(&undobase); 00295 curundo= NULL; 00296 } 00297 00298 /* based on index nr it does a restore */ 00299 void undo_editmode_number(bContext *C, int nr) 00300 { 00301 UndoElem *uel; 00302 int a=1; 00303 00304 for(uel= undobase.first; uel; uel= uel->next, a++) { 00305 if(a==nr) break; 00306 } 00307 curundo= uel; 00308 undo_editmode_step(C, 0); 00309 } 00310 00311 void undo_editmode_name(bContext *C, const char *undoname) 00312 { 00313 UndoElem *uel; 00314 00315 for(uel= undobase.last; uel; uel= uel->prev) { 00316 if(strcmp(undoname, uel->name)==0) 00317 break; 00318 } 00319 if(uel && uel->prev) { 00320 curundo= uel->prev; 00321 undo_editmode_step(C, 0); 00322 } 00323 } 00324 00325 /* undoname optionally, if NULL it just checks for existing undo steps */ 00326 int undo_editmode_valid(const char *undoname) 00327 { 00328 if(undoname) { 00329 UndoElem *uel; 00330 00331 for(uel= undobase.last; uel; uel= uel->prev) { 00332 if(strcmp(undoname, uel->name)==0) 00333 break; 00334 } 00335 return uel != NULL; 00336 } 00337 return undobase.last != undobase.first; 00338 } 00339 00340 00341 /* get name of undo item, return null if no item with this index */ 00342 /* if active pointer, set it to 1 if true */ 00343 char *undo_editmode_get_name(bContext *C, int nr, int *active) 00344 { 00345 UndoElem *uel; 00346 00347 /* prevent wrong numbers to be returned */ 00348 undo_clean_stack(C); 00349 00350 if(active) *active= 0; 00351 00352 uel= BLI_findlink(&undobase, nr); 00353 if(uel) { 00354 if(active && uel==curundo) 00355 *active= 1; 00356 return uel->name; 00357 } 00358 return NULL; 00359 } 00360 00361 00362 void *undo_editmode_get_prev(Object *ob) 00363 { 00364 UndoElem *ue= undobase.last; 00365 if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata; 00366 return NULL; 00367 }