summaryrefslogtreecommitdiffstats
path: root/generic/tkText.c
diff options
context:
space:
mode:
authorhobbs <hobbs>2001-11-13 00:19:05 (GMT)
committerhobbs <hobbs>2001-11-13 00:19:05 (GMT)
commite3690695eaccf83b8076fd877d17ed591c43065d (patch)
tree65c1489fbcc7a86e2140be2d55b1ffd0ccbd2577 /generic/tkText.c
parent3027f28d1f2411602324f0964f7f1ad28742cf93 (diff)
downloadtk-e3690695eaccf83b8076fd877d17ed591c43065d.zip
tk-e3690695eaccf83b8076fd877d17ed591c43065d.tar.gz
tk-e3690695eaccf83b8076fd877d17ed591c43065d.tar.bz2
added TIP#26 text widget undo/redo functionality
Diffstat (limited to 'generic/tkText.c')
-rw-r--r--generic/tkText.c663
1 files changed, 591 insertions, 72 deletions
diff --git a/generic/tkText.c b/generic/tkText.c
index ef1ae4d..a19b984 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -14,7 +14,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.c,v 1.18 2000/11/22 01:49:38 ericm Exp $
+ * RCS: @(#) $Id: tkText.c,v 1.19 2001/11/13 00:19:05 hobbs Exp $
*/
#include "default.h"
@@ -42,6 +42,9 @@ static Tk_CustomOption stateOption = {
*/
static Tk_ConfigSpec configSpecs[] = {
+ {TK_CONFIG_BOOLEAN, "-autoseparators", "autoSeparators",
+ "AutoSeparators", DEF_TEXT_AUTO_SEPARATORS,
+ Tk_Offset(TkText, autoSeparators), 0},
{TK_CONFIG_BORDER, "-background", "background", "Background",
DEF_TEXT_BG_COLOR, Tk_Offset(TkText, border), TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_BORDER, "-background", "background", "Background",
@@ -129,6 +132,8 @@ static Tk_ConfigSpec configSpecs[] = {
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
DEF_TEXT_TAKE_FOCUS, Tk_Offset(TkText, takeFocus),
TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BOOLEAN, "-undo", "undo", "Undo",
+ DEF_TEXT_UNDO, Tk_Offset(TkText, undo), 0},
{TK_CONFIG_INT, "-width", "width", "Width",
DEF_TEXT_WIDTH, Tk_Offset(TkText, width), 0},
{TK_CONFIG_CUSTOM, "-wrap", "wrap", "Wrap",
@@ -292,6 +297,8 @@ static int TextFetchSelection _ANSI_ARGS_((ClientData clientData,
int offset, char *buffer, int maxBytes));
static int TextSearchCmd _ANSI_ARGS_((TkText *textPtr,
Tcl_Interp *interp, int argc, char **argv));
+static int TextEditCmd _ANSI_ARGS_((TkText *textPtr,
+ Tcl_Interp *interp, int argc, char **argv));
static int TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
static void TextWorldChanged _ANSI_ARGS_((
@@ -304,6 +311,19 @@ 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 char * TextGetText _ANSI_ARGS_((TkTextIndex * index1,
+ TkTextIndex * index2));
+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));
/*
* The structure below defines text class behavior by means of procedures
@@ -361,7 +381,14 @@ Tk_TextCmd(clientData, interp, argc, argv)
return TCL_ERROR;
}
+ /*
+ * Create the text widget and initialize everything to zero,
+ * then set the necessary initial (non-NULL) values.
+ */
+
textPtr = (TkText *) ckalloc(sizeof(TkText));
+ memset((VOID *) textPtr, 0, sizeof(TkText));
+
textPtr->tkwin = new;
textPtr->display = Tk_Display(new);
textPtr->interp = interp;
@@ -370,61 +397,24 @@ Tk_TextCmd(clientData, interp, argc, argv)
(ClientData) textPtr, TextCmdDeletedProc);
textPtr->tree = TkBTreeCreate(textPtr);
Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);
- textPtr->numTags = 0;
Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);
Tcl_InitHashTable(&textPtr->windowTable, TCL_STRING_KEYS);
Tcl_InitHashTable(&textPtr->imageTable, TCL_STRING_KEYS);
textPtr->state = TK_STATE_NORMAL;
- textPtr->border = NULL;
- textPtr->borderWidth = 0;
- textPtr->padX = 0;
- textPtr->padY = 0;
textPtr->relief = TK_RELIEF_FLAT;
- textPtr->highlightWidth = 0;
- textPtr->highlightBgColorPtr = NULL;
- textPtr->highlightColorPtr = NULL;
textPtr->cursor = None;
- textPtr->fgColor = NULL;
- textPtr->tkfont = NULL;
textPtr->charWidth = 1;
- textPtr->spacing1 = 0;
- textPtr->spacing2 = 0;
- textPtr->spacing3 = 0;
- textPtr->tabOptionString = NULL;
- textPtr->tabArrayPtr = NULL;
textPtr->wrapMode = TEXT_WRAPMODE_CHAR;
- textPtr->width = 0;
- textPtr->height = 0;
- textPtr->setGrid = 0;
textPtr->prevWidth = Tk_Width(new);
textPtr->prevHeight = Tk_Height(new);
TkTextCreateDInfo(textPtr);
TkTextMakeByteIndex(textPtr->tree, 0, 0, &startIndex);
TkTextSetYView(textPtr, &startIndex, 0);
- textPtr->selTagPtr = NULL;
- textPtr->selBorder = NULL;
- textPtr->selBdString = NULL;
- textPtr->selFgColorPtr = NULL;
textPtr->exportSelection = 1;
- textPtr->abortSelections = 0;
- textPtr->insertMarkPtr = NULL;
- textPtr->insertBorder = NULL;
- textPtr->insertWidth = 0;
- textPtr->insertBorderWidth = 0;
- textPtr->insertOnTime = 0;
- textPtr->insertOffTime = 0;
- textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
- textPtr->bindingTable = NULL;
- textPtr->currentMarkPtr = NULL;
textPtr->pickEvent.type = LeaveNotify;
- textPtr->pickEvent.xcrossing.x = 0;
- textPtr->pickEvent.xcrossing.y = 0;
- textPtr->numCurTags = 0;
- textPtr->curTagArrayPtr = NULL;
- textPtr->takeFocus = NULL;
- textPtr->xScrollCmd = NULL;
- textPtr->yScrollCmd = NULL;
- textPtr->flags = 0;
+ textPtr->undo = 1;
+ textPtr->isDirtyIncrement = 1;
+ textPtr->autoSeparators = 1;
/*
* Create the "sel" tag and the "current" and "insert" marks.
@@ -488,6 +478,7 @@ TextWidgetCmd(clientData, interp, argc, argv)
size_t length;
int c;
TkTextIndex index1, index2;
+ char * string;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
@@ -635,6 +626,8 @@ TextWidgetCmd(clientData, interp, argc, argv)
sprintf(buf, "%d %d %d %d %d", x, y, width, height, base);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
}
+ } else if ((c == 'e') && (strncmp(argv[1], "edit", length) == 0)) {
+ result = TextEditCmd(textPtr, interp, argc, argv);
} else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
@@ -657,32 +650,10 @@ TextWidgetCmd(clientData, interp, argc, argv)
if (TkTextIndexCmp(&index1, &index2) >= 0) {
goto done;
}
- while (1) {
- int offset, last, savedChar;
- TkTextSegment *segPtr;
-
- segPtr = TkTextIndexToSeg(&index1, &offset);
- last = segPtr->size;
- if (index1.linePtr == index2.linePtr) {
- int last2;
-
- if (index2.byteIndex == index1.byteIndex) {
- break;
- }
- last2 = index2.byteIndex - index1.byteIndex + offset;
- if (last2 < last) {
- last = last2;
- }
- }
- if (segPtr->typePtr == &tkTextCharType) {
- savedChar = segPtr->body.chars[last];
- segPtr->body.chars[last] = 0;
- Tcl_AppendResult(interp, segPtr->body.chars + offset,
- (char *) NULL);
- segPtr->body.chars[last] = savedChar;
- }
- TkTextIndexForwBytes(&index1, last-offset, &index1);
- }
+ string = TextGetText(&index1,&index2);
+ Tcl_AppendResult(interp, string, (char *) NULL);
+ ckfree(string);
+
} else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
&& (length >= 3)) {
char buf[200];
@@ -770,8 +741,8 @@ TextWidgetCmd(clientData, interp, argc, argv)
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\": must be bbox, cget, compare, configure, debug, delete, ",
- "dlineinfo, dump, get, image, index, insert, mark, scan, ",
- "search, see, tag, window, xview, or yview",
+ "dlineinfo, dump, edit, get, image, index, insert, mark, ",
+ "scan, search, see, tag, window, xview, or yview",
(char *) NULL);
result = TCL_ERROR;
}
@@ -1208,6 +1179,8 @@ InsertChars(textPtr, indexPtr, string)
{
int lineIndex, resetView, offset;
TkTextIndex newTop;
+ TkTextEditAtom * insertion;
+ char indexBuffer[TK_POS_CHARS];
/*
* Don't allow insertions on the last (dummy) line of the text.
@@ -1236,6 +1209,32 @@ InsertChars(textPtr, indexPtr, string)
}
TkTextChanged(textPtr, indexPtr, indexPtr);
TkBTreeInsertChars(indexPtr, string);
+
+ /*
+ * Push the insertion on the undo stack
+ */
+
+ if ( textPtr->undo ) {
+ if (textPtr->autoSeparators && textPtr->undoStack &&
+ textPtr->undoStack->type != INSERT) {
+ insertSeparator(&(textPtr->undoStack));
+ }
+
+ insertion = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom));
+ insertion->type = INSERT;
+
+ TkTextPrintIndex(indexPtr,indexBuffer);
+ insertion->index = (char *) ckalloc(strlen(indexBuffer) + 1);
+ strcpy(insertion->index,indexBuffer);
+
+ insertion->string = (char *) ckalloc(strlen(string) + 1);
+ strcpy(insertion->string,string);
+
+ pushStack(&(textPtr->undoStack),insertion);
+ clearStack(&(textPtr->redoStack));
+ }
+ updateDirtyFlag(textPtr);
+
if (resetView) {
TkTextMakeByteIndex(textPtr->tree, lineIndex, 0, &newTop);
TkTextIndexForwBytes(&newTop, offset, &newTop);
@@ -1279,6 +1278,8 @@ DeleteChars(textPtr, index1String, index2String)
{
int line1, line2, line, byteIndex, resetView;
TkTextIndex index1, index2;
+ TkTextEditAtom * deletion;
+ char indexBuffer[TK_POS_CHARS];
/*
* Parse the starting and stopping indices.
@@ -1390,6 +1391,31 @@ DeleteChars(textPtr, index1String, index2String)
byteIndex -= (index2.byteIndex - index1.byteIndex);
}
}
+
+ /*
+ * Push the deletion on the undo stack
+ */
+
+ if ( textPtr->undo ) {
+ if (textPtr->autoSeparators && (textPtr->undoStack != NULL)
+ && (textPtr->undoStack->type != DELETE)) {
+ insertSeparator(&(textPtr->undoStack));
+ }
+
+ deletion = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom));
+ deletion->type = DELETE;
+
+ TkTextPrintIndex(&index1,indexBuffer);
+ deletion->index = (char *) ckalloc(strlen(indexBuffer) + 1);
+ strcpy(deletion->index,indexBuffer);
+
+ deletion->string = TextGetText(&index1, &index2);
+
+ pushStack(&(textPtr->undoStack),deletion);
+ clearStack(&(textPtr->redoStack));
+ }
+ updateDirtyFlag(textPtr);
+
TkBTreeDeleteChars(&index1, &index2);
if (resetView) {
TkTextMakeByteIndex(textPtr->tree, line, byteIndex, &index1);
@@ -1569,6 +1595,7 @@ TkTextLostSelection(clientData)
ClientData clientData; /* Information about text widget. */
{
register TkText *textPtr = (TkText *) clientData;
+ XEvent event;
#ifdef ALWAYS_SHOW_SELECTION
TkTextIndex start, end;
@@ -1587,6 +1614,21 @@ TkTextLostSelection(clientData)
TkTextRedrawTag(textPtr, &start, &end, textPtr->selTagPtr, 1);
TkBTreeTag(&start, &end, textPtr->selTagPtr, 0);
#endif
+
+ /*
+ * Send an event that the selection changed. This is equivalent to
+ * "event generate $textWidget <<Selection>>"
+ */
+
+ memset((VOID *) &event, 0, sizeof(event));
+ event.xany.type = VirtualEvent;
+ event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
+ event.xany.send_event = False;
+ event.xany.window = Tk_WindowId(textPtr->tkwin);
+ event.xany.display = Tk_Display(textPtr->tkwin);
+ ((XVirtualEvent *) &event)->name = Tk_GetUid("Selection");
+ Tk_HandleEvent(&event);
+
textPtr->flags &= ~GOT_SELECTION;
}
@@ -2319,9 +2361,9 @@ DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)
offset += segPtr->size, segPtr = segPtr->nextPtr) {
if ((what & TK_DUMP_TEXT) && (segPtr->typePtr == &tkTextCharType) &&
(offset + segPtr->size > startByte)) {
- char savedChar; /* Last char used in the seg */
- int last = segPtr->size; /* Index of savedChar */
- int first = 0; /* Index of first char in seg */
+ char savedChar; /* Last char used in the seg */
+ int last = segPtr->size; /* Index of savedChar */
+ int first = 0; /* Index of first char in seg */
if (offset + segPtr->size > endByte) {
last = endByte - offset;
}
@@ -2419,4 +2461,481 @@ DumpSegment(interp, key, value, command, index, what)
return result;
}
}
+
+/*
+ * 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.
+ */
+
+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 != SEPARATOR ) {
+ separator = (TkTextEditAtom *) ckalloc(sizeof(TkTextEditAtom));
+ separator->type = 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 != SEPARATOR ) {
+ ckfree(elem->index);
+ ckfree(elem->string);
+ }
+ ckfree((char *)elem);
+ }
+ *stack = NULL;
+}
+
+/*
+ * TextEditUndo --
+ * undo the last change.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+static int TextEditUndo (interp,textPtr)
+ Tcl_Interp * interp;
+ TkText * textPtr; /* Overall information about text widget. */
+{
+ TkTextEditAtom * elem;
+ TkTextIndex fromIndex, toIndex;
+ char buffer[TK_POS_CHARS];
+ char viewIndex[TK_POS_CHARS];
+
+ if ( ! textPtr->undo ) {
+ return TCL_OK;
+ }
+
+ /* Turn off the undo feature */
+
+ textPtr->undo = 0;
+
+ /* insert a separator on the redo stack */
+
+ insertSeparator(&(textPtr->redoStack));
+
+ /* 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 == SEPARATOR ) ) {
+ ckfree((char *) elem);
+ elem = popStack(&(textPtr->undoStack));
+ }
+
+ while ( elem && (elem->type != SEPARATOR) ) {
+ switch ( elem->type ) {
+ case 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 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;
+}
+
+/*
+ * TextEditRedo --
+ * redo the last undone change.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+static int TextEditRedo (interp,textPtr)
+ Tcl_Interp * interp;
+ TkText * textPtr; /* Overall information about text widget. */
+{
+ TkTextEditAtom *elem;
+ TkTextIndex fromIndex, toIndex;
+ char buffer[TK_POS_CHARS];
+ char viewIndex[TK_POS_CHARS];
+
+ if (!textPtr->undo) {
+ return TCL_OK;
+ }
+
+ /* Turn off the undo feature temporarily */
+
+ 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 == SEPARATOR ) ) {
+ ckfree((char *) elem);
+ elem = popStack(&(textPtr->redoStack));
+ }
+
+ while ( elem && (elem->type != SEPARATOR) ) {
+ switch ( elem->type ) {
+ case INSERT:
+ TkTextGetIndex(interp, textPtr, elem->index, &fromIndex);
+ InsertChars(textPtr, &fromIndex, elem->string);
+ TkTextIndexForwBytes(&fromIndex, (int) strlen(elem->string),
+ &toIndex);
+ TkTextPrintIndex(&toIndex, viewIndex);
+ break;
+ case 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 */
+
+ 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;
+}
+
+/*
+ * TextEditCmd --
+ *
+ * Handle the subcommands to "$text edit ...".
+ * See documentation for details.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+static int
+TextEditCmd(textPtr, interp, argc, argv)
+ TkText *textPtr; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int c, setModified;
+ size_t length;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " edit option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[2][0];
+ length = strlen(argv[2]);
+ if ((c == 'm') && (strncmp(argv[2], "modified", length) == 0)) {
+ if (argc == 3) {
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(textPtr->isDirty));
+ } else if (argc != 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " edit modified ?boolean?\"", (char *) NULL);
+ return TCL_ERROR;
+ } else {
+ XEvent event;
+ if (Tcl_GetBoolean(interp, argv[3], &setModified) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Set or reset the dirty info and trigger a Modified event.
+ */
+ if (setModified) {
+ textPtr->isDirty = 1;
+ textPtr->modifiedSet = 1;
+ } else {
+ textPtr->isDirty = 0;
+ textPtr->modifiedSet = 0;
+ }
+
+ /*
+ * Send an event that the text was modified. This is equivalent to
+ * "event generate $textWidget <<Modified>>"
+ */
+
+ memset((VOID *) &event, 0, sizeof(event));
+ event.xany.type = VirtualEvent;
+ event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
+ event.xany.send_event = False;
+ event.xany.window = Tk_WindowId(textPtr->tkwin);
+ event.xany.display = Tk_Display(textPtr->tkwin);
+ ((XVirtualEvent *) &event)->name = Tk_GetUid("Modified");
+ Tk_HandleEvent(&event);
+ }
+ } else if ((c == 'r') && (strncmp(argv[2], "redo", length) == 0)
+ && (length >= 3)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " edit redo\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if ( TextEditRedo(interp,textPtr) ) {
+ Tcl_AppendResult(interp, "nothing to redo", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if ((c == 'r') && (strncmp(argv[2], "reset", length) == 0)
+ && (length >= 3)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " edit reset\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ clearStack(&(textPtr->undoStack));
+ clearStack(&(textPtr->redoStack));
+ } 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));
+ } 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) ) {
+ Tcl_AppendResult(interp, "nothing to undo",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad edit option \"", argv[2],
+ "\": must be modified, redo, reset, separator or undo",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+/*
+ * TextGetText --
+ * returns the text from indexPtr1 to indexPtr2.
+ *
+ * Results:
+ * the string between indices indexPtr1 and indexPtr2
+ *
+ * Side effects:
+ * None.
+ */
+
+char * TextGetText(indexPtr1,indexPtr2)
+ TkTextIndex * indexPtr1;
+ TkTextIndex * indexPtr2;
+{
+ TkTextIndex tmpIndex;
+ char * string;
+ string = (char *) ckalloc(1);
+ *string = '\0';
+
+ TkTextMakeByteIndex(indexPtr1->tree, TkBTreeLineIndex(indexPtr1->linePtr),
+ indexPtr1->byteIndex, &tmpIndex);
+
+ if (TkTextIndexCmp(indexPtr1, indexPtr2) >= 0) {
+ return string;
+ }
+ while (1) {
+ int offset, last, savedChar;
+ TkTextSegment *segPtr;
+
+ segPtr = TkTextIndexToSeg(&tmpIndex, &offset);
+ last = segPtr->size;
+ if (tmpIndex.linePtr == indexPtr2->linePtr) {
+ int last2;
+
+ if (indexPtr2->byteIndex == tmpIndex.byteIndex) {
+ break;
+ }
+ last2 = indexPtr2->byteIndex - tmpIndex.byteIndex + offset;
+ if (last2 < last) {
+ last = last2;
+ }
+ }
+ if (segPtr->typePtr == &tkTextCharType) {
+ savedChar = segPtr->body.chars[last];
+ segPtr->body.chars[last] = 0;
+ string = (char *) ckrealloc(string,strlen(string)
+ + strlen(segPtr->body.chars + offset) + 1);
+ strcat(string,segPtr->body.chars + offset);
+ segPtr->body.chars[last] = savedChar;
+ }
+ TkTextIndexForwBytes(&tmpIndex, last-offset, &tmpIndex);
+ }
+
+ return string;
+}
+
+/*
+ * updateDirtyFlag --
+ * increases the dirtyness of the text widget
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * None.
+ */
+
+static void updateDirtyFlag (textPtr)
+ TkText *textPtr; /* Information about text widget. */
+{
+ int oldDirtyFlag;
+
+ if (textPtr->modifiedSet) {
+ return;
+ }
+ oldDirtyFlag = textPtr->isDirty;
+ textPtr->isDirty += textPtr->isDirtyIncrement;
+ if (textPtr->isDirty == 0 || oldDirtyFlag == 0) {
+ XEvent event;
+ /*
+ * Send an event that the text was modified. This is equivalent to
+ * "event generate $textWidget <<Modified>>"
+ */
+
+ memset((VOID *) &event, 0, sizeof(event));
+ event.xany.type = VirtualEvent;
+ event.xany.serial = NextRequest(Tk_Display(textPtr->tkwin));
+ event.xany.send_event = False;
+ event.xany.window = Tk_WindowId(textPtr->tkwin);
+ event.xany.display = Tk_Display(textPtr->tkwin);
+ ((XVirtualEvent *) &event)->name = Tk_GetUid("Modified");
+ Tk_HandleEvent(&event);
+ }
+}