summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tkText.c124
-rw-r--r--generic/tkUndo.c26
-rw-r--r--generic/tkUndo.h4
-rw-r--r--library/text.tcl67
4 files changed, 171 insertions, 50 deletions
diff --git a/generic/tkText.c b/generic/tkText.c
index 8698825..f34b221 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -398,8 +398,8 @@ 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 int TextEditUndo(TkText *textPtr);
+static int TextEditRedo(TkText *textPtr);
static Tcl_Obj * TextGetText(const TkText *textPtr,
const TkTextIndex *index1,
const TkTextIndex *index2, int visibleOnly);
@@ -2774,6 +2774,10 @@ TextPushUndoAction(
{
TkUndoSubAtom *iAtom, *dAtom;
int canUndo, canRedo;
+ static int undoMarkId = -1;
+ char lMarkName[20] = "tk::undoMarkL";
+ char rMarkName[20] = "tk::undoMarkR";
+ char stringUndoMarkId[7] = "";
/*
* Create the helpers.
@@ -2784,7 +2788,10 @@ TextPushUndoAction(
Tcl_Obj *markSet2InsertObj = NULL;
Tcl_Obj *insertCmdObj = Tcl_NewObj();
Tcl_Obj *deleteCmdObj = Tcl_NewObj();
- Tcl_Obj *returnCmdObj = Tcl_NewObj();
+ Tcl_Obj *markSetLUndoMarkCmdObj = Tcl_NewObj();
+ Tcl_Obj *markSetRUndoMarkCmdObj = NULL;
+ Tcl_Obj *markGravityLUndoMarkCmdObj = Tcl_NewObj();
+ Tcl_Obj *markGravityRUndoMarkCmdObj = NULL;
/*
* Get the index positions.
@@ -2834,10 +2841,39 @@ TextPushUndoAction(
Tcl_ListObjAppendElement(NULL, deleteCmdObj, index1Obj);
Tcl_ListObjAppendElement(NULL, deleteCmdObj, index2Obj);
- Tcl_ListObjAppendElement(NULL, returnCmdObj,
- Tcl_NewStringObj("list", 4));
- Tcl_ListObjAppendElement(NULL, returnCmdObj, index1Obj);
- Tcl_ListObjAppendElement(NULL, returnCmdObj, index2Obj);
+ Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj,
+ Tcl_NewStringObj(Tk_PathName(textPtr->tkwin), -1));
+ Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj,
+ Tcl_NewStringObj("mark", 4));
+ Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj,
+ Tcl_NewStringObj("set", 3));
+ markSetRUndoMarkCmdObj = Tcl_DuplicateObj(markSetLUndoMarkCmdObj);
+ undoMarkId++;
+ sprintf(stringUndoMarkId, "%d", undoMarkId);
+ strcat(lMarkName, stringUndoMarkId);
+ strcat(rMarkName, stringUndoMarkId);
+ Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj,
+ Tcl_NewStringObj(lMarkName, -1));
+ Tcl_ListObjAppendElement(NULL, markSetRUndoMarkCmdObj,
+ Tcl_NewStringObj(rMarkName, -1));
+ Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj, index1Obj);
+ Tcl_ListObjAppendElement(NULL, markSetRUndoMarkCmdObj, index2Obj);
+
+ Tcl_ListObjAppendElement(NULL, markGravityLUndoMarkCmdObj,
+ Tcl_NewStringObj(Tk_PathName(textPtr->tkwin), -1));
+ Tcl_ListObjAppendElement(NULL, markGravityLUndoMarkCmdObj,
+ Tcl_NewStringObj("mark", 4));
+ Tcl_ListObjAppendElement(NULL, markGravityLUndoMarkCmdObj,
+ Tcl_NewStringObj("gravity", 7));
+ markGravityRUndoMarkCmdObj = Tcl_DuplicateObj(markGravityLUndoMarkCmdObj);
+ Tcl_ListObjAppendElement(NULL, markGravityLUndoMarkCmdObj,
+ Tcl_NewStringObj(lMarkName, -1));
+ Tcl_ListObjAppendElement(NULL, markGravityRUndoMarkCmdObj,
+ Tcl_NewStringObj(rMarkName, -1));
+ Tcl_ListObjAppendElement(NULL, markGravityLUndoMarkCmdObj,
+ Tcl_NewStringObj("left", 4));
+ Tcl_ListObjAppendElement(NULL, markGravityRUndoMarkCmdObj,
+ Tcl_NewStringObj("right", 5));
/*
* Note: we don't wish to use textPtr->widgetCmd in these callbacks
@@ -2856,13 +2892,19 @@ TextPushUndoAction(
insertCmdObj, NULL);
TkUndoMakeCmdSubAtom(NULL, markSet2InsertObj, iAtom);
TkUndoMakeCmdSubAtom(NULL, seeInsertObj, iAtom);
- TkUndoMakeCmdSubAtom(NULL, returnCmdObj, iAtom);
+ TkUndoMakeCmdSubAtom(NULL, markSetLUndoMarkCmdObj, iAtom);
+ TkUndoMakeCmdSubAtom(NULL, markSetRUndoMarkCmdObj, iAtom);
+ TkUndoMakeCmdSubAtom(NULL, markGravityLUndoMarkCmdObj, iAtom);
+ TkUndoMakeCmdSubAtom(NULL, markGravityRUndoMarkCmdObj, iAtom);
dAtom = TkUndoMakeSubAtom(&TextUndoRedoCallback, textPtr->sharedTextPtr,
deleteCmdObj, NULL);
TkUndoMakeCmdSubAtom(NULL, markSet1InsertObj, dAtom);
TkUndoMakeCmdSubAtom(NULL, seeInsertObj, dAtom);
- TkUndoMakeCmdSubAtom(NULL, returnCmdObj, dAtom);
+ TkUndoMakeCmdSubAtom(NULL, markSetLUndoMarkCmdObj, dAtom);
+ TkUndoMakeCmdSubAtom(NULL, markSetRUndoMarkCmdObj, dAtom);
+ TkUndoMakeCmdSubAtom(NULL, markGravityLUndoMarkCmdObj, dAtom);
+ TkUndoMakeCmdSubAtom(NULL, markGravityRUndoMarkCmdObj, dAtom);
Tcl_DecrRefCount(seeInsertObj);
Tcl_DecrRefCount(index1Obj);
@@ -5063,7 +5105,7 @@ DumpSegment(
* Undo the last change.
*
* Results:
- * The ranges of text that were changed by the undo operation.
+ * None.
*
* Side effects:
* Apart from manipulating the undo and redo stacks, the state of the
@@ -5074,10 +5116,11 @@ DumpSegment(
static int
TextEditUndo(
- TkText *textPtr, /* Overall information about text widget. */
- Tcl_Obj *rangesObj) /* Ranges of text that were changed. */
+ TkText *textPtr) /* Overall information about text widget. */
{
int status;
+ Tcl_Obj *cmdObj;
+ int code;
if (!textPtr->sharedTextPtr->undo) {
return TCL_OK;
@@ -5094,13 +5137,29 @@ TextEditUndo(
textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_UNDO;
}
- status = TkUndoRevert(textPtr->sharedTextPtr->undoStack, rangesObj);
+ status = TkUndoRevert(textPtr->sharedTextPtr->undoStack);
if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
}
textPtr->sharedTextPtr->undo = 1;
+ /*
+ * Convert undo/redo temporary marks set by TkUndoRevert() into
+ * indices left in the interp result.
+ */
+
+ cmdObj = Tcl_ObjPrintf("::tk::TextUndoRedoProcessMarks %s",
+ Tk_PathName(textPtr->tkwin));
+ Tcl_IncrRefCount(cmdObj);
+ code = Tcl_EvalObjEx(textPtr->interp, cmdObj, TCL_EVAL_GLOBAL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(textPtr->interp,
+ "\n (on undoing)");
+ Tcl_BackgroundException(textPtr->interp, code);
+ }
+ Tcl_DecrRefCount(cmdObj);
+
return status;
}
@@ -5112,7 +5171,7 @@ TextEditUndo(
* Redo the last undone change.
*
* Results:
- * The ranges of text that were changed by the undo operation.
+ * None.
*
* Side effects:
* Apart from manipulating the undo and redo stacks, the state of the
@@ -5123,10 +5182,11 @@ TextEditUndo(
static int
TextEditRedo(
- TkText *textPtr, /* Overall information about text widget. */
- Tcl_Obj *rangesObj) /* Ranges of text that were changed. */
+ TkText *textPtr) /* Overall information about text widget. */
{
int status;
+ Tcl_Obj *cmdObj;
+ int code;
if (!textPtr->sharedTextPtr->undo) {
return TCL_OK;
@@ -5143,12 +5203,29 @@ TextEditRedo(
textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_REDO;
}
- status = TkUndoApply(textPtr->sharedTextPtr->undoStack, rangesObj);
+ status = TkUndoApply(textPtr->sharedTextPtr->undoStack);
if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) {
textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL;
}
textPtr->sharedTextPtr->undo = 1;
+
+ /*
+ * Convert undo/redo temporary marks set by TkUndoApply() into
+ * indices left in the interp result.
+ */
+
+ cmdObj = Tcl_ObjPrintf("::tk::TextUndoRedoProcessMarks %s",
+ Tk_PathName(textPtr->tkwin));
+ Tcl_IncrRefCount(cmdObj);
+ code = Tcl_EvalObjEx(textPtr->interp, cmdObj, TCL_EVAL_GLOBAL);
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(textPtr->interp,
+ "\n (on undoing)");
+ Tcl_BackgroundException(textPtr->interp, code);
+ }
+ Tcl_DecrRefCount(cmdObj);
+
return status;
}
@@ -5179,7 +5256,6 @@ TextEditCmd(
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",
@@ -5263,13 +5339,11 @@ TextEditCmd(
return TCL_ERROR;
}
canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack);
- if (TextEditRedo(textPtr, rangesObj)) {
+ if (TextEditRedo(textPtr)) {
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);
@@ -5300,13 +5374,11 @@ TextEditCmd(
return TCL_ERROR;
}
canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack);
- if (TextEditUndo(textPtr, rangesObj)) {
+ if (TextEditUndo(textPtr)) {
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);
diff --git a/generic/tkUndo.c b/generic/tkUndo.c
index f364326..c66905d 100644
--- a/generic/tkUndo.c
+++ b/generic/tkUndo.c
@@ -556,8 +556,7 @@ TkUndoInsertUndoSeparator(
* 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.
+ * A Tcl status code
*
* Side effects:
* None.
@@ -567,8 +566,7 @@ TkUndoInsertUndoSeparator(
int
TkUndoRevert(
- TkUndoRedoStack *stack,
- Tcl_Obj *retObj)
+ TkUndoRedoStack *stack)
{
TkUndoAtom *elem;
@@ -600,13 +598,6 @@ TkUndoRevert(
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);
}
@@ -628,8 +619,7 @@ TkUndoRevert(
* 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.
+ * A Tcl status code
*
* Side effects:
* None.
@@ -639,8 +629,7 @@ TkUndoRevert(
int
TkUndoApply(
- TkUndoRedoStack *stack,
- Tcl_Obj *retObj)
+ TkUndoRedoStack *stack)
{
TkUndoAtom *elem;
@@ -671,13 +660,6 @@ TkUndoApply(
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);
}
diff --git a/generic/tkUndo.h b/generic/tkUndo.h
index 505e0f0..490ede9 100644
--- a/generic/tkUndo.h
+++ b/generic/tkUndo.h
@@ -109,7 +109,7 @@ MODULE_SCOPE TkUndoSubAtom *TkUndoMakeSubAtom(TkUndoProc *funcPtr,
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);
+MODULE_SCOPE int TkUndoRevert(TkUndoRedoStack *stack);
+MODULE_SCOPE int TkUndoApply(TkUndoRedoStack *stack);
#endif /* _TKUNDO */
diff --git a/library/text.tcl b/library/text.tcl
index 2bf1b2b..cc78bc2 100644
--- a/library/text.tcl
+++ b/library/text.tcl
@@ -1202,3 +1202,70 @@ proc ::tk::TextScanDrag {w x y} {
$w scan dragto $x $y
}
}
+
+# ::tk::TextUndoRedoProcessMarks --
+#
+# This proc is executed after an undo or redo action.
+# It processes the list of undo/redo marks temporarily set in the
+# text widget to positions delimiting where changes happened, and
+# returns a flat list of ranges. The temporary marks are removed
+# from the text widget.
+#
+# Arguments:
+# w - The text widget
+
+proc ::tk::TextUndoRedoProcessMarks {w} {
+ set indices {}
+ set undoMarks {}
+ # only consider the temporary marks set by an undo/redo action
+ foreach mark [$w mark names] {
+ if {[string range $mark 0 11] eq "tk::undoMark"} {
+ lappend undoMarks $mark
+ }
+ }
+ # the number of undo/redo marks is always even
+ set nUndoMarks [llength $undoMarks]
+ set n [expr {$nUndoMarks / 2}]
+ set undoMarks [lsort -dictionary $undoMarks]
+ set Lmarks [lrange $undoMarks 0 [expr {$n - 1}]]
+ set Rmarks [lrange $undoMarks $n [llength $undoMarks]]
+ foreach Lmark $Lmarks Rmark $Rmarks {
+ lappend indices [$w index $Lmark] [$w index $Rmark]
+ $w mark unset $Lmark $Rmark
+ }
+#puts "Unoptimized indices: $indices"
+ # process ranges to:
+ # - remove those already fully included in another range
+ # - merge overlapping ranges
+ set ind [lsort -dictionary -stride 2 $indices]
+ set indices {}
+ for {set i 0} {$i < $nUndoMarks} {incr i 2} {
+ set il1 [lindex $ind $i]
+ set ir1 [lindex $ind [expr {$i + 1}]]
+ lappend indices $il1 $ir1
+#puts " range1: $il1 $ir1"
+ for {set j [expr {$i + 2}]} {$j < $nUndoMarks} {incr j 2} {
+ set il2 [lindex $ind $j]
+ set ir2 [lindex $ind [expr {$j + 1}]]
+#puts " range2: $il2 $ir2"
+ if {[$w compare $il2 > $ir1]} {
+ # second range starts after the end of first range
+ set j $nUndoMarks
+ } else {
+ if {[$w compare $ir2 > $ir1]} {
+ # second range overlaps with first range
+ set indices [lreplace $indices end-1 end]
+ lappend indices $il1 $ir2
+ } else {
+ # second range is fully included in first range
+ }
+ set ind [lreplace $ind $j [expr {$j + 1}]]
+ incr j -2
+ incr nUndoMarks -2
+ }
+#puts " indices: $indices"
+ }
+ }
+#puts "OPTIMIZED indices: $indices"
+ return $indices
+}