From ab3f233c1f2acb66be6fd409a52e396acbdf6964 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 5 Apr 2016 20:28:54 +0000 Subject: Implementation of TIP #446 - Introspect Undo/Redo Stack Depths --- generic/tkText.c | 30 ++++++++++++++++++++++--- generic/tkUndo.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- generic/tkUndo.h | 3 ++- 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index 506075d..c293557 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -2093,7 +2093,7 @@ ConfigureText( textPtr->sharedTextPtr->maxUndo = textPtr->maxUndo; textPtr->sharedTextPtr->autoSeparators = textPtr->autoSeparators; - TkUndoSetDepth(textPtr->sharedTextPtr->undoStack, + TkUndoSetMaxDepth(textPtr->sharedTextPtr->undoStack, textPtr->sharedTextPtr->maxUndo); /* @@ -5165,10 +5165,12 @@ TextEditCmd( { int index, setModified, oldModified; static const char *const editOptionStrings[] = { - "modified", "redo", "reset", "separator", "undo", NULL + "modified", "redo", "redodepth", "reset", "separator", "undo", + "undodepth", NULL }; enum editOptions { - EDIT_MODIFIED, EDIT_REDO, EDIT_RESET, EDIT_SEPARATOR, EDIT_UNDO + EDIT_MODIFIED, EDIT_REDO, EDIT_REDODEPTH, EDIT_RESET, + EDIT_SEPARATOR, EDIT_UNDO, EDIT_UNDODEPTH }; if (objc < 3) { @@ -5229,6 +5231,17 @@ TextEditCmd( return TCL_ERROR; } break; + case EDIT_REDODEPTH: { + int depth; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); + return TCL_ERROR; + } + depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 1); + Tcl_SetObjResult(interp, Tcl_NewIntObj(depth)); + break; + } case EDIT_RESET: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); @@ -5254,6 +5267,17 @@ TextEditCmd( return TCL_ERROR; } break; + case EDIT_UNDODEPTH: { + int depth; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); + return TCL_ERROR; + } + depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 0); + Tcl_SetObjResult(interp, Tcl_NewIntObj(depth)); + break; + } } return TCL_OK; } diff --git a/generic/tkUndo.c b/generic/tkUndo.c index 8359e0a..3aa3ee2 100644 --- a/generic/tkUndo.c +++ b/generic/tkUndo.c @@ -353,7 +353,7 @@ TkUndoInitStack( /* *---------------------------------------------------------------------- * - * TkUndoSetDepth -- + * TkUndoSetMaxDepth -- * * Set the maximum depth of stack. * @@ -368,7 +368,7 @@ TkUndoInitStack( */ void -TkUndoSetDepth( +TkUndoSetMaxDepth( TkUndoRedoStack *stack, /* An Undo/Redo stack */ int maxdepth) /* The maximum stack depth */ { @@ -428,6 +428,67 @@ TkUndoSetDepth( /* *---------------------------------------------------------------------- * + * TkUndoGetDepth + * + * Return the depth of the undo (or redo) stack. + * + * Results: + * An integer representing the number of undoable (or redoable) actions. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkUndoGetDepth( + TkUndoRedoStack *stack, /* An Undo/Redo stack */ + int whichStack) /* 0 means the undo stack, + * otherwise the redo stack */ +{ + int depth = 0; + TkUndoAtom *elem = NULL; + + if (stack != NULL) { + if (whichStack) { + elem = stack->redoStack; + } else { + elem = stack->undoStack; + } + + if (elem != NULL) { + /* + * Skip the first (top) separator if there is one. + */ + + if (elem->type == TK_UNDO_SEPARATOR) { + elem = elem->next; + } + + /* + * The number of compound actions in the stack is then + * the number of separators plus one, except if there is + * a separator at the bottom of the stack. This latter + * case cannot however happen (TkUndoInsertSeparator + * prevents from inserting a separator there). + */ + + while (elem != NULL) { + if (elem->type == TK_UNDO_SEPARATOR) { + depth++; + } + elem = elem->next; + } + depth++; + } + } + return depth; +} + +/* + *---------------------------------------------------------------------- + * * TkUndoClearStacks -- * * Clear both the undo and redo stack. @@ -498,7 +559,7 @@ TkUndoInsertUndoSeparator( { if (TkUndoInsertSeparator(&stack->undoStack)) { stack->depth++; - TkUndoSetDepth(stack, stack->maxdepth); + TkUndoSetMaxDepth(stack, stack->maxdepth); } } diff --git a/generic/tkUndo.h b/generic/tkUndo.h index e63aac4..883f6ee 100644 --- a/generic/tkUndo.h +++ b/generic/tkUndo.h @@ -96,7 +96,8 @@ MODULE_SCOPE void TkUndoClearStack(TkUndoAtom **stack); */ MODULE_SCOPE TkUndoRedoStack *TkUndoInitStack(Tcl_Interp *interp, int maxdepth); -MODULE_SCOPE void TkUndoSetDepth(TkUndoRedoStack *stack, int maxdepth); +MODULE_SCOPE void TkUndoSetMaxDepth(TkUndoRedoStack *stack, int maxdepth); +MODULE_SCOPE int TkUndoGetDepth(TkUndoRedoStack *stack, int whichStack); MODULE_SCOPE void TkUndoClearStacks(TkUndoRedoStack *stack); MODULE_SCOPE void TkUndoFreeStack(TkUndoRedoStack *stack); MODULE_SCOPE void TkUndoInsertUndoSeparator(TkUndoRedoStack *stack); -- cgit v0.12 From f617c83058bb40a68dae5c9834acdda18b534de8 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 7 Apr 2016 19:32:29 +0000 Subject: [.t edit undodepth/redodepth] return 0 when -undo false --- generic/tkText.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index c293557..3114835 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -5232,13 +5232,15 @@ TextEditCmd( } break; case EDIT_REDODEPTH: { - int depth; + int depth = 0; if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 1); + if (textPtr->sharedTextPtr->undo) { + depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 1); + } Tcl_SetObjResult(interp, Tcl_NewIntObj(depth)); break; } @@ -5268,13 +5270,15 @@ TextEditCmd( } break; case EDIT_UNDODEPTH: { - int depth; + int depth = 0; if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 0); + if (textPtr->sharedTextPtr->undo) { + depth = TkUndoGetDepth(textPtr->sharedTextPtr->undoStack, 0); + } Tcl_SetObjResult(interp, Tcl_NewIntObj(depth)); break; } -- cgit v0.12 From b8b29378dcd5a89e371ac9bd1ae4737d08379a46 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 7 Apr 2016 19:50:31 +0000 Subject: Documentation for [.t edit undodepth/redodepth] --- doc/text.n | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/text.n b/doc/text.n index cf516f4..e38b4f1 100644 --- a/doc/text.n +++ b/doc/text.n @@ -1295,6 +1295,12 @@ When the \fB\-undo\fR option is true, reapplies the last undone edits provided no other edits were done since then. Generates an error when the redo stack is empty. Does nothing when the \fB\-undo\fR option is false. .TP +\fIpathName \fBedit redodepth\fR +. +Returns the depth of the redo stack (number of redoable actions). When this is +zero there is nothing to redo. When the \fB\-undo\fR option is false zero is +returned. +.TP \fIpathName \fBedit reset\fR . Clears the undo and redo stacks. @@ -1310,6 +1316,12 @@ Undoes the last edit action when the \fB\-undo\fR option is true. An edit action is defined as all the insert and delete commands that are recorded on the undo stack in between two separators. Generates an error when the undo stack is empty. Does nothing when the \fB\-undo\fR option is false. +.TP +\fIpathName \fBedit undodepth\fR +. +Returns the depth of the undo stack (number of undoable actions). When this is +zero there is nothing to undo. When the \fB\-undo\fR option is false zero is +returned. .RE .TP \fIpathName \fBget\fR ?\fB\-displaychars\fR? ?\fB\-\-\fR? \fIindex1\fR ?\fIindex2 ...\fR? -- cgit v0.12 From a49d18f50fd597f7b77eae71f3df70469136f839 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 7 Apr 2016 20:18:24 +0000 Subject: Tests for [.t edit undodepth/redodepth] --- tests/text.test | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/text.test b/tests/text.test index a778b79..da246f9 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6188,7 +6188,7 @@ test text-27.2 {TextEditCmd procedure, argument parsing} -body { .t edit gorp } -cleanup { destroy .t -} -returnCodes {error} -result {bad edit option "gorp": must be modified, redo, reset, separator, or undo} +} -returnCodes {error} -result {bad edit option "gorp": must be modified, redo, redodepth, reset, separator, undo, or undodepth} test text-27.3 {TextEditUndo procedure, undoing changes} -body { text .t -undo 1 pack .t @@ -6456,6 +6456,32 @@ test text-27.22 {patch 1669632 (v) - <> is atomic} -setup { } -cleanup { destroy .top.t .top } -result "This A an example text" +test text-27.24 {TextEditCmd procedure, undo and redo stack depths} -setup { + destroy .t + set res {} +} -body { + text .t -undo false -autoseparators false + lappend res [.t edit undodepth] [.t edit redodepth] + .t configure -undo true + lappend res [.t edit undodepth] [.t edit redodepth] + .t insert end "DO\n" + .t edit separator + .t insert end "IT\n" + .t insert end "YOURSELF\n" + .t edit separator + lappend res [.t edit undodepth] [.t edit redodepth] + .t edit undo + lappend res [.t edit undodepth] [.t edit redodepth] + .t configure -undo false + lappend res [.t edit undodepth] [.t edit redodepth] + .t configure -undo true + lappend res [.t edit undodepth] [.t edit redodepth] + .t edit redo + lappend res [.t edit undodepth] [.t edit redodepth] +} -cleanup { + destroy .t +} -result {0 0 0 0 2 0 1 1 0 0 1 1 2 0} + test text-28.1 {bug fix - 624372, ControlUtfProc long lines} -body { pack [text .t -wrap none] -- cgit v0.12 From 603eb20ad00d84cd8494a90826c37edc71916e97 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 20:26:20 +0000 Subject: Implementation of TIP #446 - Introspect Undo/Redo Stack --- generic/tkText.c | 34 +++++++++++++++++++++++--- generic/tkUndo.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- generic/tkUndo.h | 4 +++- 3 files changed, 103 insertions(+), 7 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index 3e8d625..4a352b2 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -2093,7 +2093,7 @@ ConfigureText( textPtr->sharedTextPtr->maxUndo = textPtr->maxUndo; textPtr->sharedTextPtr->autoSeparators = textPtr->autoSeparators; - TkUndoSetDepth(textPtr->sharedTextPtr->undoStack, + TkUndoSetMaxDepth(textPtr->sharedTextPtr->undoStack, textPtr->sharedTextPtr->maxUndo); /* @@ -5156,10 +5156,12 @@ TextEditCmd( { int index, setModified, oldModified; static const char *const editOptionStrings[] = { - "modified", "redo", "reset", "separator", "undo", NULL + "canundo", "canredo", "modified", "redo", "reset", "separator", + "undo", NULL }; enum editOptions { - EDIT_MODIFIED, EDIT_REDO, EDIT_RESET, EDIT_SEPARATOR, EDIT_UNDO + EDIT_CANUNDO, EDIT_CANREDO, EDIT_MODIFIED, EDIT_REDO, EDIT_RESET, + EDIT_SEPARATOR, EDIT_UNDO }; if (objc < 3) { @@ -5173,6 +5175,32 @@ TextEditCmd( } switch ((enum editOptions) index) { + case EDIT_CANREDO: { + int canRedo = 0; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); + return TCL_ERROR; + } + if (textPtr->sharedTextPtr->undo) { + canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(canRedo)); + break; + } + case EDIT_CANUNDO: { + int canUndo = 0; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 3, objv, NULL); + return TCL_ERROR; + } + if (textPtr->sharedTextPtr->undo) { + canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(canUndo)); + break; + } case EDIT_MODIFIED: if (objc == 3) { Tcl_SetObjResult(interp, diff --git a/generic/tkUndo.c b/generic/tkUndo.c index 8359e0a..5934154 100644 --- a/generic/tkUndo.c +++ b/generic/tkUndo.c @@ -353,7 +353,7 @@ TkUndoInitStack( /* *---------------------------------------------------------------------- * - * TkUndoSetDepth -- + * TkUndoSetMaxDepth -- * * Set the maximum depth of stack. * @@ -368,7 +368,7 @@ TkUndoInitStack( */ void -TkUndoSetDepth( +TkUndoSetMaxDepth( TkUndoRedoStack *stack, /* An Undo/Redo stack */ int maxdepth) /* The maximum stack depth */ { @@ -478,6 +478,72 @@ TkUndoFreeStack( /* *---------------------------------------------------------------------- * + * TkUndoCanRedo -- + * + * Returns true if redo is possible, i.e. if the redo stack is not empty. + * + * Results: + * A boolean. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkUndoCanRedo( + TkUndoRedoStack *stack) /* An Undo/Redo stack */ +{ + int canRedo = 0; + TkUndoAtom *elem = stack->redoStack; + + while (elem != NULL) { + if (elem->type != TK_UNDO_SEPARATOR) { + canRedo = 1; + break; + } + elem = elem->next; + } + return canRedo; +} + +/* + *---------------------------------------------------------------------- + * + * TkUndoCanUndo -- + * + * Returns true if undo is possible, i.e. if the undo stack is not empty. + * + * Results: + * A boolean. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkUndoCanUndo( + TkUndoRedoStack *stack) /* An Undo/Redo stack */ +{ + int canUndo = 0; + TkUndoAtom *elem = stack->undoStack; + + while (elem != NULL) { + if (elem->type != TK_UNDO_SEPARATOR) { + canUndo = 1; + break; + } + elem = elem->next; + } + return canUndo; +} + +/* + *---------------------------------------------------------------------- + * * TkUndoInsertUndoSeparator -- * * Insert a separator on the undo stack, indicating a border for an @@ -498,7 +564,7 @@ TkUndoInsertUndoSeparator( { if (TkUndoInsertSeparator(&stack->undoStack)) { stack->depth++; - TkUndoSetDepth(stack, stack->maxdepth); + TkUndoSetMaxDepth(stack, stack->maxdepth); } } diff --git a/generic/tkUndo.h b/generic/tkUndo.h index e63aac4..490ede9 100644 --- a/generic/tkUndo.h +++ b/generic/tkUndo.h @@ -96,9 +96,11 @@ MODULE_SCOPE void TkUndoClearStack(TkUndoAtom **stack); */ MODULE_SCOPE TkUndoRedoStack *TkUndoInitStack(Tcl_Interp *interp, int maxdepth); -MODULE_SCOPE void TkUndoSetDepth(TkUndoRedoStack *stack, int maxdepth); +MODULE_SCOPE void TkUndoSetMaxDepth(TkUndoRedoStack *stack, int maxdepth); MODULE_SCOPE void TkUndoClearStacks(TkUndoRedoStack *stack); MODULE_SCOPE void TkUndoFreeStack(TkUndoRedoStack *stack); +MODULE_SCOPE int TkUndoCanRedo(TkUndoRedoStack *stack); +MODULE_SCOPE int TkUndoCanUndo(TkUndoRedoStack *stack); MODULE_SCOPE void TkUndoInsertUndoSeparator(TkUndoRedoStack *stack); MODULE_SCOPE TkUndoSubAtom *TkUndoMakeCmdSubAtom(Tcl_Command command, Tcl_Obj *actionScript, TkUndoSubAtom *subAtomList); -- cgit v0.12 From a1fa144f468ecc214ff241aa6c61d2dbee1e3f82 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 20:32:12 +0000 Subject: Documentation for [.t edit canundo/canredo] --- doc/text.n | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/text.n b/doc/text.n index 9ec8f74..c788fa5 100644 --- a/doc/text.n +++ b/doc/text.n @@ -1282,6 +1282,16 @@ behavior of the command depends on the \fIoption\fR argument that follows the supported: .RS .TP +\fIpathName \fBedit canredo\fR +. +Returns a boolean true if redo is possible, i.e. when the redo stack is not +empty. Otherwise returns false. +.TP +\fIpathName \fBedit canundo\fR +. +Returns a boolean true if undo is possible, i.e. when the undo stack is not +empty. Otherwise returns false. +.TP \fIpathName \fBedit modified \fR?\fIboolean\fR? . If \fIboolean\fR is not specified, returns the modified flag of the widget. -- cgit v0.12 From 561dec77f0234df7380ae28f6c22e386ba08d63b Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 20:37:11 +0000 Subject: Tests for [.t edit canundo/canredo] --- tests/text.test | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/text.test b/tests/text.test index f217bcf..9be243d 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6207,7 +6207,7 @@ test text-27.2 {TextEditCmd procedure, argument parsing} -body { .t edit gorp } -cleanup { destroy .t -} -returnCodes {error} -result {bad edit option "gorp": must be modified, redo, reset, separator, or undo} +} -returnCodes {error} -result {bad edit option "gorp": must be canundo, canredo, modified, redo, reset, separator, or undo} test text-27.3 {TextEditUndo procedure, undoing changes} -body { text .t -undo 1 pack .t @@ -6494,6 +6494,32 @@ test text-27.22 {patch 1669632 (v) - <> is atomic} -setup { } -cleanup { destroy .top.t .top } -result "This A an example text" +test text-27.24 {TextEditCmd procedure, canundo and canredo} -setup { + destroy .t + set res {} +} -body { + text .t -undo false -autoseparators false + lappend res [.t edit canundo] [.t edit canredo] + .t configure -undo true + lappend res [.t edit canundo] [.t edit canredo] + .t insert end "DO\n" + .t edit separator + .t insert end "IT\n" + .t insert end "YOURSELF\n" + .t edit separator + lappend res [.t edit canundo] [.t edit canredo] + .t edit undo + lappend res [.t edit canundo] [.t edit canredo] + .t configure -undo false + lappend res [.t edit canundo] [.t edit canredo] + .t configure -undo true + lappend res [.t edit canundo] [.t edit canredo] + .t edit redo + lappend res [.t edit canundo] [.t edit canredo] +} -cleanup { + destroy .t +} -result {0 0 0 0 1 0 1 1 0 0 1 1 1 0} + test text-28.1 {bug fix - 624372, ControlUtfProc long lines} -body { pack [text .t -wrap none] -- cgit v0.12 From 4c0630f07e5f4720094070cdd78a0a58c5696aad Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:12:30 +0000 Subject: Added <> event, triggering when either the undo stack or the redo stack becomes empty or unempty --- generic/tkText.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index 4a352b2..5522154 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -401,6 +401,7 @@ static Tcl_Obj * TextGetText(const TkText *textPtr, const TkTextIndex *index1, const TkTextIndex *index2, int visibleOnly); static void GenerateModifiedEvent(TkText *textPtr); +static void GenerateUndoStackEvent(TkText *textPtr); static void UpdateDirtyFlag(TkSharedText *sharedPtr); static void RunAfterSyncCmd(ClientData clientData); static void TextPushUndoAction(TkText *textPtr, @@ -2769,6 +2770,7 @@ TextPushUndoAction( /* Index describing second location. */ { TkUndoSubAtom *iAtom, *dAtom; + TkUndoAtom *redoElem, *undoElem; /* * Create the helpers. @@ -2855,6 +2857,9 @@ TextPushUndoAction( Tcl_DecrRefCount(index1Obj); Tcl_DecrRefCount(index2Obj); + undoElem = textPtr->sharedTextPtr->undoStack->undoStack; + redoElem = textPtr->sharedTextPtr->undoStack->redoStack; + /* * Depending whether the action is to insert or delete, we provide the * appropriate second and third arguments to TkUndoPushAction. (The first @@ -2866,6 +2871,10 @@ TextPushUndoAction( } else { TkUndoPushAction(textPtr->sharedTextPtr->undoStack, dAtom, iAtom); } + + if (undoElem == NULL || redoElem != NULL) { + GenerateUndoStackEvent(textPtr); + } } /* @@ -5155,6 +5164,10 @@ TextEditCmd( Tcl_Obj *const objv[]) /* Argument objects. */ { int index, setModified, oldModified; + int canRedo = 0; + int canUndo = 0; + TkUndoAtom *redoElem, *undoElem; + static const char *const editOptionStrings[] = { "canundo", "canredo", "modified", "redo", "reset", "separator", "undo", NULL @@ -5175,9 +5188,7 @@ TextEditCmd( } switch ((enum editOptions) index) { - case EDIT_CANREDO: { - int canRedo = 0; - + case EDIT_CANREDO: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; @@ -5187,10 +5198,7 @@ TextEditCmd( } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(canRedo)); break; - } - case EDIT_CANUNDO: { - int canUndo = 0; - + case EDIT_CANUNDO: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; @@ -5200,7 +5208,6 @@ TextEditCmd( } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(canUndo)); break; - } case EDIT_MODIFIED: if (objc == 3) { Tcl_SetObjResult(interp, @@ -5241,22 +5248,32 @@ TextEditCmd( } break; case EDIT_REDO: - if (objc != 3) { + if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - if (TextEditRedo(textPtr)) { + undoElem = textPtr->sharedTextPtr->undoStack->undoStack; + if (TextEditRedo(textPtr)) { Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to redo", -1)); Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_REDO", NULL); return TCL_ERROR; } + redoElem = textPtr->sharedTextPtr->undoStack->redoStack; + if (undoElem == NULL || redoElem == NULL) { + GenerateUndoStackEvent(textPtr); + } break; case EDIT_RESET: if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } + undoElem = textPtr->sharedTextPtr->undoStack->undoStack; + redoElem = textPtr->sharedTextPtr->undoStack->redoStack; TkUndoClearStacks(textPtr->sharedTextPtr->undoStack); + if (undoElem != NULL || redoElem != NULL) { + GenerateUndoStackEvent(textPtr); + } break; case EDIT_SEPARATOR: if (objc != 3) { @@ -5266,15 +5283,20 @@ TextEditCmd( TkUndoInsertUndoSeparator(textPtr->sharedTextPtr->undoStack); break; case EDIT_UNDO: - if (objc != 3) { + if (objc != 3) { Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } + redoElem = textPtr->sharedTextPtr->undoStack->redoStack; if (TextEditUndo(textPtr)) { Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to undo", -1)); Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_UNDO", NULL); return TCL_ERROR; } + undoElem = textPtr->sharedTextPtr->undoStack->undoStack; + if (redoElem == NULL || undoElem == NULL) { + GenerateUndoStackEvent(textPtr); + } break; } return TCL_OK; @@ -5390,6 +5412,36 @@ GenerateModifiedEvent( /* *---------------------------------------------------------------------- * + * GenerateUndoStackEvent -- + * + * Send an event that the undo or redo stack became empty or unempty. + * This is equivalent to: + * event generate $textWidget <> + * for all peers of $textWidget. + * + * Results: + * None + * + * Side effects: + * May force the text window (and all peers) into existence. + * + *---------------------------------------------------------------------- + */ + +static void +GenerateUndoStackEvent( + TkText *textPtr) /* Information about text widget. */ +{ + for (textPtr = textPtr->sharedTextPtr->peers; textPtr != NULL; + textPtr = textPtr->next) { + Tk_MakeWindowExist(textPtr->tkwin); + TkSendVirtualEvent(textPtr->tkwin, "UndoStack", NULL); + } +} + +/* + *---------------------------------------------------------------------- + * * UpdateDirtyFlag -- * * Updates the dirtyness of the text widget -- cgit v0.12 From b4f74086bc18e8d816af1d49606295c975420241 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:22:57 +0000 Subject: Documented the <> event --- doc/event.n | 4 ++++ doc/text.n | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/doc/event.n b/doc/event.n index 045339e..54ad42e 100644 --- a/doc/event.n +++ b/doc/event.n @@ -352,6 +352,10 @@ user-driven .QW "tab to widget" action. .TP +\fB<>\fR +This is sent to a text widget when its undo stack or redo stack becomes +empty or unempty. +.TP \fB<>\fR This is sent to a text widget when its internal data become obsolete, and again when these internal data are back in sync with the widget diff --git a/doc/text.n b/doc/text.n index c788fa5..96e7ccc 100644 --- a/doc/text.n +++ b/doc/text.n @@ -897,6 +897,10 @@ separator is already present at the top of the undo stack no other will be inserted. That means that two separators on the undo stack are always separated by at least one insert or delete action. .PP +The \fB<>\fR virtual event is generated every time the undo stack +or the redo stack becomes empty or unempty. This event is generated once for +each peer widget. +.PP The undo mechanism is also linked to the modified flag. This means that undoing or redoing changes can take a modified text widget back to the unmodified state or vice versa. The modified flag will be set automatically to -- cgit v0.12 From 7a226093c601aa9aed761133e3a03a3b98d839a4 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:24:43 +0000 Subject: Removed useless precision regarding the <> event --- doc/text.n | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/text.n b/doc/text.n index 96e7ccc..e2bb01f 100644 --- a/doc/text.n +++ b/doc/text.n @@ -898,8 +898,7 @@ inserted. That means that two separators on the undo stack are always separated by at least one insert or delete action. .PP The \fB<>\fR virtual event is generated every time the undo stack -or the redo stack becomes empty or unempty. This event is generated once for -each peer widget. +or the redo stack becomes empty or unempty. .PP The undo mechanism is also linked to the modified flag. This means that undoing or redoing changes can take a modified text widget back to the -- cgit v0.12 From 496aa58fc7b28242708064b4bd4f75a055ded9e2 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:35:47 +0000 Subject: Added test for the <> event --- tests/text.test | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/text.test b/tests/text.test index 9be243d..0ec69d0 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6519,6 +6519,49 @@ test text-27.24 {TextEditCmd procedure, canundo and canredo} -setup { } -cleanup { destroy .t } -result {0 0 0 0 1 0 1 1 0 0 1 1 1 0} +test text-27.25 {<> virtual event} -setup { + destroy .t + set res {} + set nbUS 0 +} -body { + text .t -undo false -autoseparators false + bind .t <> {incr nbUS} + update ; lappend res $nbUS + .t configure -undo true + update ; lappend res $nbUS + .t insert end "DO\n" + .t edit separator + .t insert end "IT\n" + .t insert end "YOURSELF\n" + .t edit separator + .t insert end "MAN\n" + .t edit separator + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit redo + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit redo + update ; lappend res $nbUS + .t edit redo + update ; lappend res $nbUS + .t edit redo + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit undo + update ; lappend res $nbUS + .t edit reset + update ; lappend res $nbUS +} -cleanup { + destroy .t +} -result {0 0 1 2 3 4 4 5 6 6 7 8 8 9} test text-28.1 {bug fix - 624372, ControlUtfProc long lines} -body { -- cgit v0.12 From 285b73d3427cf1e29de1ddc1a6ee2a3a138901a6 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:42:49 +0000 Subject: Aligned GenerateModifiedEvent() on GenerateUndoStackEvent() regarding generation of the event for each peer --- generic/tkText.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index 5522154..a9b7527 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -5241,10 +5241,7 @@ TextEditCmd( */ if ((!oldModified) != (!setModified)) { - for (textPtr = textPtr->sharedTextPtr->peers; textPtr != NULL; - textPtr = textPtr->next) { - GenerateModifiedEvent(textPtr); - } + GenerateModifiedEvent(textPtr); } break; case EDIT_REDO: @@ -5391,7 +5388,8 @@ TextGetText( * * Send an event that the text was modified. This is equivalent to: * event generate $textWidget <> - * + * for all peers of $textWidget. +* * Results: * None * @@ -5405,8 +5403,11 @@ static void GenerateModifiedEvent( TkText *textPtr) /* Information about text widget. */ { - Tk_MakeWindowExist(textPtr->tkwin); - TkSendVirtualEvent(textPtr->tkwin, "Modified", NULL); + for (textPtr = textPtr->sharedTextPtr->peers; textPtr != NULL; + textPtr = textPtr->next) { + Tk_MakeWindowExist(textPtr->tkwin); + TkSendVirtualEvent(textPtr->tkwin, "Modified", NULL); + } } /* @@ -5460,7 +5461,6 @@ UpdateDirtyFlag( TkSharedText *sharedTextPtr)/* Information about text widget. */ { int oldDirtyFlag; - TkText *textPtr; /* * If we've been forced to be dirty, we stay dirty (until explicitly @@ -5491,10 +5491,7 @@ UpdateDirtyFlag( } if (sharedTextPtr->isDirty == 0 || oldDirtyFlag == 0) { - for (textPtr = sharedTextPtr->peers; textPtr != NULL; - textPtr = textPtr->next) { - GenerateModifiedEvent(textPtr); - } + GenerateModifiedEvent(sharedTextPtr->peers); } } -- cgit v0.12 From d1ed9566df0379d64ce1bcbeb9cf4d6ce5054dc1 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 17 May 2016 20:00:20 +0000 Subject: Took comments from Koen Danckaert into account (with my thanks) to simplify the implementation of TkUndoCanUndo() and TkUndoCanRedo() in tkUndo.c, and in tkText.c to remove direct calls to internals of the undo or redo stack --- generic/tkText.c | 27 +++++++++++++-------------- generic/tkUndo.c | 24 ++---------------------- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index a9b7527..b610844 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -2770,7 +2770,7 @@ TextPushUndoAction( /* Index describing second location. */ { TkUndoSubAtom *iAtom, *dAtom; - TkUndoAtom *redoElem, *undoElem; + int canUndo, canRedo; /* * Create the helpers. @@ -2857,8 +2857,8 @@ TextPushUndoAction( Tcl_DecrRefCount(index1Obj); Tcl_DecrRefCount(index2Obj); - undoElem = textPtr->sharedTextPtr->undoStack->undoStack; - redoElem = textPtr->sharedTextPtr->undoStack->redoStack; + canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); + canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); /* * Depending whether the action is to insert or delete, we provide the @@ -2872,7 +2872,7 @@ TextPushUndoAction( TkUndoPushAction(textPtr->sharedTextPtr->undoStack, dAtom, iAtom); } - if (undoElem == NULL || redoElem != NULL) { + if (!canUndo || canRedo) { GenerateUndoStackEvent(textPtr); } } @@ -5166,7 +5166,6 @@ TextEditCmd( int index, setModified, oldModified; int canRedo = 0; int canUndo = 0; - TkUndoAtom *redoElem, *undoElem; static const char *const editOptionStrings[] = { "canundo", "canredo", "modified", "redo", "reset", "separator", @@ -5249,14 +5248,14 @@ TextEditCmd( Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - undoElem = textPtr->sharedTextPtr->undoStack->undoStack; + canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); if (TextEditRedo(textPtr)) { Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to redo", -1)); Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_REDO", NULL); return TCL_ERROR; } - redoElem = textPtr->sharedTextPtr->undoStack->redoStack; - if (undoElem == NULL || redoElem == NULL) { + canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); + if (!canUndo || !canRedo) { GenerateUndoStackEvent(textPtr); } break; @@ -5265,10 +5264,10 @@ TextEditCmd( Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - undoElem = textPtr->sharedTextPtr->undoStack->undoStack; - redoElem = textPtr->sharedTextPtr->undoStack->redoStack; + canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); + canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); TkUndoClearStacks(textPtr->sharedTextPtr->undoStack); - if (undoElem != NULL || redoElem != NULL) { + if (canUndo || canRedo) { GenerateUndoStackEvent(textPtr); } break; @@ -5284,14 +5283,14 @@ TextEditCmd( Tcl_WrongNumArgs(interp, 3, objv, NULL); return TCL_ERROR; } - redoElem = textPtr->sharedTextPtr->undoStack->redoStack; + canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); if (TextEditUndo(textPtr)) { Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to undo", -1)); Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_UNDO", NULL); return TCL_ERROR; } - undoElem = textPtr->sharedTextPtr->undoStack->undoStack; - if (redoElem == NULL || undoElem == NULL) { + canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); + if (!canRedo || !canUndo) { GenerateUndoStackEvent(textPtr); } break; diff --git a/generic/tkUndo.c b/generic/tkUndo.c index 5934154..c66905d 100644 --- a/generic/tkUndo.c +++ b/generic/tkUndo.c @@ -495,17 +495,7 @@ int TkUndoCanRedo( TkUndoRedoStack *stack) /* An Undo/Redo stack */ { - int canRedo = 0; - TkUndoAtom *elem = stack->redoStack; - - while (elem != NULL) { - if (elem->type != TK_UNDO_SEPARATOR) { - canRedo = 1; - break; - } - elem = elem->next; - } - return canRedo; + return stack->redoStack != NULL; } /* @@ -528,17 +518,7 @@ int TkUndoCanUndo( TkUndoRedoStack *stack) /* An Undo/Redo stack */ { - int canUndo = 0; - TkUndoAtom *elem = stack->undoStack; - - while (elem != NULL) { - if (elem->type != TK_UNDO_SEPARATOR) { - canUndo = 1; - break; - } - elem = elem->next; - } - return canUndo; + return stack->undoStack != NULL; } /* -- cgit v0.12