1 /*
2 * tkUndo.c --
3 *
4 * This module provides the implementation of an undo stack.
5 *
6 * Copyright (c) 2002 by Ludwig Callewaert.
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: tkUndo.c,v 1.1 2002/06/21 23:09:55 hobbs Exp $
12 */
13
14 #include "tkUndo.h"
15 #include "tkVMacro.h"
16 /*
17 * TkUndoPushStack
18 * Push elem on the stack identified by stack.
19 *
20 * Results:
21 * None
22 *
23 * Side effects:
24 * None.
25 */
26
TkUndoPushStack(stack,elem)27 void TkUndoPushStack ( stack, elem )
28 TkUndoAtom ** stack;
29 TkUndoAtom * elem;
30 {
31 elem->next = *stack;
32 *stack = elem;
33 }
34
35 /*
36 * TkUndoPopStack --
37 * Remove and return the top element from the stack identified by
38 * stack.
39 *
40 * Results:
41 * None
42 *
43 * Side effects:
44 * None.
45 */
46
TkUndoPopStack(stack)47 TkUndoAtom * TkUndoPopStack ( stack )
48 TkUndoAtom ** stack ;
49 {
50 TkUndoAtom * elem = NULL;
51 if (*stack != NULL ) {
52 elem = *stack;
53 *stack = elem->next;
54 }
55 return elem;
56 }
57
58 /*
59 * TkUndoInsertSeparator --
60 * insert a separator on the stack, indicating a border for
61 * an undo/redo chunk.
62 *
63 * Results:
64 * None
65 *
66 * Side effects:
67 * None.
68 */
69
TkUndoInsertSeparator(stack)70 int TkUndoInsertSeparator ( stack )
71 TkUndoAtom ** stack;
72 {
73 TkUndoAtom * separator;
74
75 if ( *stack != NULL && (*stack)->type != TK_UNDO_SEPARATOR ) {
76 separator = (TkUndoAtom *) ckalloc(sizeof(TkUndoAtom));
77 separator->type = TK_UNDO_SEPARATOR;
78 TkUndoPushStack(stack,separator);
79 return 1;
80 } else {
81 return 0;
82 }
83 }
84
85 /*
86 * TkUndoClearStack --
87 * Clear an entire undo or redo stack and destroy all elements in it.
88 *
89 * Results:
90 * None
91 *
92 * Side effects:
93 * None.
94 */
95
TkUndoClearStack(stack)96 void TkUndoClearStack ( stack )
97 TkUndoAtom ** stack; /* An Undo or Redo stack */
98 {
99 TkUndoAtom * elem;
100
101 while ( (elem = TkUndoPopStack(stack)) ) {
102 if ( elem->type != TK_UNDO_SEPARATOR ) {
103 Tcl_DecrRefCount(elem->apply);
104 Tcl_DecrRefCount(elem->revert);
105 }
106 ckfree((char *)elem);
107 }
108 *stack = NULL;
109 }
110
111 /*
112 * TkUndoPushAction
113 * Push a new elem on the stack identified by stack.
114 * action and revert are given through Tcl_DStrings
115 *
116 * Results:
117 * None
118 *
119 * Side effects:
120 * None.
121 */
122
TkUndoPushAction(stack,actionScript,revertScript)123 void TkUndoPushAction ( stack, actionScript, revertScript )
124 TkUndoRedoStack * stack; /* An Undo or Redo stack */
125 Tcl_DString * actionScript; /* The script to get the action (redo) */
126 Tcl_DString * revertScript; /* The script to revert the action (undo) */
127 {
128 TkUndoAtom * atom;
129
130 atom = (TkUndoAtom *) ckalloc(sizeof(TkUndoAtom));
131 atom->type = TK_UNDO_ACTION;
132
133 atom->apply = Tcl_NewStringObj(Tcl_DStringValue(actionScript),Tcl_DStringLength(actionScript));
134 Tcl_IncrRefCount(atom->apply);
135
136 atom->revert = Tcl_NewStringObj(Tcl_DStringValue(revertScript),Tcl_DStringLength(revertScript));
137 Tcl_IncrRefCount(atom->revert);
138
139 TkUndoPushStack(&(stack->undoStack), atom);
140 TkUndoClearStack(&(stack->redoStack));
141 }
142
143
144 /*
145 * TkUndoInitStack
146 * Initialize a new undo/redo stack
147 *
148 * Results:
149 * un Undo/Redo stack pointer
150 *
151 * Side effects:
152 * None.
153 */
154
TkUndoInitStack(interp,maxdepth)155 TkUndoRedoStack * TkUndoInitStack ( interp, maxdepth )
156 Tcl_Interp * interp; /* The interpreter */
157 int maxdepth; /* The maximum stack depth */
158 {
159 TkUndoRedoStack * stack; /* An Undo/Redo stack */
160 stack = (TkUndoRedoStack *) ckalloc(sizeof(TkUndoRedoStack));
161 stack->undoStack = NULL;
162 stack->redoStack = NULL;
163 stack->interp = interp;
164 stack->maxdepth = maxdepth;
165 stack->depth = 0;
166 return stack;
167 }
168
169
170 /*
171 * TkUndoInitStack
172 * Initialize a new undo/redo stack
173 *
174 * Results:
175 * un Undo/Redo stack pointer
176 *
177 * Side effects:
178 * None.
179 */
180
TkUndoSetDepth(stack,maxdepth)181 void TkUndoSetDepth ( stack, maxdepth )
182 TkUndoRedoStack * stack; /* An Undo/Redo stack */
183 int maxdepth; /* The maximum stack depth */
184 {
185 TkUndoAtom * elem;
186 TkUndoAtom * prevelem;
187 int sepNumber = 0;
188
189 stack->maxdepth = maxdepth;
190
191 if ((stack->maxdepth > 0) && (stack->depth > stack->maxdepth)) {
192 /* Maximum stack depth exceeded. We have to remove the last compound
193 elements on the stack */
194 elem = stack->undoStack;
195 prevelem = NULL;
196 while ( sepNumber <= stack->maxdepth ) {
197 if (elem != NULL && (elem->type == TK_UNDO_SEPARATOR) ) {
198 sepNumber++;
199 }
200 prevelem = elem;
201 elem = elem->next;
202 }
203 prevelem->next = NULL;
204 while ( elem ) {
205 prevelem = elem;
206 elem = elem->next;
207 ckfree((char *) elem);
208 }
209 stack->depth = stack->maxdepth;
210 }
211 }
212
213
214 /*
215 * TkUndoClearStacks
216 * Clear both the undo and redo stack
217 *
218 * Results:
219 * None
220 *
221 * Side effects:
222 * None.
223 */
224
TkUndoClearStacks(stack)225 void TkUndoClearStacks ( stack )
226 TkUndoRedoStack * stack; /* An Undo/Redo stack */
227 {
228 TkUndoClearStack(&(stack->undoStack));
229 TkUndoClearStack(&(stack->redoStack));
230 stack->depth = 0;
231 }
232
233
234 /*
235 * TkUndoFreeStack
236 * Clear both the undo and redo stack
237 * also free the memory allocated to the u/r stack pointer
238 *
239 * Results:
240 * None
241 *
242 * Side effects:
243 * None.
244 */
245
TkUndoFreeStack(stack)246 void TkUndoFreeStack ( stack )
247 TkUndoRedoStack * stack; /* An Undo/Redo stack */
248 {
249 TkUndoClearStacks(stack);
250 /* ckfree((TkUndoRedoStack *) stack); */
251 ckfree((char *) stack);
252 }
253
254
255 /*
256 * TkUndoInsertUndoSeparator --
257 * insert a separator on the undo stack, indicating a border for
258 * an undo/redo chunk.
259 *
260 * Results:
261 * None
262 *
263 * Side effects:
264 * None.
265 */
266
TkUndoInsertUndoSeparator(stack)267 void TkUndoInsertUndoSeparator ( stack )
268 TkUndoRedoStack * stack;
269 {
270 /* TkUndoAtom * elem;
271 TkUndoAtom * prevelem;
272 int sepNumber = 0;
273 */
274
275 if ( TkUndoInsertSeparator(&(stack->undoStack)) ) {
276 ++(stack->depth);
277 TkUndoSetDepth(stack,stack->maxdepth);
278 /* if ((stack->maxdepth > 0) && (stack->depth > stack->maxdepth)) {
279 elem = stack->undoStack;
280 prevelem = NULL;
281 while ( sepNumber < stack->depth ) {
282 if (elem != NULL && (elem->type == TK_UNDO_SEPARATOR) ) {
283 sepNumber++;
284 }
285 prevelem = elem;
286 elem = elem->next;
287 }
288 prevelem->next = NULL;
289 while ( elem ) {
290 prevelem = elem;
291 elem = elem->next;
292 ckfree((char *) elem);
293 }
294 stack->depth;
295 } */
296 }
297 }
298
299
300 /*
301 * TkUndoRevert --
302 * Undo a compound action on the stack.
303 *
304 * Results:
305 * A TCL status code
306 *
307 * Side effects:
308 * None.
309 */
310
TkUndoRevert(stack)311 int TkUndoRevert ( stack )
312 TkUndoRedoStack * stack;
313 {
314 TkUndoAtom * elem;
315
316 /* insert a separator on the undo and the redo stack */
317
318 TkUndoInsertUndoSeparator(stack);
319 TkUndoInsertSeparator(&(stack->redoStack));
320
321 /* Pop and skip the first separator if there is one*/
322
323 elem = TkUndoPopStack(&(stack->undoStack));
324
325 if ( elem == NULL ) {
326 return TCL_ERROR;
327 }
328
329 if ( ( elem != NULL ) && ( elem->type == TK_UNDO_SEPARATOR ) ) {
330 ckfree((char *) elem);
331 elem = TkUndoPopStack(&(stack->undoStack));
332 }
333
334 while ( elem && (elem->type != TK_UNDO_SEPARATOR) ) {
335 Tcl_EvalObjEx(stack->interp,elem->revert,TCL_EVAL_GLOBAL);
336
337 TkUndoPushStack(&(stack->redoStack),elem);
338 elem = TkUndoPopStack(&(stack->undoStack));
339 }
340
341 /* insert a separator on the redo stack */
342
343 TkUndoInsertSeparator(&(stack->redoStack));
344
345 --(stack->depth);
346
347 return TCL_OK;
348 }
349
350
351 /*
352 * TkUndoApply --
353 * Redo a compound action on the stack.
354 *
355 * Results:
356 * A TCL status code
357 *
358 * Side effects:
359 * None.
360 */
361
TkUndoApply(stack)362 int TkUndoApply ( stack )
363 TkUndoRedoStack * stack;
364 {
365 TkUndoAtom *elem;
366
367 /* insert a separator on the undo stack */
368
369 TkUndoInsertSeparator(&(stack->undoStack));
370
371 /* Pop and skip the first separator if there is one*/
372
373 elem = TkUndoPopStack(&(stack->redoStack));
374
375 if ( elem == NULL ) {
376 return TCL_ERROR;
377 }
378
379 if ( ( elem != NULL ) && ( elem->type == TK_UNDO_SEPARATOR ) ) {
380 ckfree((char *) elem);
381 elem = TkUndoPopStack(&(stack->redoStack));
382 }
383
384 while ( elem && (elem->type != TK_UNDO_SEPARATOR) ) {
385 Tcl_EvalObjEx(stack->interp,elem->apply,TCL_EVAL_GLOBAL);
386
387 TkUndoPushStack(&(stack->undoStack), elem);
388 elem = TkUndoPopStack(&(stack->redoStack));
389 }
390
391 /* insert a separator on the undo stack */
392
393 TkUndoInsertSeparator(&(stack->undoStack));
394
395 ++(stack->depth);
396
397 return TCL_OK;
398 }
399
400
401