2008-12-30 19:01:12 +00:00
|
|
|
/**
|
|
|
|
|
* $Id:
|
|
|
|
|
*
|
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is Copyright (C) 2004 Blender Foundation
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is: all of this file.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s): none yet.
|
|
|
|
|
*
|
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
#include "DNA_mesh_types.h"
|
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
|
#include "DNA_screen_types.h"
|
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_context.h"
|
|
|
|
|
#include "BKE_depsgraph.h"
|
|
|
|
|
#include "BKE_global.h"
|
|
|
|
|
#include "BKE_object.h"
|
|
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
|
#include "BLI_dynstr.h"
|
|
|
|
|
|
|
|
|
|
#include "BKE_utildefines.h"
|
|
|
|
|
|
|
|
|
|
#include "ED_util.h"
|
|
|
|
|
|
|
|
|
|
#include "UI_interface.h"
|
|
|
|
|
#include "UI_resources.h"
|
|
|
|
|
|
|
|
|
|
/* ***************** generic editmode undo system ********************* */
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
Add this in your local code:
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
void undo_editmode_push(bContext *C, char *name,
|
|
|
|
|
void * (*getdata)(bContext *C), // use context to retrieve current editdata
|
2008-12-30 19:01:12 +00:00
|
|
|
void (*freedata)(void *), // pointer to function freeing data
|
2009-01-01 13:15:35 +00:00
|
|
|
void (*to_editmode)(void *, void *), // data to editmode conversion
|
|
|
|
|
void * (*from_editmode)(void *)) // editmode to data conversion
|
|
|
|
|
int (*validate_undo)(void *, void *)) // check if undo data is still valid
|
2008-12-30 19:01:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
Further exported for UI is:
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
void undo_editmode_step(bContext *C, int step); // undo and redo
|
2008-12-30 19:01:12 +00:00
|
|
|
void undo_editmode_clear(void) // free & clear all data
|
|
|
|
|
void undo_editmode_menu(void) // history menu
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
/* ********************************************************************* */
|
|
|
|
|
|
|
|
|
|
/* ****** XXX ***** */
|
|
|
|
|
void error() {}
|
|
|
|
|
/* ****** XXX ***** */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define MAXUNDONAME 64
|
|
|
|
|
typedef struct UndoElem {
|
|
|
|
|
struct UndoElem *next, *prev;
|
|
|
|
|
ID id; // copy of editmode object ID
|
|
|
|
|
Object *ob; // pointer to edited object
|
|
|
|
|
int type; // type of edited object
|
|
|
|
|
void *undodata;
|
|
|
|
|
uintptr_t undosize;
|
|
|
|
|
char name[MAXUNDONAME];
|
2009-01-01 13:15:35 +00:00
|
|
|
void * (*getdata)(bContext *C);
|
2008-12-30 19:01:12 +00:00
|
|
|
void (*freedata)(void *);
|
2009-01-01 13:15:35 +00:00
|
|
|
void (*to_editmode)(void *, void *);
|
|
|
|
|
void * (*from_editmode)(void *);
|
|
|
|
|
int (*validate_undo)(void *, void *);
|
2008-12-30 19:01:12 +00:00
|
|
|
} UndoElem;
|
|
|
|
|
|
|
|
|
|
static ListBase undobase={NULL, NULL};
|
|
|
|
|
static UndoElem *curundo= NULL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ********************* xtern api calls ************* */
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
static void undo_restore(UndoElem *undo, void *editdata)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
if(undo) {
|
2009-01-01 13:15:35 +00:00
|
|
|
undo->to_editmode(undo->undodata, editdata);
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* name can be a dynamic string */
|
2009-01-01 13:15:35 +00:00
|
|
|
void undo_editmode_push(bContext *C, char *name,
|
|
|
|
|
void * (*getdata)(bContext *C),
|
|
|
|
|
void (*freedata)(void *),
|
|
|
|
|
void (*to_editmode)(void *, void *),
|
|
|
|
|
void *(*from_editmode)(void *),
|
|
|
|
|
int (*validate_undo)(void *, void *))
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
UndoElem *uel;
|
2009-01-02 19:10:35 +00:00
|
|
|
Object *obedit= CTX_data_edit_object(C);
|
2009-01-01 13:15:35 +00:00
|
|
|
void *editdata;
|
2008-12-30 19:01:12 +00:00
|
|
|
int nr;
|
|
|
|
|
uintptr_t memused, totmem, maxmem;
|
|
|
|
|
|
|
|
|
|
/* at first here was code to prevent an "original" key to be insterted twice
|
|
|
|
|
this was giving conflicts for example when mesh changed due to keys or apply */
|
|
|
|
|
|
|
|
|
|
/* remove all undos after (also when curundo==NULL) */
|
|
|
|
|
while(undobase.last != curundo) {
|
|
|
|
|
uel= undobase.last;
|
|
|
|
|
uel->freedata(uel->undodata);
|
|
|
|
|
BLI_freelinkN(&undobase, uel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* make new */
|
2009-01-01 13:15:35 +00:00
|
|
|
curundo= uel= MEM_callocN(sizeof(UndoElem), "undo editmode");
|
2008-12-30 19:01:12 +00:00
|
|
|
strncpy(uel->name, name, MAXUNDONAME-1);
|
|
|
|
|
BLI_addtail(&undobase, uel);
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
uel->getdata= getdata;
|
2008-12-30 19:01:12 +00:00
|
|
|
uel->freedata= freedata;
|
|
|
|
|
uel->to_editmode= to_editmode;
|
|
|
|
|
uel->from_editmode= from_editmode;
|
|
|
|
|
uel->validate_undo= validate_undo;
|
|
|
|
|
|
|
|
|
|
/* limit amount to the maximum amount*/
|
|
|
|
|
nr= 0;
|
|
|
|
|
uel= undobase.last;
|
|
|
|
|
while(uel) {
|
|
|
|
|
nr++;
|
|
|
|
|
if(nr==U.undosteps) break;
|
|
|
|
|
uel= uel->prev;
|
|
|
|
|
}
|
|
|
|
|
if(uel) {
|
|
|
|
|
while(undobase.first!=uel) {
|
|
|
|
|
UndoElem *first= undobase.first;
|
|
|
|
|
first->freedata(first->undodata);
|
|
|
|
|
BLI_freelinkN(&undobase, first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* copy */
|
|
|
|
|
memused= MEM_get_memory_in_use();
|
2009-01-01 13:15:35 +00:00
|
|
|
editdata= getdata(C);
|
|
|
|
|
curundo->undodata= curundo->from_editmode(editdata);
|
2008-12-30 19:01:12 +00:00
|
|
|
curundo->undosize= MEM_get_memory_in_use() - memused;
|
2009-01-02 19:10:35 +00:00
|
|
|
curundo->ob= obedit;
|
|
|
|
|
curundo->id= obedit->id;
|
|
|
|
|
curundo->type= obedit->type;
|
2008-12-30 19:01:12 +00:00
|
|
|
|
|
|
|
|
if(U.undomemory != 0) {
|
|
|
|
|
/* limit to maximum memory (afterwards, we can't know in advance) */
|
|
|
|
|
totmem= 0;
|
|
|
|
|
maxmem= ((uintptr_t)U.undomemory)*1024*1024;
|
|
|
|
|
|
|
|
|
|
uel= undobase.last;
|
|
|
|
|
while(uel && uel->prev) {
|
|
|
|
|
totmem+= uel->undosize;
|
|
|
|
|
if(totmem>maxmem) break;
|
|
|
|
|
uel= uel->prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(uel) {
|
|
|
|
|
if(uel->prev && uel->prev->prev)
|
|
|
|
|
uel= uel->prev;
|
|
|
|
|
|
|
|
|
|
while(undobase.first!=uel) {
|
|
|
|
|
UndoElem *first= undobase.first;
|
|
|
|
|
first->freedata(first->undodata);
|
|
|
|
|
BLI_freelinkN(&undobase, first);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* helper to remove clean other objects from undo stack */
|
2009-01-01 13:15:35 +00:00
|
|
|
static void undo_clean_stack(bContext *C)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
UndoElem *uel, *next;
|
2009-01-02 19:10:35 +00:00
|
|
|
Object *obedit= CTX_data_edit_object(C);
|
2008-12-30 19:01:12 +00:00
|
|
|
int mixed= 0;
|
|
|
|
|
|
|
|
|
|
/* global undo changes pointers, so we also allow identical names */
|
|
|
|
|
/* side effect: when deleting/renaming object and start editing new one with same name */
|
|
|
|
|
|
|
|
|
|
uel= undobase.first;
|
|
|
|
|
while(uel) {
|
2009-01-01 13:15:35 +00:00
|
|
|
void *editdata= uel->getdata(C);
|
|
|
|
|
int isvalid= 0;
|
2008-12-30 19:01:12 +00:00
|
|
|
next= uel->next;
|
|
|
|
|
|
|
|
|
|
/* for when objects are converted, renamed, or global undo changes pointers... */
|
2009-01-02 19:10:35 +00:00
|
|
|
if(uel->type==obedit->type) {
|
|
|
|
|
if(strcmp(uel->id.name, obedit->id.name)==0) {
|
2009-01-01 13:15:35 +00:00
|
|
|
if(uel->validate_undo==NULL)
|
|
|
|
|
isvalid= 1;
|
|
|
|
|
else if(uel->validate_undo(uel->undodata, editdata))
|
|
|
|
|
isvalid= 1;
|
|
|
|
|
}
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
2009-01-01 13:15:35 +00:00
|
|
|
if(isvalid)
|
2009-01-02 19:10:35 +00:00
|
|
|
uel->ob= obedit;
|
2008-12-30 19:01:12 +00:00
|
|
|
else {
|
|
|
|
|
mixed= 1;
|
|
|
|
|
uel->freedata(uel->undodata);
|
|
|
|
|
BLI_freelinkN(&undobase, uel);
|
|
|
|
|
}
|
2009-01-01 13:15:35 +00:00
|
|
|
|
2008-12-30 19:01:12 +00:00
|
|
|
uel= next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(mixed) curundo= undobase.last;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
|
2009-01-01 13:15:35 +00:00
|
|
|
void undo_editmode_step(bContext *C, int step)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* prevent undo to happen on wrong object, stack can be a mix */
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_clean_stack(C);
|
2008-12-30 19:01:12 +00:00
|
|
|
|
|
|
|
|
if(step==0) {
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_restore(curundo, curundo->getdata(C));
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
|
|
|
|
else if(step==1) {
|
|
|
|
|
|
|
|
|
|
if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo");
|
|
|
|
|
else {
|
|
|
|
|
if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
|
|
|
|
|
curundo= curundo->prev;
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_restore(curundo, curundo->getdata(C));
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* curundo has to remain current situation! */
|
|
|
|
|
|
|
|
|
|
if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
|
|
|
|
|
else {
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_restore(curundo->next, curundo->getdata(C));
|
2008-12-30 19:01:12 +00:00
|
|
|
curundo= curundo->next;
|
|
|
|
|
if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-02 19:10:35 +00:00
|
|
|
// DAG_object_flush_update(G.scene, obedit, OB_RECALC_DATA);
|
2008-12-30 19:01:12 +00:00
|
|
|
/* XXX notifiers */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void undo_editmode_clear(void)
|
|
|
|
|
{
|
|
|
|
|
UndoElem *uel;
|
|
|
|
|
|
|
|
|
|
uel= undobase.first;
|
|
|
|
|
while(uel) {
|
|
|
|
|
uel->freedata(uel->undodata);
|
|
|
|
|
uel= uel->next;
|
|
|
|
|
}
|
|
|
|
|
BLI_freelistN(&undobase);
|
|
|
|
|
curundo= NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* based on index nr it does a restore */
|
2009-01-01 13:15:35 +00:00
|
|
|
static void undo_number(bContext *C, int nr)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
UndoElem *uel;
|
|
|
|
|
int a=1;
|
|
|
|
|
|
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, a++) {
|
|
|
|
|
if(a==nr) break;
|
|
|
|
|
}
|
|
|
|
|
curundo= uel;
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_editmode_step(C, 0);
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ************** for interaction with menu/pullown */
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
void undo_editmode_menu(bContext *C)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
UndoElem *uel;
|
|
|
|
|
DynStr *ds= BLI_dynstr_new();
|
2009-02-19 16:22:07 +00:00
|
|
|
short event= 0;
|
2008-12-30 19:01:12 +00:00
|
|
|
char *menu;
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_clean_stack(C); // removes other objects from it
|
2008-12-30 19:01:12 +00:00
|
|
|
|
|
|
|
|
BLI_dynstr_append(ds, "Editmode Undo History %t");
|
|
|
|
|
|
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next) {
|
|
|
|
|
BLI_dynstr_append(ds, "|");
|
|
|
|
|
BLI_dynstr_append(ds, uel->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
menu= BLI_dynstr_get_cstring(ds);
|
|
|
|
|
BLI_dynstr_free(ds);
|
|
|
|
|
|
|
|
|
|
// XXX event= pupmenu_col(menu, 20);
|
|
|
|
|
MEM_freeN(menu);
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
if(event>0) undo_number(C, event);
|
2008-12-30 19:01:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_editmode_undohistorymenu(bContext *C, void *arg, int event)
|
|
|
|
|
{
|
2009-01-02 19:10:35 +00:00
|
|
|
Object *obedit= CTX_data_edit_object(C);
|
2008-12-30 19:01:12 +00:00
|
|
|
|
2009-01-02 19:10:35 +00:00
|
|
|
if(obedit==NULL || event<1) return;
|
2008-12-30 19:01:12 +00:00
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_number(C, event-1);
|
2008-12-30 19:01:12 +00:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2.5: UI & Menus
* Cleaned up UI_interface.h a bit, and added some comments to
organize things a bit and indicate what should be used when.
* uiMenu* functions can now be used to create menus for headers
too, this is done with a uiDefMenuBut, which takes a pointer
to a uiMenuCreateFunc, that will then call uiMenu* functions.
* Renamed uiMenuBegin/End to uiPupMenuBegin/End, as these are
specific to making popup menus. Will convert the other
conformation popup menu functions to use this too so we can
remove some code.
* Extended uiMenu functions, now there is is also:
BooleanO, FloatO, BooleanR, EnumR, LevelEnumR, Separator.
* Converted image window headers to use uiMenu functions, simplifies
menu code further here. Did not remove the uiDefMenu functions as
they are used in sequencer/view3d in some places now (will fix).
* Also tried to simplify and fix bounds computation a bit better
for popup menus. It tried to find out in advance what the size
of the menu was but this is difficult with keymap strings in
there, now uiPopupBoundsBlock can figure this out afterwards and
ensure the popup is within the window bounds. Will convert some
other functions to use this too.
2009-01-30 12:18:08 +00:00
|
|
|
uiBlock *editmode_undohistorymenu(bContext *C, ARegion *ar, void *arg_unused)
|
2008-12-30 19:01:12 +00:00
|
|
|
{
|
|
|
|
|
uiBlock *block;
|
|
|
|
|
UndoElem *uel;
|
|
|
|
|
short yco = 20, menuwidth = 120;
|
|
|
|
|
short item= 1;
|
|
|
|
|
|
2009-01-01 13:15:35 +00:00
|
|
|
undo_clean_stack(C); // removes other objects from it
|
2008-12-30 19:01:12 +00:00
|
|
|
|
2.5
More cleanup!
- removed old UI font completely, including from uiBeginBlock
- emboss hints for uiBlock only have three types now;
Regular, Pulldown, or "Nothing" (only icon/text)
- removed old font path from Userdef
- removed all old button theme hinting
- removed old "auto block" to merge buttons in groups
(was only in use for radiosity buttons)
And went over all warnings. One hooray for make giving clean output :)
Well, we need uniform definitions for warnings, so people at least fix
them... here's the real bad bugs I found:
- in mesh code, a call to editmesh mixed *em and *me
- in armature, ED_util.h was not included, so no warnings for wrong call
to ED_undo_push()
- The extern Py api .h was not included in the bpy_interface.c, showing
a several calls using different args.
Further just added the missing includes, and removed unused vars.
2009-04-14 15:59:52 +00:00
|
|
|
block= uiBeginBlock(C, ar, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP);
|
2008-12-30 19:01:12 +00:00
|
|
|
uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
|
|
|
|
|
|
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, item++) {
|
|
|
|
|
if (uel==curundo) uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
|
|
|
|
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, uel->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, (float)item, "");
|
|
|
|
|
if (uel==curundo) uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uiBlockSetDirection(block, UI_RIGHT);
|
|
|
|
|
uiTextBoundsBlock(block, 60);
|
|
|
|
|
return block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *undo_editmode_get_prev(Object *ob)
|
|
|
|
|
{
|
|
|
|
|
UndoElem *ue= undobase.last;
|
|
|
|
|
if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata;
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|