convert line ends
[canaan.git] / prj / cam / src / editor / brundo.c
blob437f62ec763282671fae0a1f4acf079e9dbcf34b
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/editor/brundo.c,v 1.14 2000/02/19 12:27:48 toml Exp $
7 // brush level undo code
9 #include <stdlib.h> // all the S's
10 #include <string.h>
11 #include <stdio.h>
13 #include <lg.h>
14 #include <mprintf.h>
15 #include <command.h>
17 #include <editbr.h>
18 #include <editbr_.h>
19 #include <brlist.h>
20 #include <vbrush.h>
21 //#include <gedit.h>
22 #include <ged_undo.h>
23 #include <undoredo.h>
24 #include <brundo.h>
25 #include <brquery.h>
26 #include <brinfo.h>
27 #include <editobj.h>
28 #include <memall.h>
29 #include <dbmem.h> // must be last header!
31 static BOOL editUndoInProgress=FALSE;
32 static BOOL editUndoSuspended=FALSE;
33 static editBrush start_brush;
35 extern bool set_brush_to_time_n(editBrush *br, int n);
37 //#define UNDO_STACK_TRACE
38 #ifdef UNDO_STACK_TRACE
39 #define editUndoStoreAndTraceAction(action) do { undoStoreAction(action); editUndoStackPrint(); } while (0)
40 #else
41 #define editUndoStoreAndTraceAction(action) undoStoreAction(action)
42 #endif
44 #define noUndoRedoActions (editUndoInProgress||editUndoSuspended)
46 typedef struct {
47 int type;
48 char data[];
49 } undoGeneric;
51 typedef struct {
52 int type;
53 int time;
54 editBrush total; // has the brush id inside it, of course
55 } undoBrushState;
57 typedef struct {
58 int type;
59 int data1, data2;
60 } undoData;
62 #define UNDO_BRUSH 1
63 #define UNDO_DELETE 2
64 #define UNDO_CREATE 3
65 #define MAX_UNDO_BRUSH_OP (UNDO_CREATE)
67 #define UNDO_BLOCK_ST 4
68 #define UNDO_BLOCK_END 5
69 #define UNDO_GROUP 6
71 #ifdef FANCY_DELTA_STUFF
73 // a delta structure looks like
74 // typedef struct {
75 // uchar len;
76 // uchar offset;
77 // uchar old[len];
78 // uchar new[len];
79 // } delta;
81 // the deltalist_header starts with the current total length
83 #define DELTA_HEADER_SIZE (sizeof(uchar)*2)
84 #define DELTALIST_HEADER_SIZE (sizeof(short)*1)
85 #define delta_header(len,offset)
86 #define delta_size(len) (DELTA_HEADER_SIZE+sizeof(uchar)*len)
87 #define deltalist_get_len(dl) (*((short *)dl))
89 static void *_storeDelta(void **dlist, uchar *o, uchar *n, int offset, int len)
91 uchar *ptr;
92 int deltalist_len=0;
93 if (dlist==NULL)
94 *dlist=(void *)Malloc(DELTALIST_HEADER_SIZE+delta_size(len));
95 else
97 deltalist_len=(int)deltalist_get_len(dlist);
98 *dlist=(void *)Realloc(dlist,deltalist_len+delta_size(len));
100 ptr=((uchar *)*dlist)+deltalist_len;
101 *ptr++=(uchar)len;
102 *ptr++=(uchar)offset;
103 memcpy(ptr,o+offset,len);
104 memcpy(ptr+len,n+offset,len);
105 *((short *)*dlist)=(short)deltalist_len+delta_size(len);
106 return *dlist;
109 // returns a deltalist that it has malloced
110 static void *_buildBrushDelta(editBrush *old_br, editBrush *new_br)
112 uchar *o=(uchar *)old_br, *n=(uchar *)new_br;
113 void *delta=NULL;
114 int i, missmatch=-1;
116 for (i=0; i<sizeof(editBrush); i++)
118 bool diff=(*(o+i)!=*(n+i));
119 if (diff&&(missmatch==-1))
120 missmatch=i;
121 if (!diff&&(missmatch!=-1))
123 delta=_storeDelta(&delta,o,n,missmatch,i-missmatch);
124 missmatch=-1;
127 return delta;
129 #endif
131 // this is to be called before performing an action which will modify a brush
132 // for now it saves the whole brush
133 // TODO: eventually, it should check to see if top of undo stack is this
134 // brush, and know not to bother if it is. unless your last actions was an
135 // undo, in which case it should. of course, really really it should scan
136 // the brush and record just the delta. hmmm, maybe ill do that now.
137 // so for now, full is ignored, since we always do full
138 static void *_buildBrushState(int type, editBrush *us, bool full)
140 undoBrushState *newU;
141 newU=(undoBrushState *)Malloc(sizeof(undoBrushState));
142 newU->type=type;
143 newU->time=blistCheck(us);
144 newU->total=*us;
145 return newU;
148 static void *_buildData(int type, int data1, int data2)
150 undoData *newU;
151 newU=(undoData *)Malloc(sizeof(undoData));
152 newU->type=type;
153 newU->data1=data1;
154 newU->data2=data2;
155 return newU;
158 static void *_restoreBrushState(undoGeneric *undoRec, bool create)
160 undoBrushState *lastU=(undoBrushState *)undoRec;
161 editBrush *us;
162 void *rv=NULL;
164 if (create)
166 us=brushCopy(&lastU->total);
167 us->br_id=lastU->total.br_id; // preseve brush id
169 // Undelete objects properly
171 if (brushGetType(us) == brType_OBJECT)
173 editObjCreateObjFromBrush(OBJ_NULL, us);
175 blistInsert(us); // and insert, so if we reverse it brush id will still be valid
176 blistSetPostoBrush(us);
177 set_brush_to_time_n(us, lastU->time);
178 we_switched_brush_focus();
180 else
181 { // for now, this gross hack to try and make undo/redo for texture changes more useful
183 if (lastU->total.br_id==VIRTUAL_BRUSH_ID)
185 if ((us=vBrush_UndoVBrush(&lastU->total))!=NULL)
187 rv=_buildBrushState(UNDO_BRUSH,us,FALSE);
188 *us=lastU->total;
190 else
191 Warning(("MultiBrush Undo failed\n"));
193 else if ((us=brFind(lastU->total.br_id))!=NULL)
195 BOOL tdelta=gedundo_check_texture_delta(&lastU->total,us);
196 rv=_buildBrushState(UNDO_BRUSH,us,FALSE);
197 *us=lastU->total;
198 if (tdelta)
199 gedundo_do_texture_delta(us);
201 else
202 Warning(("Cant find undo brush %d!!\n",lastU->total.br_id));
204 return rv;
207 // for now, just saves off the current brush, for use or not
208 void editUndoStoreStart(editBrush *us)
210 if (!noUndoRedoActions && (us!=NULL))
211 start_brush=*us;
214 // this is the guts of the undo thinking - which will someday be smart and sensible...
215 // till then, this hack
217 // ok, lastU->total is the brush in the current undo (ie. state we will currently undo to)
218 // start_brush is where we started this frame (often != lastU)
219 // us is the brush as it currently exists
220 // we hand them to gedit, it tells us existing thing is ok, or to save it
221 void editUndoStoreBrush(editBrush *us)
223 undoBrushState *lastU=(undoBrushState *)undoPeek();
224 editBrush *base=NULL;
226 if (noUndoRedoActions) return; // well, the easy case
227 if (lastU&&lastU->type==UNDO_BRUSH) base=&lastU->total;
228 if (gedundo_check_brush_delta(us,&start_brush,base))
230 void *newAction=_buildBrushState(UNDO_BRUSH,&start_brush,FALSE);
231 editUndoStoreAndTraceAction(newAction);
232 // mprintf("new action for brush %d record (tx %d - old %d)\n",us->br_id,us->tx_id,start_brush.tx_id);
234 // else
235 // mprintf("punt store of brush, we think it is the same\n");
238 // these both store full, so that neither needs to change for redo or anything
239 // ie. the del and create records are just backwards versions of eachother
240 // which have the same packet, and direction+type indicates which to do
241 void editUndoStoreDelete(editBrush *us)
243 if (!noUndoRedoActions)
244 editUndoStoreAndTraceAction(_buildBrushState(UNDO_DELETE,us,TRUE));
247 void editUndoStoreCreate(editBrush *us)
249 if (!noUndoRedoActions)
250 editUndoStoreAndTraceAction(_buildBrushState(UNDO_CREATE,us,TRUE));
253 // this is used to store off block starts and stops
254 // so that you can treat a set of undo's as a block
255 // and a single call to undo/redo will do all of them
256 void editUndoStoreBlock(BOOL start_of_block)
258 undoBrushState *lastU=(undoBrushState *)undoPeek();
260 if (noUndoRedoActions) return;
261 if (!start_of_block && (lastU!=NULL) && (lastU->type==UNDO_BLOCK_ST))
262 undoKillStackTop();
263 else
264 editUndoStoreAndTraceAction(_buildData(start_of_block?UNDO_BLOCK_ST:UNDO_BLOCK_END,-1,-1));
267 void editUndoStoreGroup(int old_group, int new_group)
269 if (!noUndoRedoActions)
270 editUndoStoreAndTraceAction(_buildData(UNDO_GROUP,old_group,new_group));
273 void editUndoDoGroupOp(undoData *groupOp, BOOL is_undo)
275 if (is_undo)
276 vBrush_GoToGroup(groupOp->data1);
277 else
278 vBrush_GoToGroup(groupOp->data2);
281 void editUndoSuspend(BOOL suspend)
283 editUndoSuspended=suspend;
286 static int getBrushIdfromUndo(undoGeneric *und)
288 undoBrushState *lastU=(undoBrushState *)und;
289 return lastU->total.br_id;
292 bool editUndoDoUndo(void)
294 int in_block=0;
295 editBrush *us;
296 BOOL rv=TRUE;
298 editUndoInProgress=TRUE;
299 do {
300 undoGeneric *lastUndo=(undoGeneric *)undoDoUndo();
301 if (lastUndo==NULL)
302 rv=FALSE;
303 else
304 switch (lastUndo->type)
306 case UNDO_GROUP:
307 editUndoDoGroupOp((undoData *)lastUndo,TRUE);
308 break;
309 case UNDO_BLOCK_ST:
310 in_block--;
311 break;
312 case UNDO_BLOCK_END:
313 in_block++;
314 break;
315 case UNDO_BRUSH:
316 undoUndoReplace(_restoreBrushState(lastUndo,FALSE));
317 break;
318 case UNDO_CREATE:
319 us=brFind(getBrushIdfromUndo(lastUndo));
320 vBrush_DeletePtr(us);
321 break;
322 case UNDO_DELETE:
323 _restoreBrushState(lastUndo,TRUE);
324 break;
326 } while (in_block&&rv);
327 editUndoInProgress=FALSE;
328 return rv;
331 bool editUndoDoRedo(void)
333 int in_block=0;
334 editBrush *us;
335 BOOL rv=TRUE;
337 editUndoInProgress=TRUE;
338 do {
339 undoGeneric *lastUndo=(undoGeneric *)undoDoRedo();
340 if (lastUndo==NULL)
341 rv=FALSE;
342 else
343 switch (lastUndo->type)
345 case UNDO_GROUP:
346 editUndoDoGroupOp((undoData *)lastUndo,FALSE);
347 break;
348 case UNDO_BLOCK_ST:
349 in_block++;
350 break;
351 case UNDO_BLOCK_END:
352 in_block--;
353 break;
354 case UNDO_BRUSH:
355 undoRedoReplace(_restoreBrushState(lastUndo,FALSE));
356 break;
357 case UNDO_CREATE:
358 _restoreBrushState(lastUndo,TRUE);
359 break;
360 case UNDO_DELETE:
361 us=brFind(getBrushIdfromUndo(lastUndo));
362 vBrush_DeletePtr(us);
363 break;
365 } while (in_block&&rv);
366 editUndoInProgress=FALSE;
367 return rv;
370 #ifdef DBG_ON
371 void _undostackwalkcallback(void *action, bool next)
373 undoGeneric *tmpUndo=(undoGeneric *)action;
374 mprintf("%s",next?"[":"(");
375 if (tmpUndo==NULL)
376 mprintf("NULL");
377 else if (tmpUndo->type<=MAX_UNDO_BRUSH_OP)
378 mprintf("%d %d",
379 ((undoGeneric *)action)->type,
380 ((undoBrushState *)action)->total.br_id);
381 else
382 mprintf("%d",((undoGeneric *)action)->type);
383 mprintf("%s",next?"]":")");
386 void editUndoStackPrint(void)
388 undoDumpStack(_undostackwalkcallback);
390 #endif // DBG_ON