summaryrefslogtreecommitdiffstats
path: root/generic/tkUndo.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkUndo.c')
-rw-r--r--generic/tkUndo.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/generic/tkUndo.c b/generic/tkUndo.c
new file mode 100644
index 0000000..eb6f07b
--- /dev/null
+++ b/generic/tkUndo.c
@@ -0,0 +1,400 @@
+/*
+ * tkUndo.c --
+ *
+ * This module provides the implementation of an undo stack.
+ *
+ * Copyright (c) 2002 by Ludwig Callewaert.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tkUndo.c,v 1.1 2002/06/21 23:09:55 hobbs Exp $
+ */
+
+#include "tkUndo.h"
+
+
+/*
+ * TkUndoPushStack
+ * Push elem on the stack identified by stack.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoPushStack ( stack, elem )
+ TkUndoAtom ** stack;
+ TkUndoAtom * elem;
+{
+ elem->next = *stack;
+ *stack = elem;
+}
+
+/*
+ * TkUndoPopStack --
+ * Remove and return the top element from the stack identified by
+ * stack.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+TkUndoAtom * TkUndoPopStack ( stack )
+ TkUndoAtom ** stack ;
+{
+ TkUndoAtom * elem = NULL;
+ if (*stack != NULL ) {
+ elem = *stack;
+ *stack = elem->next;
+ }
+ return elem;
+}
+
+/*
+ * TkUndoInsertSeparator --
+ * insert a separator on the stack, indicating a border for
+ * an undo/redo chunk.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+int TkUndoInsertSeparator ( stack )
+ TkUndoAtom ** stack;
+{
+ TkUndoAtom * separator;
+
+ if ( *stack != NULL && (*stack)->type != TK_UNDO_SEPARATOR ) {
+ separator = (TkUndoAtom *) ckalloc(sizeof(TkUndoAtom));
+ separator->type = TK_UNDO_SEPARATOR;
+ TkUndoPushStack(stack,separator);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * TkUndoClearStack --
+ * Clear an entire undo or redo stack and destroy all elements in it.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoClearStack ( stack )
+ TkUndoAtom ** stack; /* An Undo or Redo stack */
+{
+ TkUndoAtom * elem;
+
+ while ( (elem = TkUndoPopStack(stack)) ) {
+ if ( elem->type != TK_UNDO_SEPARATOR ) {
+ Tcl_DecrRefCount(elem->apply);
+ Tcl_DecrRefCount(elem->revert);
+ }
+ ckfree((char *)elem);
+ }
+ *stack = NULL;
+}
+
+/*
+ * TkUndoPushAction
+ * Push a new elem on the stack identified by stack.
+ * action and revert are given through Tcl_DStrings
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoPushAction ( stack, actionScript, revertScript )
+ TkUndoRedoStack * stack; /* An Undo or Redo stack */
+ Tcl_DString * actionScript; /* The script to get the action (redo) */
+ Tcl_DString * revertScript; /* The script to revert the action (undo) */
+{
+ TkUndoAtom * atom;
+
+ atom = (TkUndoAtom *) ckalloc(sizeof(TkUndoAtom));
+ atom->type = TK_UNDO_ACTION;
+
+ atom->apply = Tcl_NewStringObj(Tcl_DStringValue(actionScript),Tcl_DStringLength(actionScript));
+ Tcl_IncrRefCount(atom->apply);
+
+ atom->revert = Tcl_NewStringObj(Tcl_DStringValue(revertScript),Tcl_DStringLength(revertScript));
+ Tcl_IncrRefCount(atom->revert);
+
+ TkUndoPushStack(&(stack->undoStack), atom);
+ TkUndoClearStack(&(stack->redoStack));
+}
+
+
+/*
+ * TkUndoInitStack
+ * Initialize a new undo/redo stack
+ *
+ * Results:
+ * un Undo/Redo stack pointer
+ *
+ * Side effects:
+ * None.
+ */
+
+TkUndoRedoStack * TkUndoInitStack ( interp, maxdepth )
+ Tcl_Interp * interp; /* The interpreter */
+ int maxdepth; /* The maximum stack depth */
+{
+ TkUndoRedoStack * stack; /* An Undo/Redo stack */
+ stack = (TkUndoRedoStack *) ckalloc(sizeof(TkUndoRedoStack));
+ stack->undoStack = NULL;
+ stack->redoStack = NULL;
+ stack->interp = interp;
+ stack->maxdepth = maxdepth;
+ stack->depth = 0;
+ return stack;
+}
+
+
+/*
+ * TkUndoInitStack
+ * Initialize a new undo/redo stack
+ *
+ * Results:
+ * un Undo/Redo stack pointer
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoSetDepth ( stack, maxdepth )
+ TkUndoRedoStack * stack; /* An Undo/Redo stack */
+ int maxdepth; /* The maximum stack depth */
+{
+ TkUndoAtom * elem;
+ TkUndoAtom * prevelem;
+ int sepNumber = 0;
+
+ stack->maxdepth = maxdepth;
+
+ if ((stack->maxdepth > 0) && (stack->depth > stack->maxdepth)) {
+ /* Maximum stack depth exceeded. We have to remove the last compound
+ elements on the stack */
+ elem = stack->undoStack;
+ prevelem = NULL;
+ while ( sepNumber <= stack->maxdepth ) {
+ if (elem != NULL && (elem->type == TK_UNDO_SEPARATOR) ) {
+ sepNumber++;
+ }
+ prevelem = elem;
+ elem = elem->next;
+ }
+ prevelem->next = NULL;
+ while ( elem ) {
+ prevelem = elem;
+ elem = elem->next;
+ ckfree((char *) elem);
+ }
+ stack->depth = stack->maxdepth;
+ }
+}
+
+
+/*
+ * TkUndoClearStacks
+ * Clear both the undo and redo stack
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoClearStacks ( stack )
+ TkUndoRedoStack * stack; /* An Undo/Redo stack */
+{
+ TkUndoClearStack(&(stack->undoStack));
+ TkUndoClearStack(&(stack->redoStack));
+ stack->depth = 0;
+}
+
+
+/*
+ * TkUndoFreeStack
+ * Clear both the undo and redo stack
+ * also free the memory allocated to the u/r stack pointer
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoFreeStack ( stack )
+ TkUndoRedoStack * stack; /* An Undo/Redo stack */
+{
+ TkUndoClearStacks(stack);
+/* ckfree((TkUndoRedoStack *) stack); */
+ ckfree((char *) stack);
+}
+
+
+/*
+ * TkUndoInsertUndoSeparator --
+ * insert a separator on the undo stack, indicating a border for
+ * an undo/redo chunk.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+void TkUndoInsertUndoSeparator ( stack )
+ TkUndoRedoStack * stack;
+{
+/* TkUndoAtom * elem;
+ TkUndoAtom * prevelem;
+ int sepNumber = 0;
+*/
+
+ if ( TkUndoInsertSeparator(&(stack->undoStack)) ) {
+ ++(stack->depth);
+ TkUndoSetDepth(stack,stack->maxdepth);
+/* if ((stack->maxdepth > 0) && (stack->depth > stack->maxdepth)) {
+ elem = stack->undoStack;
+ prevelem = NULL;
+ while ( sepNumber < stack->depth ) {
+ if (elem != NULL && (elem->type == TK_UNDO_SEPARATOR) ) {
+ sepNumber++;
+ }
+ prevelem = elem;
+ elem = elem->next;
+ }
+ prevelem->next = NULL;
+ while ( elem ) {
+ prevelem = elem;
+ elem = elem->next;
+ ckfree((char *) elem);
+ }
+ stack->depth;
+ } */
+ }
+}
+
+
+/*
+ * TkUndoRevert --
+ * Undo a compound action on the stack.
+ *
+ * Results:
+ * A TCL status code
+ *
+ * Side effects:
+ * None.
+ */
+
+int TkUndoRevert ( stack )
+ TkUndoRedoStack * stack;
+{
+ TkUndoAtom * elem;
+
+ /* insert a separator on the undo and the redo stack */
+
+ TkUndoInsertUndoSeparator(stack);
+ TkUndoInsertSeparator(&(stack->redoStack));
+
+ /* Pop and skip the first separator if there is one*/
+
+ elem = TkUndoPopStack(&(stack->undoStack));
+
+ if ( elem == NULL ) {
+ return TCL_ERROR;
+ }
+
+ if ( ( elem != NULL ) && ( elem->type == TK_UNDO_SEPARATOR ) ) {
+ ckfree((char *) elem);
+ elem = TkUndoPopStack(&(stack->undoStack));
+ }
+
+ while ( elem && (elem->type != TK_UNDO_SEPARATOR) ) {
+ Tcl_EvalObjEx(stack->interp,elem->revert,TCL_EVAL_GLOBAL);
+
+ TkUndoPushStack(&(stack->redoStack),elem);
+ elem = TkUndoPopStack(&(stack->undoStack));
+ }
+
+ /* insert a separator on the redo stack */
+
+ TkUndoInsertSeparator(&(stack->redoStack));
+
+ --(stack->depth);
+
+ return TCL_OK;
+}
+
+
+/*
+ * TkUndoApply --
+ * Redo a compound action on the stack.
+ *
+ * Results:
+ * A TCL status code
+ *
+ * Side effects:
+ * None.
+ */
+
+int TkUndoApply ( stack )
+ TkUndoRedoStack * stack;
+{
+ TkUndoAtom *elem;
+
+ /* insert a separator on the undo stack */
+
+ TkUndoInsertSeparator(&(stack->undoStack));
+
+ /* Pop and skip the first separator if there is one*/
+
+ elem = TkUndoPopStack(&(stack->redoStack));
+
+ if ( elem == NULL ) {
+ return TCL_ERROR;
+ }
+
+ if ( ( elem != NULL ) && ( elem->type == TK_UNDO_SEPARATOR ) ) {
+ ckfree((char *) elem);
+ elem = TkUndoPopStack(&(stack->redoStack));
+ }
+
+ while ( elem && (elem->type != TK_UNDO_SEPARATOR) ) {
+ Tcl_EvalObjEx(stack->interp,elem->apply,TCL_EVAL_GLOBAL);
+
+ TkUndoPushStack(&(stack->undoStack), elem);
+ elem = TkUndoPopStack(&(stack->redoStack));
+ }
+
+ /* insert a separator on the undo stack */
+
+ TkUndoInsertSeparator(&(stack->undoStack));
+
+ ++(stack->depth);
+
+ return TCL_OK;
+}
+