summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authorhobbs <hobbs>2002-06-21 23:09:53 (GMT)
committerhobbs <hobbs>2002-06-21 23:09:53 (GMT)
commita0edbf3ca6fa387b4197db27447f16b9c4bb58ab (patch)
tree454006e4ff105b7ca979593a68e8dc7ea1cd12f4 /generic
parentdd3ada4cac8079dae041786afcc06b495e1b9354 (diff)
downloadtk-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.c370
-rw-r--r--generic/tkText.h37
-rw-r--r--generic/tkUndo.c400
-rw-r--r--generic/tkUndo.h90
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 */