From e53f6d854da7ed78ebd3ee5f7f564e62e73ad988 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 FossilOrigin-Name: 8fc35b22e19eb1d59535c65998270701cbc68734 --- 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 17e10b955a1420f0aa1d7a3ff9ba7257c4fe3566 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 FossilOrigin-Name: d2672735ed11df711bbfefad0d39eb36f1216d71 --- 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 064fd2bf601ef424c44de3debd3ddb7141d43958 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] FossilOrigin-Name: 3b1114b825ec4b96a29862b306915945786dd79c --- 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 424405dff35e08b462b7770bac775512331753b7 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] FossilOrigin-Name: 00090592cefb1b09d2bb37e6bf653ef51b4eaec3 --- 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 dc4d737d706ce7b8934cd0cf103f73647da4d792 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 FossilOrigin-Name: fe13004f507cc999338f995588785036deacded8 --- 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 f6e2a01f75fc14a2337636495c4ccd724f816d1b 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] FossilOrigin-Name: 16484cdd29966f986f320d7be9305cb38accf97a --- 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 baacdd0371aeece43689fd0dc6d326d8aec354d2 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] FossilOrigin-Name: b9932d5fc9f7578797ada36def202fc2fa4f408a --- 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 c33afcc7a67a887ac0aaa056ad184b928901e38d 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 FossilOrigin-Name: 1dfd16153229e1c307063b64f6831aec77710f1a --- 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 4a917d787fbcb7fb6aadfede882bde8a705e2be3 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:22:57 +0000 Subject: Documented the <> event FossilOrigin-Name: 5ddbec41ee681a75ebfb8e365636d463812f680e --- 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 6de1fd0291d58c542e687e55b68f126d6b1f544c 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 FossilOrigin-Name: bab37fee09687d24dda39aebcf1ab7031f26e014 --- 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 ac1ec1fd43addc81f0b25194047bb9bc720d3404 Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 12 May 2016 21:35:47 +0000 Subject: Added test for the <> event FossilOrigin-Name: efe4cb36c399edc9e9c839e345a4b2c27c7bf913 --- 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 a463ad69a0999ded24429e87516a8bb36636fc78 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 FossilOrigin-Name: ef7364ed74b99bb76753d6223c54fb07112191be --- 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 bac52478bad68f58e979eaf00ff9d06c081617cf 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 FossilOrigin-Name: b699eb0e2cb0acd16c39095dcf9f632b931614ee --- 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