============== Various changes from trunk that were somehow missed with merges, and small code cleanups and fixes.
376 lines
9.2 KiB
C
376 lines
9.2 KiB
C
/**
|
|
* $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) 2001-2002 by NaN Holding BV.
|
|
* 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>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#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_depsgraph.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_object.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_dynstr.h"
|
|
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "BIF_editmesh.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_resources.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_space.h"
|
|
|
|
#include "BDR_editcurve.h"
|
|
|
|
#include "BSE_edit.h"
|
|
|
|
#include "mydevice.h"
|
|
|
|
/* ***************** generic editmode undo system ********************* */
|
|
/*
|
|
|
|
Add this in your local code:
|
|
|
|
void undo_editmode_push(char *name,
|
|
void (*freedata)(void *), // pointer to function freeing data
|
|
void (*to_editmode)(void *), // data to editmode conversion
|
|
void * (*from_editmode)(void)) // editmode to data conversion
|
|
int (*validate_undo)(void *)) // check if undo data is still valid
|
|
|
|
|
|
Further exported for UI is:
|
|
|
|
void undo_editmode_step(int step); // undo and redo
|
|
void undo_editmode_clear(void) // free & clear all data
|
|
void undo_editmode_menu(void) // history menu
|
|
|
|
|
|
*/
|
|
/* ********************************************************************* */
|
|
|
|
/* local prototypes --------------- */
|
|
void undo_editmode_step(int step); // undo and redo
|
|
void undo_editmode_clear(void); // free & clear all data
|
|
void undo_editmode_menu(void); // history menu
|
|
void undo_editmode_push(char *name, void (*freedata)(void *),
|
|
void (*to_editmode)(void *), void *(*from_editmode)(void),
|
|
int (*validate_undo)(void *));
|
|
struct uiBlock *editmode_undohistorymenu(void *arg_unused);
|
|
|
|
|
|
|
|
#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];
|
|
void (*freedata)(void *);
|
|
void (*to_editmode)(void *);
|
|
void * (*from_editmode)(void);
|
|
int (*validate_undo)(void *);
|
|
} UndoElem;
|
|
|
|
static ListBase undobase={NULL, NULL};
|
|
static UndoElem *curundo= NULL;
|
|
|
|
|
|
/* ********************* xtern api calls ************* */
|
|
|
|
static void undo_restore(UndoElem *undo)
|
|
{
|
|
if(undo) {
|
|
waitcursor(1);
|
|
undo->to_editmode(undo->undodata);
|
|
waitcursor(0);
|
|
|
|
countall();
|
|
}
|
|
}
|
|
|
|
/* name can be a dynamic string */
|
|
void undo_editmode_push(char *name, void (*freedata)(void *),
|
|
void (*to_editmode)(void *), void *(*from_editmode)(void),
|
|
int (*validate_undo)(void *))
|
|
{
|
|
UndoElem *uel;
|
|
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 */
|
|
curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
|
|
strncpy(uel->name, name, MAXUNDONAME-1);
|
|
BLI_addtail(&undobase, uel);
|
|
|
|
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();
|
|
curundo->undodata= curundo->from_editmode();
|
|
curundo->undosize= MEM_get_memory_in_use() - memused;
|
|
curundo->ob= G.obedit;
|
|
curundo->id= G.obedit->id;
|
|
curundo->type= G.obedit->type;
|
|
|
|
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 */
|
|
static void undo_clean_stack(void)
|
|
{
|
|
UndoElem *uel, *next;
|
|
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) {
|
|
next= uel->next;
|
|
|
|
/* for when objects are converted, renamed, or global undo changes pointers... */
|
|
if(uel->type==G.obedit->type && strcmp(uel->id.name, G.obedit->id.name)==0 &&
|
|
(!uel->validate_undo || uel->validate_undo(uel->undodata))) {
|
|
uel->ob= G.obedit;
|
|
}
|
|
else {
|
|
mixed= 1;
|
|
uel->freedata(uel->undodata);
|
|
BLI_freelinkN(&undobase, uel);
|
|
}
|
|
|
|
uel= next;
|
|
}
|
|
|
|
if(mixed) curundo= undobase.last;
|
|
}
|
|
|
|
/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
|
|
void undo_editmode_step(int step)
|
|
{
|
|
|
|
/* prevent undo to happen on wrong object, stack can be a mix */
|
|
undo_clean_stack();
|
|
|
|
if(step==0) {
|
|
undo_restore(curundo);
|
|
}
|
|
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;
|
|
undo_restore(curundo);
|
|
}
|
|
}
|
|
else {
|
|
/* curundo has to remain current situation! */
|
|
|
|
if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
|
|
else {
|
|
undo_restore(curundo->next);
|
|
curundo= curundo->next;
|
|
if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
|
|
}
|
|
}
|
|
|
|
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
|
allqueue(REDRAWVIEW3D, 1);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWIMAGE, 0);
|
|
}
|
|
|
|
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 */
|
|
static void undo_number(int nr)
|
|
{
|
|
UndoElem *uel;
|
|
int a=1;
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, a++) {
|
|
if(a==nr) break;
|
|
}
|
|
curundo= uel;
|
|
undo_editmode_step(0);
|
|
}
|
|
|
|
/* ************** for interaction with menu/pullown */
|
|
|
|
void undo_editmode_menu(void)
|
|
{
|
|
UndoElem *uel;
|
|
DynStr *ds= BLI_dynstr_new();
|
|
short event;
|
|
char *menu;
|
|
|
|
undo_clean_stack(); // removes other objects from it
|
|
|
|
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);
|
|
|
|
event= pupmenu_col(menu, 20);
|
|
MEM_freeN(menu);
|
|
|
|
if(event>0) undo_number(event);
|
|
}
|
|
|
|
static void do_editmode_undohistorymenu(void *arg, int event)
|
|
{
|
|
|
|
if(G.obedit==NULL || event<1) return;
|
|
|
|
if (event==1) {
|
|
if(G.obedit->type==OB_MESH) remake_editMesh();
|
|
}
|
|
else undo_number(event-1);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
|
|
uiBlock *editmode_undohistorymenu(void *arg_unused)
|
|
{
|
|
uiBlock *block;
|
|
UndoElem *uel;
|
|
short yco = 20, menuwidth = 120;
|
|
short item=2;
|
|
|
|
undo_clean_stack(); // removes other objects from it
|
|
|
|
block= uiNewBlock(&curarea->uiblocks, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
|
|
uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
|
|
|
|
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Undo All Changes", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
|
|
|
|
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;
|
|
}
|