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