Blender V2.61 - r43446

undo.c

Go to the documentation of this file.
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