This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/editors/util/editmode_undo.c
Ton Roosendaal 5b3d7bfdf6 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

366 lines
9.1 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) 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:
void undo_editmode_push(bContext *C, char *name,
void * (*getdata)(bContext *C), // use context to retrieve current editdata
void (*freedata)(void *), // pointer to function freeing data
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
Further exported for UI is:
void undo_editmode_step(bContext *C, int step); // undo and redo
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];
void * (*getdata)(bContext *C);
void (*freedata)(void *);
void (*to_editmode)(void *, void *);
void * (*from_editmode)(void *);
int (*validate_undo)(void *, void *);
} UndoElem;
static ListBase undobase={NULL, NULL};
static UndoElem *curundo= NULL;
/* ********************* xtern api calls ************* */
static void undo_restore(UndoElem *undo, void *editdata)
{
if(undo) {
undo->to_editmode(undo->undodata, editdata);
}
}
/* name can be a dynamic string */
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 *))
{
UndoElem *uel;
Object *obedit= CTX_data_edit_object(C);
void *editdata;
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 editmode");
strncpy(uel->name, name, MAXUNDONAME-1);
BLI_addtail(&undobase, uel);
uel->getdata= getdata;
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();
editdata= getdata(C);
curundo->undodata= curundo->from_editmode(editdata);
curundo->undosize= MEM_get_memory_in_use() - memused;
curundo->ob= obedit;
curundo->id= obedit->id;
curundo->type= 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(bContext *C)
{
UndoElem *uel, *next;
Object *obedit= CTX_data_edit_object(C);
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) {
void *editdata= uel->getdata(C);
int isvalid= 0;
next= uel->next;
/* for when objects are converted, renamed, or global undo changes pointers... */
if(uel->type==obedit->type) {
if(strcmp(uel->id.name, obedit->id.name)==0) {
if(uel->validate_undo==NULL)
isvalid= 1;
else if(uel->validate_undo(uel->undodata, editdata))
isvalid= 1;
}
}
if(isvalid)
uel->ob= 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(bContext *C, int step)
{
/* prevent undo to happen on wrong object, stack can be a mix */
undo_clean_stack(C);
if(step==0) {
undo_restore(curundo, curundo->getdata(C));
}
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, curundo->getdata(C));
}
}
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->getdata(C));
curundo= curundo->next;
if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
}
}
// DAG_object_flush_update(G.scene, obedit, OB_RECALC_DATA);
/* 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 */
static void undo_number(bContext *C, 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(C, 0);
}
/* ************** for interaction with menu/pullown */
void undo_editmode_menu(bContext *C)
{
UndoElem *uel;
DynStr *ds= BLI_dynstr_new();
short event= 0;
char *menu;
undo_clean_stack(C); // 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);
// XXX event= pupmenu_col(menu, 20);
MEM_freeN(menu);
if(event>0) undo_number(C, event);
}
static void do_editmode_undohistorymenu(bContext *C, void *arg, int event)
{
Object *obedit= CTX_data_edit_object(C);
if(obedit==NULL || event<1) return;
undo_number(C, event-1);
}
uiBlock *editmode_undohistorymenu(bContext *C, ARegion *ar, void *arg_unused)
{
uiBlock *block;
UndoElem *uel;
short yco = 20, menuwidth = 120;
short item= 1;
undo_clean_stack(C); // removes other objects from it
block= uiBeginBlock(C, ar, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP);
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;
}