|
Blender
V2.59
|
00001 /* 00002 * $Id: undo.c 38725 2011-07-26 13:05:22Z campbellbarton $ 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 00044 #include "BLI_blenlib.h" 00045 #include "BLI_editVert.h" 00046 #include "BLI_dynstr.h" 00047 #include "BLI_utildefines.h" 00048 00049 #include "BKE_blender.h" 00050 #include "BKE_context.h" 00051 #include "BKE_global.h" 00052 #include "BKE_screen.h" 00053 00054 #include "ED_armature.h" 00055 #include "ED_particle.h" 00056 #include "ED_curve.h" 00057 #include "ED_mball.h" 00058 #include "ED_mesh.h" 00059 #include "ED_object.h" 00060 #include "ED_screen.h" 00061 #include "ED_sculpt.h" 00062 #include "ED_util.h" 00063 #include "ED_text.h" 00064 00065 #include "WM_api.h" 00066 #include "WM_types.h" 00067 00068 #include "RNA_access.h" 00069 #include "RNA_define.h" 00070 00071 #include "UI_interface.h" 00072 #include "UI_resources.h" 00073 00074 #include "util_intern.h" 00075 00076 #define MAXUNDONAME 64 /* XXX, make common define */ 00077 00078 /* ***************** generic undo system ********************* */ 00079 00080 void ED_undo_push(bContext *C, const char *str) 00081 { 00082 wmWindowManager *wm= CTX_wm_manager(C); 00083 Object *obedit= CTX_data_edit_object(C); 00084 Object *obact= CTX_data_active_object(C); 00085 00086 if (G.f & G_DEBUG) 00087 printf("undo push %s\n", str); 00088 00089 if(obedit) { 00090 if (U.undosteps == 0) return; 00091 00092 if(obedit->type==OB_MESH) 00093 undo_push_mesh(C, str); 00094 else if ELEM(obedit->type, OB_CURVE, OB_SURF) 00095 undo_push_curve(C, str); 00096 else if (obedit->type==OB_FONT) 00097 undo_push_font(C, str); 00098 else if (obedit->type==OB_MBALL) 00099 undo_push_mball(C, str); 00100 else if (obedit->type==OB_LATTICE) 00101 undo_push_lattice(C, str); 00102 else if (obedit->type==OB_ARMATURE) 00103 undo_push_armature(C, str); 00104 } 00105 else if(obact && obact->mode & OB_MODE_PARTICLE_EDIT) { 00106 if (U.undosteps == 0) return; 00107 00108 PE_undo_push(CTX_data_scene(C), str); 00109 } 00110 else { 00111 if(U.uiflag & USER_GLOBALUNDO) 00112 BKE_write_undo(C, str); 00113 } 00114 00115 if(wm->file_saved) { 00116 wm->file_saved= 0; 00117 /* notifier that data changed, for save-over warning or header */ 00118 WM_event_add_notifier(C, NC_WM|ND_DATACHANGED, NULL); 00119 } 00120 } 00121 00122 /* note: also check undo_history_exec() in bottom if you change notifiers */ 00123 static int ed_undo_step(bContext *C, int step, const char *undoname) 00124 { 00125 Object *obedit= CTX_data_edit_object(C); 00126 Object *obact= CTX_data_active_object(C); 00127 ScrArea *sa= CTX_wm_area(C); 00128 00129 if(sa && sa->spacetype==SPACE_IMAGE) { 00130 SpaceImage *sima= (SpaceImage *)sa->spacedata.first; 00131 00132 if((obact && obact->mode & OB_MODE_TEXTURE_PAINT) || sima->flag & SI_DRAWTOOL) { 00133 if(!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname) && undoname) 00134 if(U.uiflag & USER_GLOBALUNDO) 00135 BKE_undo_name(C, undoname); 00136 00137 WM_event_add_notifier(C, NC_WINDOW, NULL); 00138 return OPERATOR_FINISHED; 00139 } 00140 } 00141 00142 if(sa && sa->spacetype==SPACE_TEXT) { 00143 ED_text_undo_step(C, step); 00144 } 00145 else if(obedit) { 00146 if ELEM7(obedit->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE) { 00147 if(undoname) 00148 undo_editmode_name(C, undoname); 00149 else 00150 undo_editmode_step(C, step); 00151 00152 WM_event_add_notifier(C, NC_GEOM|ND_DATA, NULL); 00153 } 00154 } 00155 else { 00156 int do_glob_undo= 0; 00157 00158 if(obact && obact->mode & OB_MODE_TEXTURE_PAINT) { 00159 if(!ED_undo_paint_step(C, UNDO_PAINT_IMAGE, step, undoname)) 00160 do_glob_undo= 1; 00161 } 00162 else if(obact && obact->mode & OB_MODE_SCULPT) { 00163 if(!ED_undo_paint_step(C, UNDO_PAINT_MESH, step, undoname)) 00164 do_glob_undo= 1; 00165 } 00166 else if(obact && obact->mode & OB_MODE_PARTICLE_EDIT) { 00167 if(step==1) 00168 PE_undo(CTX_data_scene(C)); 00169 else 00170 PE_redo(CTX_data_scene(C)); 00171 } 00172 else { 00173 do_glob_undo= 1; 00174 } 00175 00176 if(do_glob_undo) { 00177 if(U.uiflag & USER_GLOBALUNDO) { 00178 // note python defines not valid here anymore. 00179 //#ifdef WITH_PYTHON 00180 // XXX BPY_scripts_clear_pyobjects(); 00181 //#endif 00182 if(undoname) 00183 BKE_undo_name(C, undoname); 00184 else 00185 BKE_undo_step(C, step); 00186 00187 WM_event_add_notifier(C, NC_SCENE|ND_LAYER_CONTENT, CTX_data_scene(C)); 00188 } 00189 00190 } 00191 } 00192 00193 WM_event_add_notifier(C, NC_WINDOW, NULL); 00194 00195 return OPERATOR_FINISHED; 00196 } 00197 00198 void ED_undo_pop(bContext *C) 00199 { 00200 ed_undo_step(C, 1, NULL); 00201 } 00202 void ED_undo_redo(bContext *C) 00203 { 00204 ed_undo_step(C, -1, NULL); 00205 } 00206 00207 void ED_undo_push_op(bContext *C, wmOperator *op) 00208 { 00209 /* in future, get undo string info? */ 00210 ED_undo_push(C, op->type->name); 00211 } 00212 00213 void ED_undo_pop_op(bContext *C, wmOperator *op) 00214 { 00215 /* search back a couple of undo's, in case something else added pushes */ 00216 ed_undo_step(C, 0, op->type->name); 00217 } 00218 00219 /* name optionally, function used to check for operator redo panel */ 00220 int ED_undo_valid(const bContext *C, const char *undoname) 00221 { 00222 Object *obedit= CTX_data_edit_object(C); 00223 Object *obact= CTX_data_active_object(C); 00224 ScrArea *sa= CTX_wm_area(C); 00225 00226 if(sa && sa->spacetype==SPACE_IMAGE) { 00227 SpaceImage *sima= (SpaceImage *)sa->spacedata.first; 00228 00229 if((obact && obact->mode & OB_MODE_TEXTURE_PAINT) || sima->flag & SI_DRAWTOOL) { 00230 return 1; 00231 } 00232 } 00233 00234 if(sa && sa->spacetype==SPACE_TEXT) { 00235 return 1; 00236 } 00237 else if(obedit) { 00238 if ELEM7(obedit->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE) { 00239 return undo_editmode_valid(undoname); 00240 } 00241 } 00242 else { 00243 00244 /* if below tests fail, global undo gets executed */ 00245 00246 if(obact && obact->mode & OB_MODE_TEXTURE_PAINT) { 00247 if( ED_undo_paint_valid(UNDO_PAINT_IMAGE, undoname) ) 00248 return 1; 00249 } 00250 else if(obact && obact->mode & OB_MODE_SCULPT) { 00251 if( ED_undo_paint_valid(UNDO_PAINT_MESH, undoname) ) 00252 return 1; 00253 } 00254 else if(obact && obact->mode & OB_MODE_PARTICLE_EDIT) { 00255 return PE_undo_valid(CTX_data_scene(C)); 00256 } 00257 00258 if(U.uiflag & USER_GLOBALUNDO) { 00259 return BKE_undo_valid(undoname); 00260 } 00261 } 00262 return 0; 00263 } 00264 00265 static int ed_undo_exec(bContext *C, wmOperator *UNUSED(op)) 00266 { 00267 /* "last operator" should disappear, later we can tie ths with undo stack nicer */ 00268 WM_operator_stack_clear(CTX_wm_manager(C)); 00269 return ed_undo_step(C, 1, NULL); 00270 } 00271 00272 static int ed_undo_push_exec(bContext *C, wmOperator *op) 00273 { 00274 char str[MAXUNDONAME]; 00275 RNA_string_get(op->ptr, "message", str); 00276 ED_undo_push(C, str); 00277 return OPERATOR_FINISHED; 00278 } 00279 00280 static int ed_redo_exec(bContext *C, wmOperator *UNUSED(op)) 00281 { 00282 return ed_undo_step(C, -1, NULL); 00283 } 00284 00285 00286 /* ********************** */ 00287 00288 void ED_OT_undo(wmOperatorType *ot) 00289 { 00290 /* identifiers */ 00291 ot->name= "Undo"; 00292 ot->description= "Undo previous action"; 00293 ot->idname= "ED_OT_undo"; 00294 00295 /* api callbacks */ 00296 ot->exec= ed_undo_exec; 00297 ot->poll= ED_operator_screenactive; 00298 } 00299 00300 void ED_OT_undo_push(wmOperatorType *ot) 00301 { 00302 /* identifiers */ 00303 ot->name= "Undo Push"; 00304 ot->description= "Add an undo state (internal use only)"; 00305 ot->idname= "ED_OT_undo_push"; 00306 00307 /* api callbacks */ 00308 ot->exec= ed_undo_push_exec; 00309 00310 ot->flag= OPTYPE_INTERNAL; 00311 00312 RNA_def_string(ot->srna, "message", "Add an undo step *function may be moved*", MAXUNDONAME, "Undo Message", ""); 00313 } 00314 00315 void ED_OT_redo(wmOperatorType *ot) 00316 { 00317 /* identifiers */ 00318 ot->name= "Redo"; 00319 ot->description= "Redo previous action"; 00320 ot->idname= "ED_OT_redo"; 00321 00322 /* api callbacks */ 00323 ot->exec= ed_redo_exec; 00324 ot->poll= ED_operator_screenactive; 00325 } 00326 00327 00328 /* ui callbacks should call this rather than calling WM_operator_repeat() themselves */ 00329 int ED_undo_operator_repeat(bContext *C, struct wmOperator *op) 00330 { 00331 int ret= 0; 00332 00333 if(op) { 00334 ARegion *ar= CTX_wm_region(C); 00335 ARegion *ar1= BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_WINDOW); 00336 00337 if(ar1) 00338 CTX_wm_region_set(C, ar1); 00339 00340 if(WM_operator_repeat_check(C, op) && WM_operator_poll(C, op->type)) { 00341 int retval; 00342 00343 if (G.f & G_DEBUG) 00344 printf("redo_cb: operator redo %s\n", op->type->name); 00345 ED_undo_pop_op(C, op); 00346 00347 if(op->type->check) { 00348 op->type->check(C, op); /* ignore return value since its running again anyway */ 00349 } 00350 00351 retval= WM_operator_repeat(C, op); 00352 if((retval & OPERATOR_FINISHED)==0) { 00353 if (G.f & G_DEBUG) 00354 printf("redo_cb: operator redo failed: %s, return %d\n", op->type->name, retval); 00355 ED_undo_redo(C); 00356 } 00357 else { 00358 ret= 1; 00359 } 00360 } 00361 else { 00362 if (G.f & G_DEBUG) { 00363 printf("redo_cb: WM_operator_repeat_check returned false %s\n", op->type->name); 00364 } 00365 } 00366 00367 /* set region back */ 00368 CTX_wm_region_set(C, ar); 00369 } 00370 else { 00371 if (G.f & G_DEBUG) { 00372 printf("redo_cb: ED_undo_operator_repeat called with NULL 'op'\n"); 00373 } 00374 } 00375 00376 return ret; 00377 } 00378 00379 00380 void ED_undo_operator_repeat_cb(bContext *C, void *arg_op, void *UNUSED(arg_unused)) 00381 { 00382 ED_undo_operator_repeat(C, (wmOperator *)arg_op); 00383 } 00384 00385 void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_event)) 00386 { 00387 ED_undo_operator_repeat(C, (wmOperator *)arg_op); 00388 } 00389 00390 00391 /* ************************** */ 00392 00393 #define UNDOSYSTEM_GLOBAL 1 00394 #define UNDOSYSTEM_EDITMODE 2 00395 #define UNDOSYSTEM_PARTICLE 3 00396 00397 static int get_undo_system(bContext *C) 00398 { 00399 Object *obedit= CTX_data_edit_object(C); 00400 00401 /* find out which undo system */ 00402 if(obedit) { 00403 if (ELEM7(obedit->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) 00404 return UNDOSYSTEM_EDITMODE; 00405 } 00406 else { 00407 Object *obact= CTX_data_active_object(C); 00408 00409 if(obact && obact->mode & OB_MODE_PARTICLE_EDIT) 00410 return UNDOSYSTEM_PARTICLE; 00411 else if(U.uiflag & USER_GLOBALUNDO) 00412 return UNDOSYSTEM_GLOBAL; 00413 } 00414 00415 return 0; 00416 } 00417 00418 /* create enum based on undo items */ 00419 static EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem) 00420 { 00421 EnumPropertyItem item_tmp= {0}, *item= NULL; 00422 int active, i= 0; 00423 00424 while(TRUE) { 00425 char *name= NULL; 00426 00427 if(undosys==UNDOSYSTEM_PARTICLE) { 00428 name= PE_undo_get_name(CTX_data_scene(C), i, &active); 00429 } 00430 else if(undosys==UNDOSYSTEM_EDITMODE) { 00431 name= undo_editmode_get_name(C, i, &active); 00432 } 00433 else { 00434 name= BKE_undo_get_name(i, &active); 00435 } 00436 00437 if(name) { 00438 item_tmp.identifier= item_tmp.name= name; 00439 if(active) 00440 item_tmp.icon= ICON_RESTRICT_VIEW_OFF; 00441 else 00442 item_tmp.icon= ICON_NONE; 00443 item_tmp.value= i++; 00444 RNA_enum_item_add(&item, totitem, &item_tmp); 00445 } 00446 else 00447 break; 00448 } 00449 00450 RNA_enum_item_end(&item, totitem); 00451 00452 return item; 00453 } 00454 00455 00456 static int undo_history_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) 00457 { 00458 int undosys, totitem= 0; 00459 00460 undosys= get_undo_system(C); 00461 00462 if(undosys) { 00463 EnumPropertyItem *item= rna_undo_itemf(C, undosys, &totitem); 00464 00465 if(totitem > 0) { 00466 uiPopupMenu *pup= uiPupMenuBegin(C, op->type->name, ICON_NONE); 00467 uiLayout *layout= uiPupMenuLayout(pup); 00468 uiLayout *split= uiLayoutSplit(layout, 0, 0), *column; 00469 int i, c; 00470 00471 for(c=0, i=totitem-1; i >= 0; i--, c++) { 00472 if( (c % 20)==0 ) 00473 column= uiLayoutColumn(split, 0); 00474 if(item[i].identifier) 00475 uiItemIntO(column, item[i].name, item[i].icon, op->type->idname, "item", item[i].value); 00476 00477 } 00478 00479 MEM_freeN(item); 00480 00481 uiPupMenuEnd(C, pup); 00482 } 00483 00484 } 00485 return OPERATOR_CANCELLED; 00486 } 00487 00488 /* note: also check ed_undo_step() in top if you change notifiers */ 00489 static int undo_history_exec(bContext *C, wmOperator *op) 00490 { 00491 if(RNA_property_is_set(op->ptr, "item")) { 00492 int undosys= get_undo_system(C); 00493 int item= RNA_int_get(op->ptr, "item"); 00494 00495 if(undosys==UNDOSYSTEM_PARTICLE) { 00496 PE_undo_number(CTX_data_scene(C), item); 00497 } 00498 else if(undosys==UNDOSYSTEM_EDITMODE) { 00499 undo_editmode_number(C, item+1); 00500 WM_event_add_notifier(C, NC_GEOM|ND_DATA, NULL); 00501 } 00502 else { 00503 BKE_undo_number(C, item); 00504 WM_event_add_notifier(C, NC_SCENE|ND_LAYER_CONTENT, CTX_data_scene(C)); 00505 } 00506 WM_event_add_notifier(C, NC_WINDOW, NULL); 00507 00508 return OPERATOR_FINISHED; 00509 } 00510 return OPERATOR_CANCELLED; 00511 } 00512 00513 void ED_OT_undo_history(wmOperatorType *ot) 00514 { 00515 /* identifiers */ 00516 ot->name= "Undo History"; 00517 ot->description= "Redo specific action in history"; 00518 ot->idname= "ED_OT_undo_history"; 00519 00520 /* api callbacks */ 00521 ot->invoke= undo_history_invoke; 00522 ot->exec= undo_history_exec; 00523 ot->poll= ED_operator_screenactive; 00524 00525 RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX); 00526 00527 } 00528 00529