Tk Source Code

Check-in [afa23a02]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to tclconference@googlegroups.com
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:It is not possible to rely only on the interp result. A list of indices has to be built as a return value to undo/redo because there can be several edits between two separators and all such edits have to report which range of text they changed. Note: this commit does not deal with refcounts, it is very likely wrong in that respect.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tip-449
Files: files | file ages | folders
SHA1:afa23a020ad2ff5a1c6291151d4762e5322bf248
User & Date: fvogel 2016-06-08 09:38:29
Context
2016-07-04
21:26
Return indices making sense at undo/redo return time. The returned ranges are optimized (no duplicates, no overlapping ranges). Works but needs polishing. check-in: 6c03c35d user: fvogel tags: tip-449
2016-06-08
13:23
Added test case text-27.26 for [.text edit undo/redo] return values check-in: 9d5e1c4b user: fvogel tags: tip-449
09:38
It is not possible to rely only on the interp result. A list of indices has to be built as a return value to undo/redo because there can be several edits between two separators and all such edits have to report which range of text they changed. Note: this commit does not deal with refcounts, it is very likely wrong in that respect. check-in: afa23a02 user: fvogel tags: tip-449
08:34
Updated documentation regarding return values for [.text edit undo/redo] check-in: 552ec80b user: fvogel tags: tip-449
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tkText.c.

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
....
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074

5075
5076
5077
5078
5079
5080
5081
....
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
....
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122

5123
5124
5125
5126
5127
5128
5129
....
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
....
5170
5171
5172
5173
5174
5175
5176

5177
5178
5179
5180
5181
5182
5183
....
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263


5264
5265
5266
5267
5268
5269
5270
5271
....
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298


5299
5300
5301
5302
5303
5304
5305
5306
static int		DumpLine(Tcl_Interp *interp, TkText *textPtr,
			    int what, TkTextLine *linePtr, int start, int end,
			    int lineno, Tcl_Obj *command);
static int		DumpSegment(TkText *textPtr, Tcl_Interp *interp,
			    const char *key, const char *value,
			    Tcl_Obj *command, const TkTextIndex *index,
			    int what);
static int		TextEditUndo(TkText *textPtr);
static int		TextEditRedo(TkText *textPtr);
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);
................................................................................
 *----------------------------------------------------------------------
 *
 * TextEditUndo --
 *
 *	Undo the last change.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Apart from manipulating the undo and redo stacks, the state of the
 *	rest of the widget may also change (due to whatever is being undone).
 *
 *----------------------------------------------------------------------
 */

static int
TextEditUndo(
    TkText *textPtr)		/* Overall information about text widget. */

{
    int status;

    if (!textPtr->sharedTextPtr->undo) {
	return TCL_OK;
    }

................................................................................
     */

    textPtr->sharedTextPtr->undo = 0;
    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_UNDO;
    }

    status = TkUndoRevert(textPtr->sharedTextPtr->undoStack);

    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
    }
    textPtr->sharedTextPtr->undo = 1;

    return status;
................................................................................
 *----------------------------------------------------------------------
 *
 * TextEditRedo --
 *
 *	Redo the last undone change.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Apart from manipulating the undo and redo stacks, the state of the
 *	rest of the widget may also change (due to whatever is being redone).
 *
 *----------------------------------------------------------------------
 */

static int
TextEditRedo(
    TkText *textPtr)		/* Overall information about text widget. */

{
    int status;

    if (!textPtr->sharedTextPtr->undo) {
	return TCL_OK;
    }

................................................................................
     */

    textPtr->sharedTextPtr->undo = 0;
    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_REDO;
    }

    status = TkUndoApply(textPtr->sharedTextPtr->undoStack);

    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
    }
    textPtr->sharedTextPtr->undo = 1;
    return status;
}
................................................................................
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int index, setModified, oldModified;
    int canRedo = 0;
    int canUndo = 0;


    static const char *const editOptionStrings[] = {
	"canundo", "canredo", "modified", "redo", "reset", "separator",
        "undo", NULL
    };
    enum editOptions {
	EDIT_CANUNDO, EDIT_CANREDO, EDIT_MODIFIED, EDIT_REDO, EDIT_RESET,
................................................................................
	break;
    case EDIT_REDO:
        if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 3, objv, NULL);
	    return TCL_ERROR;
	}
	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;


	}
        canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack);
        if (!canUndo || !canRedo) {
            GenerateUndoStackEvent(textPtr);
        }
	break;
    case EDIT_RESET:
	if (objc != 3) {
................................................................................
	break;
    case EDIT_UNDO:
        if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 3, objv, NULL);
	    return TCL_ERROR;
	}
        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;


	}
        canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack);
        if (!canRedo || !canUndo) {
            GenerateUndoStackEvent(textPtr);
        }
	break;
    }
    return TCL_OK;







|
|







 







|










|
>







 







|







 







|










|
>







 







|







 







>







 







|



>
>
|







 







|



>
>
|







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
....
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
....
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
....
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
....
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
....
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
....
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
....
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
static int		DumpLine(Tcl_Interp *interp, TkText *textPtr,
			    int what, TkTextLine *linePtr, int start, int end,
			    int lineno, Tcl_Obj *command);
static int		DumpSegment(TkText *textPtr, Tcl_Interp *interp,
			    const char *key, const char *value,
			    Tcl_Obj *command, const TkTextIndex *index,
			    int what);
static int		TextEditUndo(TkText *textPtr, Tcl_Obj *rangesObj);
static int		TextEditRedo(TkText *textPtr, Tcl_Obj *rangesObj);
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);
................................................................................
 *----------------------------------------------------------------------
 *
 * TextEditUndo --
 *
 *	Undo the last change.
 *
 * Results:
 *	The ranges of text that were changed by the undo operation.
 *
 * Side effects:
 *	Apart from manipulating the undo and redo stacks, the state of the
 *	rest of the widget may also change (due to whatever is being undone).
 *
 *----------------------------------------------------------------------
 */

static int
TextEditUndo(
    TkText *textPtr,		/* Overall information about text widget. */
    Tcl_Obj *rangesObj)         /* Ranges of text that were changed. */
{
    int status;

    if (!textPtr->sharedTextPtr->undo) {
	return TCL_OK;
    }

................................................................................
     */

    textPtr->sharedTextPtr->undo = 0;
    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_UNDO;
    }

    status = TkUndoRevert(textPtr->sharedTextPtr->undoStack, rangesObj);

    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
    }
    textPtr->sharedTextPtr->undo = 1;

    return status;
................................................................................
 *----------------------------------------------------------------------
 *
 * TextEditRedo --
 *
 *	Redo the last undone change.
 *
 * Results:
 *	The ranges of text that were changed by the undo operation.
 *
 * Side effects:
 *	Apart from manipulating the undo and redo stacks, the state of the
 *	rest of the widget may also change (due to whatever is being redone).
 *
 *----------------------------------------------------------------------
 */

static int
TextEditRedo(
    TkText *textPtr,		/* Overall information about text widget. */
    Tcl_Obj *rangesObj)         /* Ranges of text that were changed. */
{
    int status;

    if (!textPtr->sharedTextPtr->undo) {
	return TCL_OK;
    }

................................................................................
     */

    textPtr->sharedTextPtr->undo = 0;
    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_REDO;
    }

    status = TkUndoApply(textPtr->sharedTextPtr->undoStack, rangesObj);

    if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
	textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
    }
    textPtr->sharedTextPtr->undo = 1;
    return status;
}
................................................................................
    Tcl_Interp *interp,		/* Current interpreter. */
    int objc,			/* Number of arguments. */
    Tcl_Obj *const objv[])	/* Argument objects. */
{
    int index, setModified, oldModified;
    int canRedo = 0;
    int canUndo = 0;
    Tcl_Obj *rangesObj = Tcl_NewObj();

    static const char *const editOptionStrings[] = {
	"canundo", "canredo", "modified", "redo", "reset", "separator",
        "undo", NULL
    };
    enum editOptions {
	EDIT_CANUNDO, EDIT_CANREDO, EDIT_MODIFIED, EDIT_REDO, EDIT_RESET,
................................................................................
	break;
    case EDIT_REDO:
        if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 3, objv, NULL);
	    return TCL_ERROR;
	}
	canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack);
        if (TextEditRedo(textPtr, rangesObj)) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to redo", -1));
	    Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_REDO", NULL);
	    return TCL_ERROR;
        } else {
            Tcl_SetObjResult(interp, rangesObj);
        }
        canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack);
        if (!canUndo || !canRedo) {
            GenerateUndoStackEvent(textPtr);
        }
	break;
    case EDIT_RESET:
	if (objc != 3) {
................................................................................
	break;
    case EDIT_UNDO:
        if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 3, objv, NULL);
	    return TCL_ERROR;
	}
        canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack);
	if (TextEditUndo(textPtr, rangesObj)) {
	    Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to undo", -1));
	    Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_UNDO", NULL);
	    return TCL_ERROR;
        } else {
            Tcl_SetObjResult(interp, rangesObj);
        }
        canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack);
        if (!canRedo || !canUndo) {
            GenerateUndoStackEvent(textPtr);
        }
	break;
    }
    return TCL_OK;

Changes to generic/tkUndo.c.

552
553
554
555
556
557
558
559

560
561
562
563
564
565
566
567
568
569

570
571
572
573
574
575
576
...
594
595
596
597
598
599
600







601
602
603
604
605
606
607
...
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630
631
632

633
634
635
636
637
638
639
...
656
657
658
659
660
661
662







663
664
665
666
667
668
669
 *----------------------------------------------------------------------
 *
 * TkUndoRevert --
 *
 *	Undo a compound action on the stack.
 *
 * Results:
 *	A Tcl status code

 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkUndoRevert(
    TkUndoRedoStack *stack)

{
    TkUndoAtom *elem;

    /*
     * Insert a separator on the undo and the redo stack.
     */

................................................................................
    while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
	/*
	 * Note that we currently ignore errors thrown here.
	 */

	EvaluateActionList(stack->interp, elem->revert);








	TkUndoPushStack(&stack->redoStack, elem);
	elem = TkUndoPopStack(&stack->undoStack);
    }

    /*
     * Insert a separator on the redo stack.
     */
................................................................................
 *----------------------------------------------------------------------
 *
 * TkUndoApply --
 *
 *	Redo a compound action on the stack.
 *
 * Results:
 *	A Tcl status code

 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkUndoApply(
    TkUndoRedoStack *stack)

{
    TkUndoAtom *elem;

    /*
     * Insert a separator on the undo stack.
     */

................................................................................
    while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
	/*
	 * Note that we currently ignore errors thrown here.
	 */

	EvaluateActionList(stack->interp, elem->apply);








	TkUndoPushStack(&stack->undoStack, elem);
	elem = TkUndoPopStack(&stack->redoStack);
    }

    /*
     * Insert a separator on the undo stack.
     */







|
>









|
>







 







>
>
>
>
>
>
>







 







|
>









|
>







 







>
>
>
>
>
>
>







552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
...
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
...
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
 *----------------------------------------------------------------------
 *
 * TkUndoRevert --
 *
 *	Undo a compound action on the stack.
 *
 * Results:
 *	A Tcl status code. Also, the passed Tcl_(List)Obj is appended by the
 *	interp results from evaluation of each element of the undo stack.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkUndoRevert(
    TkUndoRedoStack *stack,
    Tcl_Obj *retObj)
{
    TkUndoAtom *elem;

    /*
     * Insert a separator on the undo and the redo stack.
     */

................................................................................
    while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
	/*
	 * Note that we currently ignore errors thrown here.
	 */

	EvaluateActionList(stack->interp, elem->revert);

        /*
         * The interp result is appended to the returned object for each
         * element of the undo stack (not for each sub-atom).
         */

        Tcl_ListObjAppendList(NULL, retObj, Tcl_GetObjResult(stack->interp));

	TkUndoPushStack(&stack->redoStack, elem);
	elem = TkUndoPopStack(&stack->undoStack);
    }

    /*
     * Insert a separator on the redo stack.
     */
................................................................................
 *----------------------------------------------------------------------
 *
 * TkUndoApply --
 *
 *	Redo a compound action on the stack.
 *
 * Results:
 *	A Tcl status code. Also, the passed Tcl_(List)Obj is appended by the
 *	interp results from evaluation of each element of the redo stack.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
TkUndoApply(
    TkUndoRedoStack *stack,
    Tcl_Obj *retObj)
{
    TkUndoAtom *elem;

    /*
     * Insert a separator on the undo stack.
     */

................................................................................
    while (elem != NULL && elem->type != TK_UNDO_SEPARATOR) {
	/*
	 * Note that we currently ignore errors thrown here.
	 */

	EvaluateActionList(stack->interp, elem->apply);

        /*
         * The interp result is appended to the returned object for each
         * element of the redo stack (not for each sub-atom).
         */

        Tcl_ListObjAppendList(NULL, retObj, Tcl_GetObjResult(stack->interp));

	TkUndoPushStack(&stack->undoStack, elem);
	elem = TkUndoPopStack(&stack->redoStack);
    }

    /*
     * Insert a separator on the undo stack.
     */

Changes to generic/tkUndo.h.

105
106
107
108
109
110
111
112
113
114
115
MODULE_SCOPE TkUndoSubAtom *TkUndoMakeCmdSubAtom(Tcl_Command command,
			    Tcl_Obj *actionScript, TkUndoSubAtom *subAtomList);
MODULE_SCOPE TkUndoSubAtom *TkUndoMakeSubAtom(TkUndoProc *funcPtr,
			    ClientData clientData, Tcl_Obj *actionScript,
			    TkUndoSubAtom *subAtomList);
MODULE_SCOPE void	TkUndoPushAction(TkUndoRedoStack *stack,
			    TkUndoSubAtom *apply, TkUndoSubAtom *revert);
MODULE_SCOPE int	TkUndoRevert(TkUndoRedoStack *stack);
MODULE_SCOPE int	TkUndoApply(TkUndoRedoStack *stack);

#endif /* _TKUNDO */







|
|


105
106
107
108
109
110
111
112
113
114
115
MODULE_SCOPE TkUndoSubAtom *TkUndoMakeCmdSubAtom(Tcl_Command command,
			    Tcl_Obj *actionScript, TkUndoSubAtom *subAtomList);
MODULE_SCOPE TkUndoSubAtom *TkUndoMakeSubAtom(TkUndoProc *funcPtr,
			    ClientData clientData, Tcl_Obj *actionScript,
			    TkUndoSubAtom *subAtomList);
MODULE_SCOPE void	TkUndoPushAction(TkUndoRedoStack *stack,
			    TkUndoSubAtom *apply, TkUndoSubAtom *revert);
MODULE_SCOPE int	TkUndoRevert(TkUndoRedoStack *stack, Tcl_Obj *retObj);
MODULE_SCOPE int	TkUndoApply(TkUndoRedoStack *stack, Tcl_Obj *retObj);

#endif /* _TKUNDO */