diff options
author | hobbs <hobbs> | 2002-06-21 23:09:53 (GMT) |
---|---|---|
committer | hobbs <hobbs> | 2002-06-21 23:09:53 (GMT) |
commit | a0edbf3ca6fa387b4197db27447f16b9c4bb58ab (patch) | |
tree | 454006e4ff105b7ca979593a68e8dc7ea1cd12f4 /generic | |
parent | dd3ada4cac8079dae041786afcc06b495e1b9354 (diff) | |
download | tk-a0edbf3ca6fa387b4197db27447f16b9c4bb58ab.zip tk-a0edbf3ca6fa387b4197db27447f16b9c4bb58ab.tar.gz tk-a0edbf3ca6fa387b4197db27447f16b9c4bb58ab.tar.bz2 |
* doc/text.n: TIP #104 implementation which generalizes the
* generic/tkText.c: undo/redo stack to not be tied solely to the
* generic/tkText.h: text widget. The APIs are still private.
* generic/tkUndo.c: This also adds a stack limiting ability and
* generic/tkUndo.h: a -maxundo option to the text widget (in
* library/text.tcl: addition to the options from TIP #26) should
* mac/tkMacDefault.h: users want to limit the undo/redo stack
* tests/text.test: (should not be necessary in most cases).
* unix/Makefile.in: [Patch #554763] (callewart)
* unix/tkUnixDefault.h:
* win/Makefile.in:
* win/makefile.vc:
* win/tkWinDefault.h:
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkText.c | 370 | ||||
-rw-r--r-- | generic/tkText.h | 37 | ||||
-rw-r--r-- | generic/tkUndo.c | 400 | ||||
-rw-r--r-- | generic/tkUndo.h | 90 |
4 files changed, 625 insertions, 272 deletions
diff --git a/generic/tkText.c b/generic/tkText.c index 2b9c60e..6d5f024 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -14,12 +14,13 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkText.c,v 1.26 2002/02/26 01:58:25 hobbs Exp $ + * RCS: @(#) $Id: tkText.c,v 1.27 2002/06/21 23:09:54 hobbs Exp $ */ #include "default.h" #include "tkPort.h" #include "tkInt.h" +#include "tkUndo.h" #ifdef MAC_TCL #define Style TkStyle @@ -90,6 +91,8 @@ static Tk_ConfigSpec configSpecs[] = { DEF_TEXT_INSERT_ON_TIME, Tk_Offset(TkText, insertOnTime), 0}, {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", DEF_TEXT_INSERT_WIDTH, Tk_Offset(TkText, insertWidth), 0}, + {TK_CONFIG_INT, "-maxundo", "maxUndo", "MaxUndo", + DEF_TEXT_MAX_UNDO, Tk_Offset(TkText, maxUndo), 0}, {TK_CONFIG_PIXELS, "-padx", "padX", "Pad", DEF_TEXT_PADX, Tk_Offset(TkText, padX), 0}, {TK_CONFIG_PIXELS, "-pady", "padY", "Pad", @@ -311,18 +314,10 @@ static void DumpLine _ANSI_ARGS_((Tcl_Interp *interp, static int DumpSegment _ANSI_ARGS_((Tcl_Interp *interp, char *key, char *value, char * command, TkTextIndex *index, int what)); -static int TextEditUndo _ANSI_ARGS_((Tcl_Interp * interp, - TkText *textPtr)); -static int TextEditRedo _ANSI_ARGS_((Tcl_Interp * interp, - TkText *textPtr)); +static int TextEditUndo _ANSI_ARGS_((TkText *textPtr)); +static int TextEditRedo _ANSI_ARGS_((TkText *textPtr)); static void TextGetText _ANSI_ARGS_((TkTextIndex * index1, TkTextIndex * index2, Tcl_DString *dsPtr)); -static void pushStack _ANSI_ARGS_(( TkTextEditAtom ** stack, - TkTextEditAtom * elem )); - -static TkTextEditAtom * popStack _ANSI_ARGS_((TkTextEditAtom ** stack)); -static void clearStack _ANSI_ARGS_((TkTextEditAtom ** stack)); -static void insertSeparator _ANSI_ARGS_((TkTextEditAtom ** stack)); static void updateDirtyFlag _ANSI_ARGS_((TkText *textPtr)); /* @@ -412,9 +407,11 @@ Tk_TextCmd(clientData, interp, argc, argv) TkTextSetYView(textPtr, &startIndex, 0); textPtr->exportSelection = 1; textPtr->pickEvent.type = LeaveNotify; + textPtr->undoStack = TkUndoInitStack(interp,0); textPtr->undo = 1; textPtr->isDirtyIncrement = 1; textPtr->autoSeparators = 1; + textPtr->lastEditMode = TK_TEXT_EDIT_OTHER; /* * Create the "sel" tag and the "current" and "insert" marks. @@ -811,8 +808,7 @@ DestroyText(memPtr) if (textPtr->bindingTable != NULL) { Tk_DeleteBindingTable(textPtr->bindingTable); } - clearStack(&(textPtr->undoStack)); - clearStack(&(textPtr->redoStack)); + TkUndoFreeStack(textPtr->undoStack); /* * NOTE: do NOT free up selBorder, selBdString, or selFgColorPtr: @@ -864,6 +860,8 @@ ConfigureText(interp, textPtr, argc, argv, flags) return TCL_ERROR; } + TkUndoSetDepth(textPtr->undoStack, textPtr->maxUndo); + /* * A few other options also need special processing, such as parsing * the geometry and setting the background from a 3-D border. @@ -1193,7 +1191,6 @@ InsertChars(textPtr, indexPtr, string) { int lineIndex, resetView, offset; TkTextIndex newTop; - TkTextEditAtom * insertion; char indexBuffer[TK_POS_CHARS]; /* @@ -1229,23 +1226,59 @@ InsertChars(textPtr, indexPtr, string) */ if ( textPtr->undo ) { - if (textPtr->autoSeparators && textPtr->undoStack && - textPtr->undoStack->type != TK_EDIT_INSERT) { - insertSeparator(&(textPtr->undoStack)); + TkTextIndex toIndex; + + Tcl_DString actionCommand; + Tcl_DString revertCommand; + + if (textPtr->autoSeparators && + textPtr->lastEditMode != TK_TEXT_EDIT_INSERT) { + TkUndoInsertUndoSeparator(textPtr->undoStack); } - insertion = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom)); - insertion->type = TK_EDIT_INSERT; + textPtr->lastEditMode = TK_TEXT_EDIT_INSERT; + Tcl_DStringInit(&actionCommand); + Tcl_DStringInit(&revertCommand); + + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," insert ",-1); TkTextPrintIndex(indexPtr,indexBuffer); - insertion->index = (char *) ckalloc(strlen(indexBuffer) + 1); - strcpy(insertion->index,indexBuffer); + Tcl_DStringAppend(&actionCommand,indexBuffer,-1); + Tcl_DStringAppend(&actionCommand," ",-1); + Tcl_DStringAppendElement(&actionCommand,string); + Tcl_DStringAppend(&actionCommand,";",-1); + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," mark set insert ",-1); + TkTextIndexForwBytes(indexPtr, (int) strlen(string), + &toIndex); + TkTextPrintIndex(&toIndex, indexBuffer); + Tcl_DStringAppend(&actionCommand,indexBuffer,-1); + Tcl_DStringAppend(&actionCommand,"; ",-1); + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," see insert",-1); - insertion->string = (char *) ckalloc(strlen(string) + 1); - strcpy(insertion->string,string); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," delete ",-1); + TkTextPrintIndex(indexPtr,indexBuffer); + Tcl_DStringAppend(&revertCommand,indexBuffer,-1); + Tcl_DStringAppend(&revertCommand," ",-1); + TkTextPrintIndex(&toIndex, indexBuffer); + Tcl_DStringAppend(&revertCommand,indexBuffer,-1); + Tcl_DStringAppend(&revertCommand," ;",-1); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," mark set insert ",-1); + TkTextPrintIndex(indexPtr,indexBuffer); + Tcl_DStringAppend(&revertCommand,indexBuffer,-1); + Tcl_DStringAppend(&revertCommand,"; ",-1); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," see insert",-1); + + TkUndoPushAction(textPtr->undoStack,&actionCommand, &revertCommand); + + Tcl_DStringFree(&actionCommand); + Tcl_DStringFree(&revertCommand); - pushStack(&(textPtr->undoStack),insertion); - clearStack(&(textPtr->redoStack)); } updateDirtyFlag(textPtr); @@ -1292,7 +1325,6 @@ DeleteChars(textPtr, index1String, index2String) { int line1, line2, line, byteIndex, resetView; TkTextIndex index1, index2; - TkTextEditAtom * deletion; char indexBuffer[TK_POS_CHARS]; /* @@ -1412,27 +1444,58 @@ DeleteChars(textPtr, index1String, index2String) if (textPtr->undo) { Tcl_DString ds; - - if (textPtr->autoSeparators && (textPtr->undoStack != NULL) - && (textPtr->undoStack->type != TK_EDIT_DELETE)) { - insertSeparator(&(textPtr->undoStack)); + Tcl_DString actionCommand; + Tcl_DString revertCommand; + + if (textPtr->autoSeparators + && (textPtr->lastEditMode != TK_TEXT_EDIT_DELETE)) { + TkUndoInsertUndoSeparator(textPtr->undoStack); } - deletion = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom)); - deletion->type = TK_EDIT_DELETE; + textPtr->lastEditMode = TK_TEXT_EDIT_DELETE; + + Tcl_DStringInit(&actionCommand); + Tcl_DStringInit(&revertCommand); - TkTextPrintIndex(&index1, indexBuffer); - deletion->index = (char *) ckalloc(strlen(indexBuffer) + 1); - strcpy(deletion->index, indexBuffer); + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," delete ",-1); + TkTextPrintIndex(&index1,indexBuffer); + Tcl_DStringAppend(&actionCommand,indexBuffer,-1); + Tcl_DStringAppend(&actionCommand," ",-1); + TkTextPrintIndex(&index2, indexBuffer); + Tcl_DStringAppend(&actionCommand,indexBuffer,-1); + Tcl_DStringAppend(&actionCommand,"; ",-1); + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," mark set insert ",-1); + TkTextPrintIndex(&index1,indexBuffer); + Tcl_DStringAppend(&actionCommand,indexBuffer,-1); + + Tcl_DStringAppend(&actionCommand,"; ",-1); + Tcl_DStringAppend(&actionCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&actionCommand," see insert",-1); TextGetText(&index1, &index2, &ds); - deletion->string = - (char *) ckalloc((unsigned int) Tcl_DStringLength(&ds) + 1); - strcpy(deletion->string, Tcl_DStringValue(&ds)); - Tcl_DStringFree(&ds); - pushStack(&(textPtr->undoStack), deletion); - clearStack(&(textPtr->redoStack)); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," insert ",-1); + TkTextPrintIndex(&index1,indexBuffer); + Tcl_DStringAppend(&revertCommand,indexBuffer,-1); + Tcl_DStringAppend(&revertCommand," ",-1); + Tcl_DStringAppendElement(&revertCommand,Tcl_DStringValue(&ds)); + Tcl_DStringAppend(&revertCommand,"; ",-1); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," mark set insert ",-1); + TkTextPrintIndex(&index2, indexBuffer); + Tcl_DStringAppend(&revertCommand,indexBuffer,-1); + Tcl_DStringAppend(&revertCommand,"; ",-1); + Tcl_DStringAppend(&revertCommand,Tcl_GetCommandName(textPtr->interp,textPtr->widgetCmd),-1); + Tcl_DStringAppend(&revertCommand," see insert",-1); + + TkUndoPushAction(textPtr->undoStack,&actionCommand, &revertCommand); + + Tcl_DStringFree(&actionCommand); + Tcl_DStringFree(&revertCommand); + } updateDirtyFlag(textPtr); @@ -2483,98 +2546,6 @@ DumpSegment(interp, key, value, command, index, what) } /* - * pushStack - * Push elem on the stack identified by stack. - * - * Results: - * None - * - * Side effects: - * None. - */ - -static void pushStack ( stack, elem ) - TkTextEditAtom ** stack; - TkTextEditAtom * elem; -{ - elem->next = *stack; - *stack = elem; -} - -/* - * popStack -- - * Remove and return the top element from the stack identified by - * stack. - * - * Results: - * None - * - * Side effects: - * None. - */ - -static TkTextEditAtom * popStack ( stack ) - TkTextEditAtom ** stack ; -{ - TkTextEditAtom * elem = NULL; - if (*stack != NULL ) { - elem = *stack; - *stack = elem->next; - } - return elem; -} - -/* - * insertSeparator -- - * insert a separator on the stack, indicating a border for - * an undo/redo chunk. - * - * Results: - * None - * - * Side effects: - * None. - */ - -static void insertSeparator ( stack ) - TkTextEditAtom ** stack; -{ - TkTextEditAtom * separator; - - if ( *stack != NULL && (*stack)->type != TK_EDIT_SEPARATOR ) { - separator = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom)); - separator->type = TK_EDIT_SEPARATOR; - pushStack(stack,separator); - } -} - -/* - * clearStack -- - * Clear an entire undo or redo stack and destroy all elements in it. - * - * Results: - * None - * - * Side effects: - * None. - */ - -static void clearStack ( stack ) - TkTextEditAtom ** stack; /* An Undo or Redo stack */ -{ - TkTextEditAtom * elem; - - while ( (elem = popStack(stack)) ) { - if ( elem->type != TK_EDIT_SEPARATOR ) { - ckfree(elem->index); - ckfree(elem->string); - } - ckfree((char *)elem); - } - *stack = NULL; -} - -/* * TextEditUndo -- * undo the last change. * @@ -2585,14 +2556,10 @@ static void clearStack ( stack ) * None. */ -static int TextEditUndo (interp,textPtr) - Tcl_Interp * interp; +static int TextEditUndo (textPtr) TkText * textPtr; /* Overall information about text widget. */ { - TkTextEditAtom * elem; - TkTextIndex fromIndex, toIndex; - char buffer[TK_POS_CHARS]; - char viewIndex[TK_POS_CHARS]; + int status; if ( ! textPtr->undo ) { return TCL_OK; @@ -2602,64 +2569,15 @@ static int TextEditUndo (interp,textPtr) textPtr->undo = 0; - /* insert a separator on the redo stack */ + /* revert one compound action */ - insertSeparator(&(textPtr->redoStack)); + status = TkUndoRevert(textPtr->undoStack); - /* Pop and skip the first separator if there is one*/ - - elem = popStack(&(textPtr->undoStack)); - - if ( elem == NULL ) { - textPtr->undo = 1; - return TCL_ERROR; - } - - if ( ( elem != NULL ) && ( elem->type == TK_EDIT_SEPARATOR ) ) { - ckfree((char *) elem); - elem = popStack(&(textPtr->undoStack)); - } - - while ( elem && (elem->type != TK_EDIT_SEPARATOR) ) { - switch ( elem->type ) { - case TK_EDIT_INSERT: - TkTextGetIndex(interp,textPtr,elem->index,&toIndex); - strcpy(viewIndex,elem->index); - TkTextIndexForwBytes(&toIndex,(int)strlen(elem->string),&toIndex); - TkTextPrintIndex(&toIndex,buffer); - textPtr->isDirtyIncrement = -1; - DeleteChars(textPtr,elem->index,buffer); - textPtr->isDirtyIncrement = 1; - break; - case TK_EDIT_DELETE: - TkTextGetIndex(interp,textPtr,elem->index,&fromIndex); - textPtr->isDirtyIncrement = -1; - InsertChars(textPtr,&fromIndex,elem->string); - TkTextIndexForwBytes(&fromIndex,(int)strlen(elem->string),&toIndex); - TkTextPrintIndex(&toIndex,viewIndex); - textPtr->isDirtyIncrement = 1; - break; - default: - return TCL_ERROR; - } - pushStack(&(textPtr->redoStack),elem); - elem = popStack(&(textPtr->undoStack)); - } - - /* view the last changed position */ - - TkTextGetIndex(interp,textPtr,viewIndex,&toIndex); - TkTextSetMark(textPtr, "insert", &toIndex); - - /* insert a separator on the undo stack */ - - insertSeparator(&(textPtr->undoStack)); - /* Turn back on the undo feature */ textPtr->undo = 1; - return TCL_OK; + return status; } /* @@ -2673,14 +2591,10 @@ static int TextEditUndo (interp,textPtr) * None. */ -static int TextEditRedo (interp,textPtr) - Tcl_Interp * interp; +static int TextEditRedo (textPtr) TkText * textPtr; /* Overall information about text widget. */ { - TkTextEditAtom *elem; - TkTextIndex fromIndex, toIndex; - char buffer[TK_POS_CHARS]; - char viewIndex[TK_POS_CHARS]; + int status; if (!textPtr->undo) { return TCL_OK; @@ -2690,62 +2604,15 @@ static int TextEditRedo (interp,textPtr) textPtr->undo = 0; - /* insert a separator on the undo stack */ - - insertSeparator(&(textPtr->undoStack)); - - /* Pop and skip the first separator if there is one*/ - - elem = popStack(&(textPtr->redoStack)); - - if ( elem == NULL ) { - textPtr->undo = 1; - return TCL_ERROR; - } - - if ( ( elem != NULL ) && ( elem->type == TK_EDIT_SEPARATOR ) ) { - ckfree((char *) elem); - elem = popStack(&(textPtr->redoStack)); - } - - while ( elem && (elem->type != TK_EDIT_SEPARATOR) ) { - switch ( elem->type ) { - case TK_EDIT_INSERT: - TkTextGetIndex(interp, textPtr, elem->index, &fromIndex); - InsertChars(textPtr, &fromIndex, elem->string); - TkTextIndexForwBytes(&fromIndex, (int) strlen(elem->string), - &toIndex); - TkTextPrintIndex(&toIndex, viewIndex); - break; - case TK_EDIT_DELETE: - TkTextGetIndex(interp, textPtr, elem->index, &toIndex); - strcpy(viewIndex, elem->index); - TkTextIndexForwBytes(&toIndex, (int) strlen(elem->string), - &toIndex); - TkTextPrintIndex(&toIndex, buffer); - DeleteChars(textPtr, elem->index, buffer); - break; - default: - return TCL_ERROR; - } - pushStack(&(textPtr->undoStack), elem); - elem = popStack(&(textPtr->redoStack)); - } - - /* view the last changed position */ + /* reapply one compound action */ - TkTextGetIndex(interp, textPtr, viewIndex, &toIndex); - TkTextSetMark(textPtr, "insert", &toIndex); - - /* insert a separator on the undo stack */ - - insertSeparator(&(textPtr->undoStack)); + status = TkUndoApply(textPtr->undoStack); /* Turn back on the undo feature */ textPtr->undo = 1; - return TCL_OK; + return status; } /* @@ -2823,7 +2690,7 @@ TextEditCmd(textPtr, interp, argc, argv) argv[0], " edit redo\"", (char *) NULL); return TCL_ERROR; } - if ( TextEditRedo(interp,textPtr) ) { + if ( TextEditRedo(textPtr) ) { Tcl_AppendResult(interp, "nothing to redo", (char *) NULL); return TCL_ERROR; } @@ -2834,22 +2701,21 @@ TextEditCmd(textPtr, interp, argc, argv) argv[0], " edit reset\"", (char *) NULL); return TCL_ERROR; } - clearStack(&(textPtr->undoStack)); - clearStack(&(textPtr->redoStack)); + TkUndoClearStacks(textPtr->undoStack); } else if ((c == 's') && (strncmp(argv[2], "separator", length) == 0)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " edit separator\"", (char *) NULL); return TCL_ERROR; } - insertSeparator(&(textPtr->undoStack)); + TkUndoInsertUndoSeparator(textPtr->undoStack); } else if ((c == 'u') && (strncmp(argv[2], "undo", length) == 0)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " edit undo\"", (char *) NULL); return TCL_ERROR; } - if ( TextEditUndo(interp,textPtr) ) { + if ( TextEditUndo(textPtr) ) { Tcl_AppendResult(interp, "nothing to undo", (char *) NULL); return TCL_ERROR; diff --git a/generic/tkText.h b/generic/tkText.h index 79e5220..0937b8f 100644 --- a/generic/tkText.h +++ b/generic/tkText.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkText.h,v 1.10 2002/01/25 21:09:37 dgp Exp $ + * RCS: @(#) $Id: tkText.h,v 1.11 2002/06/21 23:09:54 hobbs Exp $ */ #ifndef _TKTEXT @@ -20,6 +20,10 @@ #include "tk.h" #endif +#ifndef _TKUNDO +#include "tkUndo.h" +#endif + #ifdef BUILD_tk # undef TCL_STORAGE_CLASS # define TCL_STORAGE_CLASS DLLEXPORT @@ -451,24 +455,13 @@ typedef struct TkTextTabArray { * BE THE LAST IN THE STRUCTURE. */ } TkTextTabArray; -/* enum definining the types used in an edit stack */ +/* enum definining the edit modes of */ typedef enum { - TK_EDIT_SEPARATOR, /* Marker */ - TK_EDIT_INSERT, /* The undo is an insert */ - TK_EDIT_DELETE /* The undo is a delete */ -} TkTextEditType; - -/* strcut defining the basic undo/redo stack element */ - -typedef struct TkTextEditAtom { - TkTextEditType type; /* The type that will trigger the - * required action*/ - char * index; /* The starting index of the range */ - char * string; /* The text to be inserted / deleted */ - struct TkTextEditAtom * next; /* Pointer to the next element in the - * stack */ -} TkTextEditAtom; + TK_TEXT_EDIT_INSERT, /* insert mode */ + TK_TEXT_EDIT_DELETE, /* delete mode */ + TK_TEXT_EDIT_OTHER /* none of the above */ +} TkTextEditMode; /* * A data structure of the following type is kept for each text widget that @@ -649,13 +642,14 @@ typedef struct TkText { * Information related to the undo/redo functonality */ - TkTextEditAtom * undoStack; /* The undo stack */ - - TkTextEditAtom * redoStack; /* The redo stack */ + TkUndoRedoStack * undoStack; /* The undo/redo stack */ int undo; /* non zero means the undo/redo behaviour is * enabled */ + int maxUndo; /* The maximum depth of the undo stack expressed + * as the maximum number of compound statements */ + int autoSeparators; /* non zero means the separatorss will be * inserted automatically */ @@ -672,6 +666,9 @@ typedef struct TkText { * incremented every edit action */ + TkTextEditMode lastEditMode; /* Keeps track of what the last edit mode was + */ + } TkText; /* 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; +} + diff --git a/generic/tkUndo.h b/generic/tkUndo.h new file mode 100644 index 0000000..4776b3d --- /dev/null +++ b/generic/tkUndo.h @@ -0,0 +1,90 @@ +/* + * tkUndo.h -- + * + * Declarations shared among the files that implement an undo + * stack. + * + * Copyright (c) 2002 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.h,v 1.1 2002/06/21 23:09:55 hobbs Exp $ + */ + +#ifndef _TKUNDO +#define _TKUNDO + +#ifndef _TK +#include "tk.h" +#endif + +#ifdef BUILD_tk +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLEXPORT +#endif + +/* enum definining the types used in an undo stack */ + +typedef enum { + TK_UNDO_SEPARATOR, /* Marker */ + TK_UNDO_ACTION /* Command */ +} TkUndoAtomType; + +/* struct defining the basic undo/redo stack element */ + +typedef struct TkUndoAtom { + TkUndoAtomType type; /* The type that will trigger the + * required action*/ + Tcl_Obj * apply; /* Command to apply the action that was taken */ + Tcl_Obj * revert; /* The command to undo the action */ + struct TkUndoAtom * next; /* Pointer to the next element in the + * stack */ +} TkUndoAtom; + +/* struct defining the basic undo/redo stack element */ + +typedef struct TkUndoRedoStack { + TkUndoAtom * undoStack; /* The undo stack */ + TkUndoAtom * redoStack; /* The redo stack */ + Tcl_Interp * interp ; /* The interpreter in which to execute the revert and apply scripts */ + int maxdepth; + int depth; +} TkUndoRedoStack; + +/* basic functions */ + +EXTERN void TkUndoPushStack _ANSI_ARGS_((TkUndoAtom ** stack, + TkUndoAtom * elem)); + +EXTERN TkUndoAtom * TkUndoPopStack _ANSI_ARGS_((TkUndoAtom ** stack)); + +EXTERN int TkUndoInsertSeparator _ANSI_ARGS_((TkUndoAtom ** stack)); + +EXTERN void TkUndoClearStack _ANSI_ARGS_((TkUndoAtom ** stack)); + +/* functions working on an undo/redo stack */ + +EXTERN TkUndoRedoStack * TkUndoInitStack _ANSI_ARGS_((Tcl_Interp * interp, + int maxdepth)); + +EXTERN void TkUndoSetDepth _ANSI_ARGS_((TkUndoRedoStack * stack, + int maxdepth)); + +EXTERN void TkUndoClearStacks _ANSI_ARGS_((TkUndoRedoStack * stack)); + +EXTERN void TkUndoFreeStack _ANSI_ARGS_((TkUndoRedoStack * stack)); + +EXTERN void TkUndoInsertUndoSeparator _ANSI_ARGS_((TkUndoRedoStack * stack)); + +EXTERN void TkUndoPushAction _ANSI_ARGS_((TkUndoRedoStack * stack, + Tcl_DString * actionScript, Tcl_DString * revertScript)); + +EXTERN int TkUndoRevert _ANSI_ARGS_((TkUndoRedoStack * stack)); + +EXTERN int TkUndoApply _ANSI_ARGS_((TkUndoRedoStack * stack)); + +# undef TCL_STORAGE_CLASS +# define TCL_STORAGE_CLASS DLLIMPORT + +#endif /* _TKUNDO */ |