Blender  V2.59
undo.c
Go to the documentation of this file.
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