From c97db25d011e7cef5dbb37cf3ce0a2a50bcbb5ca 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 6b0ad3ecbb13963f417e8a56292c24679f23126e 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 cd651e6819b4cf35018f8bbb1114655f5eb80b0b 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 16e7c0d35d096adcb40d6dded28593d6a907f581 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 6edae2839102dc6916273780f7b1c6109c6296dd 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 4b7037b79cea719b6bf19a553dd458bbcd517689 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 0c494bd4742fae9dc0782b5ec7f3dfcbe7c9869f 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 fb21de2b2d966342327edfe777b99bad3c00db21 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 f29d303fa6970e0e9e658e722910f90a77aa5de3 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 85931fc019f97ba08c56324caa838a3526b7273b 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 b646ae45954e035debea24d3f1772a8dda43ced8 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 08928a3c446dd927df6262bbede44275a4477b6f 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 f3307676078f98736473d67cf210cbf543497648 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