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