From eb3fa653566d6bf130804d7d800f960d72819597 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 7 Jun 2016 21:04:51 +0000 Subject: Possible solution for [1217222] - [.text edit undo/redo] return ranges of modified characters --- generic/tkText.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/generic/tkText.c b/generic/tkText.c index b610844..d101364 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -2781,6 +2781,7 @@ TextPushUndoAction( Tcl_Obj *markSet2InsertObj = NULL; Tcl_Obj *insertCmdObj = Tcl_NewObj(); Tcl_Obj *deleteCmdObj = Tcl_NewObj(); + Tcl_Obj *returnCmdObj = Tcl_NewObj(); /* * Get the index positions. @@ -2830,6 +2831,11 @@ 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); + /* * Note: we don't wish to use textPtr->widgetCmd in these callbacks * because if we delete the textPtr, but peers still exist, we will then @@ -2847,11 +2853,13 @@ TextPushUndoAction( insertCmdObj, NULL); TkUndoMakeCmdSubAtom(NULL, markSet2InsertObj, iAtom); TkUndoMakeCmdSubAtom(NULL, seeInsertObj, iAtom); + TkUndoMakeCmdSubAtom(NULL, returnCmdObj, iAtom); dAtom = TkUndoMakeSubAtom(&TextUndoRedoCallback, textPtr->sharedTextPtr, deleteCmdObj, NULL); TkUndoMakeCmdSubAtom(NULL, markSet1InsertObj, dAtom); TkUndoMakeCmdSubAtom(NULL, seeInsertObj, dAtom); + TkUndoMakeCmdSubAtom(NULL, returnCmdObj, dAtom); Tcl_DecrRefCount(seeInsertObj); Tcl_DecrRefCount(index1Obj); -- cgit v0.12 From 940954ce6034eac3de51350f4d546ed7279e7ec9 Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 8 Jun 2016 08:34:52 +0000 Subject: Updated documentation regarding return values for [.text edit undo/redo] --- doc/text.n | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/text.n b/doc/text.n index e2bb01f..9633e37 100644 --- a/doc/text.n +++ b/doc/text.n @@ -1305,8 +1305,9 @@ of the widget to \fIboolean\fR. \fIpathName \fBedit redo\fR . 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. +no other edits were done since then, and returns a list of indices indicating +what ranges were changed by the redo operation. Generates an error when the +redo stack is empty. Does nothing when the \fB\-undo\fR option is false. .TP \fIpathName \fBedit reset\fR . @@ -1319,9 +1320,10 @@ Inserts a separator (boundary) on the undo stack. Does nothing when the .TP \fIpathName \fBedit undo\fR . -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 +Undoes the last edit action when the \fB\-undo\fR option is true, and returns a +list of indices indicating what ranges were changed by the undo operation. 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. .RE .TP -- cgit v0.12 From abdebd5b836480c1552c478a871c77b4668135ed Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 8 Jun 2016 09:38:29 +0000 Subject: 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. --- generic/tkText.c | 31 +++++++++++++++++++------------ generic/tkUndo.c | 26 ++++++++++++++++++++++---- generic/tkUndo.h | 4 ++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index d101364..60e726f 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -395,8 +395,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); -static int TextEditRedo(TkText *textPtr); +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); @@ -5060,7 +5060,7 @@ DumpSegment( * Undo the last change. * * Results: - * None. + * 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 @@ -5071,7 +5071,8 @@ DumpSegment( static int TextEditUndo( - TkText *textPtr) /* Overall information about text widget. */ + TkText *textPtr, /* Overall information about text widget. */ + Tcl_Obj *rangesObj) /* Ranges of text that were changed. */ { int status; @@ -5090,7 +5091,7 @@ TextEditUndo( textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_UNDO; } - status = TkUndoRevert(textPtr->sharedTextPtr->undoStack); + status = TkUndoRevert(textPtr->sharedTextPtr->undoStack, rangesObj); if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) { textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL; @@ -5108,7 +5109,7 @@ TextEditUndo( * Redo the last undone change. * * Results: - * None. + * 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 @@ -5119,7 +5120,8 @@ TextEditUndo( static int TextEditRedo( - TkText *textPtr) /* Overall information about text widget. */ + TkText *textPtr, /* Overall information about text widget. */ + Tcl_Obj *rangesObj) /* Ranges of text that were changed. */ { int status; @@ -5138,7 +5140,7 @@ TextEditRedo( textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_REDO; } - status = TkUndoApply(textPtr->sharedTextPtr->undoStack); + status = TkUndoApply(textPtr->sharedTextPtr->undoStack, rangesObj); if (textPtr->sharedTextPtr->dirtyMode != TK_TEXT_DIRTY_FIXED) { textPtr->sharedTextPtr->dirtyMode = TK_TEXT_DIRTY_NORMAL; @@ -5174,6 +5176,7 @@ 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", @@ -5257,11 +5260,13 @@ TextEditCmd( return TCL_ERROR; } canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); - if (TextEditRedo(textPtr)) { + 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); @@ -5292,11 +5297,13 @@ TextEditCmd( return TCL_ERROR; } canRedo = TkUndoCanRedo(textPtr->sharedTextPtr->undoStack); - if (TextEditUndo(textPtr)) { + 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); diff --git a/generic/tkUndo.c b/generic/tkUndo.c index c66905d..f364326 100644 --- a/generic/tkUndo.c +++ b/generic/tkUndo.c @@ -556,7 +556,8 @@ TkUndoInsertUndoSeparator( * Undo a compound action on the stack. * * Results: - * A Tcl status code + * 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. @@ -566,7 +567,8 @@ TkUndoInsertUndoSeparator( int TkUndoRevert( - TkUndoRedoStack *stack) + TkUndoRedoStack *stack, + Tcl_Obj *retObj) { TkUndoAtom *elem; @@ -598,6 +600,13 @@ 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); } @@ -619,7 +628,8 @@ TkUndoRevert( * Redo a compound action on the stack. * * Results: - * A Tcl status code + * 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. @@ -629,7 +639,8 @@ TkUndoRevert( int TkUndoApply( - TkUndoRedoStack *stack) + TkUndoRedoStack *stack, + Tcl_Obj *retObj) { TkUndoAtom *elem; @@ -660,6 +671,13 @@ 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 490ede9..505e0f0 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); -MODULE_SCOPE int TkUndoApply(TkUndoRedoStack *stack); +MODULE_SCOPE int TkUndoRevert(TkUndoRedoStack *stack, Tcl_Obj *retObj); +MODULE_SCOPE int TkUndoApply(TkUndoRedoStack *stack, Tcl_Obj *retObj); #endif /* _TKUNDO */ -- cgit v0.12 From d60b9e28ecea5421fcb384737837748d9cd677df Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 8 Jun 2016 13:23:22 +0000 Subject: Added test case text-27.26 for [.text edit undo/redo] return values --- tests/text.test | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/text.test b/tests/text.test index 0ec69d0..abbcfe5 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6562,6 +6562,36 @@ test text-27.25 {<> virtual event} -setup { } -cleanup { destroy .t } -result {0 0 1 2 3 4 4 5 6 6 7 8 8 9} +test text-27.26 {edit undo and edit redo return ranges} -setup { + destroy .t + set res {} +} -body { + text .t -undo true -autoseparators false + .t insert end "Hello " + .t edit separator + .t insert end "World!\n" + .t insert 1.6 "GREAT " + .t insert end "Another edit here!!" + lappend res [.t edit undo] + lappend res [.t edit redo] + .t edit separator + .t delete 1.6 + .t delete 1.9 1.10 + .t insert 1.9 L + lappend res [.t edit undo] + lappend res [.t edit redo] + .t replace 1.6 1.10 Tcl/Tk + .t replace 2.8 2.12 "one bites the dust" + lappend res [.t edit undo] + lappend res [.t edit redo] +} -cleanup { + destroy .t +} -result [list {2.0 2.19 1.6 1.12 1.6 2.0} \ + {1.6 2.0 1.6 1.12 2.0 2.19} \ + {1.9 1.10 1.9 1.10 1.6 1.7} \ + {1.6 1.7 1.9 1.10 1.9 1.10} \ + {2.8 2.26 2.8 2.12 1.6 1.12 1.6 1.10} \ + {1.6 1.10 1.6 1.12 2.8 2.12 2.8 2.26} ] test text-28.1 {bug fix - 624372, ControlUtfProc long lines} -body { -- cgit v0.12 From 77f4d07ef5cfb838fee58aeadf50fa3d948d273d Mon Sep 17 00:00:00 2001 From: fvogel Date: Mon, 4 Jul 2016 21:26:53 +0000 Subject: Return indices making sense at undo/redo return time. The returned ranges are optimized (no duplicates, no overlapping ranges). Works but needs polishing. --- generic/tkText.c | 124 +++++++++++++++++++++++++++++++++++++++++++------------ generic/tkUndo.c | 26 ++---------- generic/tkUndo.h | 4 +- library/text.tcl | 67 ++++++++++++++++++++++++++++++ 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 +} -- cgit v0.12 From 19daddfc9fecac5b38d749263e8553bc9b44d415 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 5 Jul 2016 20:02:59 +0000 Subject: Better comments explaining the algorithm in ::tk::TextUndoRedoProcessMarks --- library/text.tcl | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/library/text.tcl b/library/text.tcl index cc78bc2..f241abd 100644 --- a/library/text.tcl +++ b/library/text.tcl @@ -1217,13 +1217,17 @@ proc ::tk::TextScanDrag {w x y} { 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 + + # transform marks into indices + # the number of undo/redo marks is always even, each right mark + # completes a left mark to give a range set nUndoMarks [llength $undoMarks] set n [expr {$nUndoMarks / 2}] set undoMarks [lsort -dictionary $undoMarks] @@ -1233,39 +1237,51 @@ proc ::tk::TextUndoRedoProcessMarks {w} { 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 + # -> further second ranges do not need to be considered + # because ranges were sorted by increasing first index set j $nUndoMarks + } else { if {[$w compare $ir2 > $ir1]} { - # second range overlaps with first range + # second range overlaps first range + # -> merge them into a single range set indices [lreplace $indices end-1 end] lappend indices $il1 $ir2 + } else { # second range is fully included in first range + # -> ignore it + } + # in both cases above, the second range shall be + # trimmed out from the list of ranges set ind [lreplace $ind $j [expr {$j + 1}]] incr j -2 incr nUndoMarks -2 + } -#puts " indices: $indices" + } + } -#puts "OPTIMIZED indices: $indices" + return $indices } -- cgit v0.12 From 77f55a8fc19b9564340f53bc04e96c0217bf24a9 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 5 Jul 2016 20:03:53 +0000 Subject: Removed unintended whitespace change --- generic/tkText.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tkText.c b/generic/tkText.c index f34b221..4195238 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -5339,7 +5339,7 @@ TextEditCmd( return TCL_ERROR; } canUndo = TkUndoCanUndo(textPtr->sharedTextPtr->undoStack); - if (TextEditRedo(textPtr)) { + if (TextEditRedo(textPtr)) { Tcl_SetObjResult(interp, Tcl_NewStringObj("nothing to redo", -1)); Tcl_SetErrorCode(interp, "TK", "TEXT", "NO_REDO", NULL); return TCL_ERROR; -- cgit v0.12 From 5718d28895a5ea6c26d0dc5e8421b3ff31fe7785 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 5 Jul 2016 20:07:01 +0000 Subject: Added more tests, and fixed test result of text-27.26 since now indices are relative to the text widget state at undo/redo return time, and also because they are optimized (no overlaps) --- tests/text.test | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/tests/text.test b/tests/text.test index a3f9afe..65c3225 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6601,12 +6601,42 @@ test text-27.26 {edit undo and edit redo return ranges} -setup { lappend res [.t edit redo] } -cleanup { destroy .t -} -result [list {2.0 2.19 1.6 1.12 1.6 2.0} \ - {1.6 2.0 1.6 1.12 2.0 2.19} \ - {1.9 1.10 1.9 1.10 1.6 1.7} \ - {1.6 1.7 1.9 1.10 1.9 1.10} \ - {2.8 2.26 2.8 2.12 1.6 1.12 1.6 1.10} \ - {1.6 1.10 1.6 1.12 2.8 2.12 2.8 2.26} ] +} -result [list {1.6 2.0} \ + {1.6 2.19} \ + {1.6 1.7 1.10 1.12} \ + {1.6 1.7 1.9 1.11} \ + {1.6 1.16 2.8 2.19} \ + {1.6 1.16 2.8 2.30} ] +test text-27.27 {edit undo and edit redo return ranges} -setup { + destroy .t + set res {} +} -body { + text .t -undo true -autoseparators false + for {set i 3} {$i >= 1} {incr i -1} { + .t insert 1.0 "Line $i\n" + } + lappend res [.t edit undo] + lappend res [.t edit redo] +} -cleanup { + destroy .t +} -result [list {1.0 2.0} \ + {1.0 4.0} ] +test text-27.28 {edit undo and edit redo do not leave \ + spurious temporary marks behind them} -setup { + destroy .t + set res {} +} -body { + pack [text .t -undo true -autoseparators false] + .t insert end "Hello World.\n" + .t edit separator + .t insert end "Again hello.\n" + .t edit undo + lappend res [lsearch [.t mark names] tk::undoMark*] + .t edit undo + lappend res [lsearch [.t mark names] tk::undoMark*] +} -cleanup { + destroy .t +} -result [list -1 -1] test text-28.1 {bug fix - 624372, ControlUtfProc long lines} -body { -- cgit v0.12 From e28d7bfd6199766d14e5583fcf905289c6737c36 Mon Sep 17 00:00:00 2001 From: fvogel Date: Tue, 5 Jul 2016 20:36:25 +0000 Subject: Fixed copy/paste error in test text-27.28 - one wants to test undo and redo, not twice undo --- tests/text.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/text.test b/tests/text.test index 65c3225..2e4177d 100644 --- a/tests/text.test +++ b/tests/text.test @@ -6632,7 +6632,7 @@ test text-27.28 {edit undo and edit redo do not leave \ .t insert end "Again hello.\n" .t edit undo lappend res [lsearch [.t mark names] tk::undoMark*] - .t edit undo + .t edit redo lappend res [lsearch [.t mark names] tk::undoMark*] } -cleanup { destroy .t -- cgit v0.12 From bf98687ae68fe39f9b67d9cd3c1553bfdd43bf7b Mon Sep 17 00:00:00 2001 From: fvogel Date: Wed, 6 Jul 2016 17:00:05 +0000 Subject: The undo marks identifier is now a member of textPtr->sharedTextPtr. This makes results of test text-8.22 independant of previous undo/redo actions on other text widgets. --- generic/tkText.c | 5 ++--- generic/tkText.h | 2 ++ tests/text.test | 14 +++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/generic/tkText.c b/generic/tkText.c index 4195238..6b90a23 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -2774,7 +2774,6 @@ TextPushUndoAction( { TkUndoSubAtom *iAtom, *dAtom; int canUndo, canRedo; - static int undoMarkId = -1; char lMarkName[20] = "tk::undoMarkL"; char rMarkName[20] = "tk::undoMarkR"; char stringUndoMarkId[7] = ""; @@ -2848,8 +2847,8 @@ TextPushUndoAction( Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj, Tcl_NewStringObj("set", 3)); markSetRUndoMarkCmdObj = Tcl_DuplicateObj(markSetLUndoMarkCmdObj); - undoMarkId++; - sprintf(stringUndoMarkId, "%d", undoMarkId); + textPtr->sharedTextPtr->undoMarkId++; + sprintf(stringUndoMarkId, "%d", textPtr->sharedTextPtr->undoMarkId); strcat(lMarkName, stringUndoMarkId); strcat(rMarkName, stringUndoMarkId); Tcl_ListObjAppendElement(NULL, markSetLUndoMarkCmdObj, diff --git a/generic/tkText.h b/generic/tkText.h index 5d88784..430c96b 100644 --- a/generic/tkText.h +++ b/generic/tkText.h @@ -580,6 +580,8 @@ typedef struct TkSharedText { * statements. */ int autoSeparators; /* Non-zero means the separators will be * inserted automatically. */ + int undoMarkId; /* Counts undo marks temporarily used during + undo and redo operations. */ int isDirty; /* Flag indicating the 'dirtyness' of the * text widget. If the flag is not zero, * unsaved modifications have been applied to diff --git a/tests/text.test b/tests/text.test index 2e4177d..1a68239 100644 --- a/tests/text.test +++ b/tests/text.test @@ -1458,7 +1458,19 @@ Line 7" rename .t {} rename test.t .t destroy .t -} -result {{edit undo} {delete 2.1 2.4} {mark set insert 2.1} {see insert} {insert 2.1 ef} {mark set insert 2.3} {see insert}} +} -result [list {edit undo} {delete 2.1 2.4} {mark set insert 2.1} {see insert} \ + {mark set tk::undoMarkL3 2.1} {mark set tk::undoMarkR3 2.4} \ + {mark gravity tk::undoMarkL3 left} {mark gravity tk::undoMarkR3 right} \ + {insert 2.1 ef} {mark set insert 2.3} {see insert} \ + {mark set tk::undoMarkL2 2.1} {mark set tk::undoMarkR2 2.3} \ + {mark gravity tk::undoMarkL2 left} {mark gravity tk::undoMarkR2 right} \ + {mark names} \ + {index tk::undoMarkL2} {index tk::undoMarkR2} \ + {mark unset tk::undoMarkL2 tk::undoMarkR2} \ + {index tk::undoMarkL3} {index tk::undoMarkR3} \ + {mark unset tk::undoMarkL3 tk::undoMarkR3} \ + {compare 2.1 > 2.3} {compare 2.6 > 2.3} ] + test text-8.23 {TextWidgetCmd procedure, "replace" option with undo} -setup { text .t } -body { -- cgit v0.12 From 1052e56fe96f3ece407a6374714af0302c9a5e3c Mon Sep 17 00:00:00 2001 From: fvogel Date: Thu, 7 Jul 2016 06:39:26 +0000 Subject: Added comments justifying why we can rely on finding all the needed marks still in the text widget at the time they are transformed into indices. --- library/text.tcl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/text.tcl b/library/text.tcl index f241abd..02a8939 100644 --- a/library/text.tcl +++ b/library/text.tcl @@ -1228,6 +1228,19 @@ proc ::tk::TextUndoRedoProcessMarks {w} { # transform marks into indices # the number of undo/redo marks is always even, each right mark # completes a left mark to give a range + # this is true because: + # - undo/redo only deals with insertions and deletions of text + # - insertions may move marks but not delete them + # - when deleting text, marks located inside the deleted range + # are not erased but moved to the start of the deletion range + # . this is done in TkBTreeDeleteIndexRange ("This segment + # refuses to die...") + # . because MarkDeleteProc does nothing else than returning + # a value indicating that marks are not deleted by this + # deleteProc + # . mark deletion rather happen through [.text mark unset xxx] + # which was not used _up to this point of the code_ (it + # is a bit later just before exiting the present proc) set nUndoMarks [llength $undoMarks] set n [expr {$nUndoMarks / 2}] set undoMarks [lsort -dictionary $undoMarks] -- cgit v0.12 From 41d3c8f67e62fe307c46c91e5a42b71e39c59334 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 16 Sep 2016 07:49:21 +0000 Subject: Patch from Christian Werner, for evaluation --- generic/tkEntry.c | 16 +++++++- generic/tkFont.c | 41 ++++++++++++++++++++ generic/tkInt.h | 4 ++ generic/tkText.c | 8 ++++ generic/tkTextIndex.c | 12 +++++- generic/tkUtil.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++ generic/ttk/ttkEntry.c | 16 +++++++- unix/tkUnixFont.c | 6 ++- unix/tkUnixRFont.c | 12 +++++- win/tkWinFont.c | 37 ++++++++++++++---- win/tkWinKey.c | 3 +- 11 files changed, 241 insertions(+), 16 deletions(-) diff --git a/generic/tkEntry.c b/generic/tkEntry.c index a66cf18..5faf4ef 100644 --- a/generic/tkEntry.c +++ b/generic/tkEntry.c @@ -1926,7 +1926,6 @@ EntryComputeGeometry( */ if (entryPtr->showChar != NULL) { - Tcl_UniChar ch; char buf[4]; int size; @@ -1936,8 +1935,15 @@ EntryComputeGeometry( * characters might end up looking like one valid UTF character in the * resulting string. */ +#if TCL_UTF_MAX == 4 + int ch; + + TkUtfToUniChar32(entryPtr->showChar, &ch); +#else + Tcl_UniChar ch; Tcl_UtfToUniChar(entryPtr->showChar, &ch); +#endif size = Tcl_UniCharToUtf(ch, buf); entryPtr->numDisplayBytes = entryPtr->numChars * size; @@ -3414,7 +3420,11 @@ ExpandPercents( * list element. */ int number, length; register const char *string; +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif char numStorage[2*TCL_INTEGER_SPACE]; while (1) { @@ -3447,7 +3457,11 @@ ExpandPercents( before++; /* skip over % */ if (*before != '\0') { +#if TCL_UTF_MAX == 4 + before += TkUtfToUniChar32(before, &ch); +#else before += Tcl_UtfToUniChar(before, &ch); +#endif } else { ch = '%'; } diff --git a/generic/tkFont.c b/generic/tkFont.c index 1ffac16..3088959 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -497,7 +497,11 @@ Tk_FontObjCmd( const char *s; Tk_Font tkfont; Tcl_Obj *optPtr, *charPtr, *resultPtr; +#if TCL_UTF_MAX == 4 int uniChar = 0; +#else + Tcl_UniChar uniChar = 0; +#endif const TkFontAttributes *faPtr; TkFontAttributes fa; @@ -562,6 +566,10 @@ Tk_FontObjCmd( */ if (charPtr != NULL) { +#if TCL_UTF_MAX == 4 + Tcl_UniChar *ucPtr; +#endif + if (Tcl_GetCharLength(charPtr) != 1) { resultPtr = Tcl_NewStringObj( "expected a single character but got \"", -1); @@ -572,7 +580,18 @@ Tk_FontObjCmd( Tcl_SetErrorCode(interp, "TK", "VALUE", "FONT_SAMPLE", NULL); return TCL_ERROR; } +#if TCL_UTF_MAX == 4 + ucPtr = Tcl_GetUnicodeFromObj(charPtr, NULL); + uniChar = *ucPtr; + if (((uniChar & 0xFC00) == 0xD800) && (ucPtr[1] != 0x000)) { + if ((ucPtr[1] & 0xFC00) == 0xDC00) { + uniChar = ((uniChar & 0x3FF) << 10) + (ucPtr[1] & 0x3FF); + uniChar += 0x10000; + } + } +#else uniChar = Tcl_GetUniChar(charPtr, 0); +#endif } /* @@ -1694,7 +1713,11 @@ Tk_PostscriptFontName( } else if (strcasecmp(family, "ZapfDingbats") == 0) { family = "ZapfDingbats"; } else { +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif /* * Inline, capitalize the first letter of each word, lowercase the @@ -1712,7 +1735,11 @@ Tk_PostscriptFontName( src++; upper = 1; } +#if TCL_UTF_MAX == 4 + src += TkUtfToUniChar32(src, &ch); +#else src += Tcl_UtfToUniChar(src, &ch); +#endif if (upper) { ch = (Tcl_UniChar) Tcl_UniCharToUpper(ch); upper = 0; @@ -3249,7 +3276,11 @@ Tk_TextLayoutToPostscript( int i, j, len; const char *p, *glyphname; char uindex[5], c, *ps; +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif Tcl_AppendToObj(psObj, "[(", -1); for (i = 0; i < layoutPtr->numChunks; i++, chunkPtr++) { @@ -3272,7 +3303,11 @@ Tk_TextLayoutToPostscript( * international postscript fonts. */ +#if TCL_UTF_MAX == 4 + p += TkUtfToUniChar32(p, &ch); +#else p += Tcl_UtfToUniChar(p, &ch); +#endif if ((ch == '(') || (ch == ')') || (ch == '\\') || (ch < 0x20)) { /* * Tricky point: the "03" is necessary in the sprintf below, @@ -3298,6 +3333,11 @@ Tk_TextLayoutToPostscript( * use the full glyph name. */ +#if TCL_UTF_MAX > 3 + if (ch > 0xffff) { + goto noMapping; + } +#endif sprintf(uindex, "%04X", ch); /* endianness? */ glyphname = Tcl_GetVar2(interp, "::tk::psglyphs", uindex, 0); if (glyphname) { @@ -3318,6 +3358,7 @@ Tk_TextLayoutToPostscript( * No known mapping for the character into the space of * PostScript glyphs. Ignore it. :-( */ +noMapping: ; #ifdef TK_DEBUG_POSTSCRIPT_OUTPUT fprintf(stderr, "Warning: no mapping to PostScript " diff --git a/generic/tkInt.h b/generic/tkInt.h index 0b502e4..367ef3a 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1232,6 +1232,10 @@ MODULE_SCOPE Status TkParseColor (Display * display, MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); #endif +#if TCL_UTF_MAX == 4 +MODULE_SCOPE int TkUtfToUniChar32(const char *src, int *chPtr); +#endif + /* * Unsupported commands. */ diff --git a/generic/tkText.c b/generic/tkText.c index 1227e7b..834e842 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -4508,7 +4508,11 @@ TkTextGetTabs( Tcl_Obj **objv; TkTextTabArray *tabArrayPtr; TkTextTab *tabPtr; +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif double prevStop, lastStop; /* * Map these strings to TkTextTabAlign values. @@ -4615,7 +4619,11 @@ TkTextGetTabs( * There may be a more efficient way of getting this. */ +#if TCL_UTF_MAX == 4 + TkUtfToUniChar32(Tcl_GetString(objv[i+1]), &ch); +#else Tcl_UtfToUniChar(Tcl_GetString(objv[i+1]), &ch); +#endif if (!Tcl_UniCharIsAlpha(ch)) { continue; } diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index 92ca03b..f64a6d2 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -2298,9 +2298,13 @@ StartEnd( int chSize = 1; if (segPtr->typePtr == &tkTextCharType) { +#if TCL_UTF_MAX == 4 + int ch; + chSize = TkUtfToUniChar32(segPtr->body.chars + offset, &ch); +#else Tcl_UniChar ch; - chSize = Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch); +#endif if (!Tcl_UniCharIsWordChar(ch)) { break; } @@ -2343,9 +2347,13 @@ StartEnd( int chSize = 1; if (segPtr->typePtr == &tkTextCharType) { +#if TCL_UTF_MAX == 4 + int ch; + TkUtfToUniChar32(segPtr->body.chars + offset, &ch); +#else Tcl_UniChar ch; - Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch); +#endif if (!Tcl_UniCharIsWordChar(ch)) { break; } diff --git a/generic/tkUtil.c b/generic/tkUtil.c index d4c4d2d..fb796fd 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1192,6 +1192,108 @@ TkSendVirtualEvent( Tk_QueueWindowEvent(&event.general, TCL_QUEUE_TAIL); } + +#if TCL_UTF_MAX == 4 +/* + *--------------------------------------------------------------------------- + * + * TkUtfToUniChar32 -- + * + * Copied from Tcl_UtfToUniChar but using int instead of Tcl_UniChar! + * + * Extract the Tcl_UniChar represented by the UTF-8 string. Bad UTF-8 + * sequences are converted to valid Tcl_UniChars and processing + * continues. Equivalent to Plan 9 chartorune(). + * + * The caller must ensure that the source buffer is long enough that this + * routine does not run off the end and dereference non-existent memory + * looking for trail bytes. If the source buffer is known to be '\0' + * terminated, this cannot happen. Otherwise, the caller should call + * Tcl_UtfCharComplete() before calling this routine to ensure that + * enough bytes remain in the string. + * + * Results: + * *chPtr is filled with the Tcl_UniChar, and the return value is the + * number of bytes from the UTF-8 string that were consumed. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkUtfToUniChar32( + const char *src, /* The UTF-8 string. */ + int *chPtr) /* Filled with the Tcl_UniChar represented by + * the UTF-8 string. */ +{ + int byte; + + /* + * Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. + */ + + byte = *((unsigned char *) src); + if (byte < 0xC0) { + /* + * Handles properly formed UTF-8 characters between 0x01 and 0x7F. + * Also treats \0 and naked trail bytes 0x80 to 0xBF as valid + * characters representing themselves. + */ + + *chPtr = byte; + return 1; + } else if (byte < 0xE0) { + if ((src[1] & 0xC0) == 0x80) { + /* + * Two-byte-character lead-byte followed by a trail-byte. + */ + + *chPtr = ((byte & 0x1F) << 6) | (src[1] & 0x3F); + return 2; + } + + /* + * A two-byte-character lead-byte not followed by trail-byte + * represents itself. + */ + } else if (byte < 0xF0) { + if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80)) { + /* + * Three-byte-character lead byte followed by two trail bytes. + */ + + *chPtr = ((byte & 0x0F) << 12) + | ((src[1] & 0x3F) << 6) | (src[2] & 0x3F); + return 3; + } + + /* + * A three-byte-character lead-byte not followed by two trail-bytes + * represents itself. + */ + } else if (byte < 0xF8) { + if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80) && ((src[3] & 0xC0) == 0x80)) { + /* + * Four-byte-character lead byte followed by three trail bytes. + */ + + *chPtr = ((byte & 0x0E) << 18) | ((src[1] & 0x3F) << 12) + | ((src[2] & 0x3F) << 6) | (src[3] & 0x3F); + return 4; + } + + /* + * A three-byte-character lead-byte not followed by two trail-bytes + * represents itself. + */ + } + + *chPtr = byte; + return 1; +} +#endif /* * Local Variables: * mode: c diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c index d80e1fd..63ebc5f 100644 --- a/generic/ttk/ttkEntry.c +++ b/generic/ttk/ttkEntry.c @@ -282,10 +282,16 @@ static char *EntryDisplayString(const char *showChar, int numChars) { char *displayString, *p; int size; - Tcl_UniChar ch; char buf[4]; +#if TCL_UTF_MAX == 4 + int ch; + + TkUtfToUniChar32(showChar, &ch); +#else + Tcl_UniChar ch; Tcl_UtfToUniChar(showChar, &ch); +#endif size = Tcl_UniCharToUtf(ch, buf); p = displayString = ckalloc(numChars * size + 1); @@ -406,7 +412,11 @@ ExpandPercents( int number, length; const char *string; int stringLength; +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif char numStorage[2*TCL_INTEGER_SPACE]; while (*template) { @@ -430,7 +440,11 @@ ExpandPercents( */ ++template; /* skip over % */ if (*template != '\0') { +#if TCL_UTF_MAX == 4 + template += TkUtfToUniChar32(template, &ch); +#else template += Tcl_UtfToUniChar(template, &ch); +#endif } else { ch = '%'; } diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index a6826b5..b10fddd 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -35,9 +35,13 @@ static const char *const encodingList[] = { * family": the foundry, face name, and charset. */ +#if TCL_UTF_MAX > 3 +#define FONTMAP_SHIFT 12 +#define FONTMAP_PAGES (1 << (21 - FONTMAP_SHIFT)) +#else #define FONTMAP_SHIFT 10 - #define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) +#endif #define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) typedef struct FontFamily { diff --git a/unix/tkUnixRFont.c b/unix/tkUnixRFont.c index 01bbb30..ee87657 100644 --- a/unix/tkUnixRFont.c +++ b/unix/tkUnixRFont.c @@ -54,6 +54,12 @@ typedef struct ThreadSpecificData { Region clipRegion; /* The clipping region, or None. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; + +#if TCL_UTF_MAX == 4 +#define UtfToUniChar(src, chPtr) TkUtfToUniChar32(src, chPtr) +#else +#define UtfToUniChar(src, chPtr) Tcl_UtfToUniChar(src, chPtr) +#endif /* * Package initialization: @@ -668,9 +674,13 @@ Tk_MeasureChars( curByte = 0; sawNonSpace = 0; while (numBytes > 0) { +#if TCL_UTF_MAX == 4 + int unichar; +#else Tcl_UniChar unichar; +#endif - clen = Tcl_UtfToUniChar(source, &unichar); + clen = UtfToUniChar(source, &unichar); c = (FcChar32) unichar; if (clen <= 0) { diff --git a/win/tkWinFont.c b/win/tkWinFont.c index 10ea1b9..e413e7d 100644 --- a/win/tkWinFont.c +++ b/win/tkWinFont.c @@ -26,9 +26,13 @@ * Under Windows, a "font family" is uniquely identified by its face name. */ -#define FONTMAP_SHIFT 10 - -#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) +#if TCL_UTF_MAX > 3 +#define FONTMAP_SHIFT 12 +#define FONTMAP_PAGES (1 << (21 - FONTMAP_SHIFT)) +#else +#define FONTMAP_SHIFT 10 +#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) +#endif #define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) typedef struct FontFamily { @@ -229,6 +233,11 @@ static inline HFONT SelectFont(HDC hdc, WinFont *fontPtr, SubFont *subFontPtr, double angle); static inline void SwapLong(PULONG p); static inline void SwapShort(USHORT *p); +#if TCL_UTF_MAX == 4 +#define UtfToUniChar(src, chPtr) TkUtfToUniChar32(src, chPtr) +#else +#define UtfToUniChar(src, chPtr) Tcl_UtfToUniChar(src, chPtr) +#endif static int CALLBACK WinFontCanUseProc(ENUMLOGFONT *lfPtr, NEWTEXTMETRIC *tmPtr, int fontType, LPARAM lParam); @@ -828,7 +837,11 @@ Tk_MeasureChars( HFONT oldFont; WinFont *fontPtr; int curX, moretomeasure; +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif SIZE size; FontFamily *familyPtr; Tcl_DString runString; @@ -859,7 +872,7 @@ Tk_MeasureChars( start = source; end = start + numBytes; for (p = start; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); + next = p + UtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { familyPtr = lastSubFontPtr->familyPtr; @@ -921,7 +934,7 @@ Tk_MeasureChars( familyPtr = lastSubFontPtr->familyPtr; Tcl_DStringInit(&runString); for (p = start; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); + next = p + UtfToUniChar(p, &ch); Tcl_UtfToExternal(NULL, familyPtr->encoding, p, (int) (next - p), 0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL); @@ -970,13 +983,17 @@ Tk_MeasureChars( */ const char *lastWordBreak = NULL; +#if TCL_UTF_MAX == 4 + int ch2; +#else Tcl_UniChar ch2; +#endif end = p; p = source; ch = ' '; while (p < end) { - next = p + Tcl_UtfToUniChar(p, &ch2); + next = p + UtfToUniChar(p, &ch2); if ((ch != ' ') && (ch2 == ' ')) { lastWordBreak = p; } @@ -1443,7 +1460,11 @@ MultiFontTextOut( * string when drawing. */ double angle) { +#if TCL_UTF_MAX == 4 + int ch; +#else Tcl_UniChar ch; +#endif SIZE size; HFONT oldFont; FontFamily *familyPtr; @@ -1458,7 +1479,7 @@ MultiFontTextOut( end = source + numBytes; for (p = source; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); + next = p + UtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { if (p > source) { @@ -2188,7 +2209,7 @@ FontMapLoadPage( { FontFamily *familyPtr; Tcl_Encoding encoding; - char src[XMaxTransChars], buf[16]; + char src[TCL_UTF_MAX], buf[16]; USHORT *startCount, *endCount; int i, j, bitOffset, end, segCount; diff --git a/win/tkWinKey.c b/win/tkWinKey.c index 2698c4d..815ff3b 100644 --- a/win/tkWinKey.c +++ b/win/tkWinKey.c @@ -113,7 +113,6 @@ TkpGetString( Tcl_DStringAppend(dsPtr, buf, len); } else if (keyEv->send_event == -3) { - char buf[XMaxTransChars]; int len; @@ -122,7 +121,7 @@ TkpGetString( */ len = Tcl_UniCharToUtf(keyEv->keycode, buf); - if ((keyEv->keycode <= 0xffff) || (len == XMaxTransChars)) { + if ((keyEv->keycode <= 0xffff) || (len > 3)) { Tcl_DStringAppend(dsPtr, buf, len); } else { Tcl_UniCharToUtf(((keyEv->keycode - 0x10000) >> 10) | 0xd800, buf); -- cgit v0.12 From 7a5d64437977cc14b7b79e336b3b2a20785348e5 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 16 Sep 2016 12:17:59 +0000 Subject: Rename TkUtfToUniChar32 to TkUtfToUniChar2, and various simplifications needing less #ifdef's --- generic/tkEntry.c | 16 +------- generic/tkFont.c | 47 +++--------------------- generic/tkInt.h | 6 ++- generic/tkText.c | 10 +---- generic/tkTextDisp.c | 4 +- generic/tkTextIndex.c | 16 ++------ generic/tkUtil.c | 99 +++++++++++--------------------------------------- generic/ttk/ttkEntry.c | 16 +------- unix/tkUnixFont.c | 5 --- unix/tkUnixRFont.c | 12 +----- win/tkWinFont.c | 32 +++------------- win/tkWinKey.c | 3 +- 12 files changed, 49 insertions(+), 217 deletions(-) diff --git a/generic/tkEntry.c b/generic/tkEntry.c index 5faf4ef..a66cf18 100644 --- a/generic/tkEntry.c +++ b/generic/tkEntry.c @@ -1926,6 +1926,7 @@ EntryComputeGeometry( */ if (entryPtr->showChar != NULL) { + Tcl_UniChar ch; char buf[4]; int size; @@ -1935,15 +1936,8 @@ EntryComputeGeometry( * characters might end up looking like one valid UTF character in the * resulting string. */ -#if TCL_UTF_MAX == 4 - int ch; - - TkUtfToUniChar32(entryPtr->showChar, &ch); -#else - Tcl_UniChar ch; Tcl_UtfToUniChar(entryPtr->showChar, &ch); -#endif size = Tcl_UniCharToUtf(ch, buf); entryPtr->numDisplayBytes = entryPtr->numChars * size; @@ -3420,11 +3414,7 @@ ExpandPercents( * list element. */ int number, length; register const char *string; -#if TCL_UTF_MAX == 4 - int ch; -#else Tcl_UniChar ch; -#endif char numStorage[2*TCL_INTEGER_SPACE]; while (1) { @@ -3457,11 +3447,7 @@ ExpandPercents( before++; /* skip over % */ if (*before != '\0') { -#if TCL_UTF_MAX == 4 - before += TkUtfToUniChar32(before, &ch); -#else before += Tcl_UtfToUniChar(before, &ch); -#endif } else { ch = '%'; } diff --git a/generic/tkFont.c b/generic/tkFont.c index 3088959..4a45691 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -497,11 +497,7 @@ Tk_FontObjCmd( const char *s; Tk_Font tkfont; Tcl_Obj *optPtr, *charPtr, *resultPtr; -#if TCL_UTF_MAX == 4 int uniChar = 0; -#else - Tcl_UniChar uniChar = 0; -#endif const TkFontAttributes *faPtr; TkFontAttributes fa; @@ -566,32 +562,19 @@ Tk_FontObjCmd( */ if (charPtr != NULL) { -#if TCL_UTF_MAX == 4 - Tcl_UniChar *ucPtr; -#endif + const char *string = Tcl_GetString(charPtr); + int len = TkUtfToUniChar2(string, &uniChar); - if (Tcl_GetCharLength(charPtr) != 1) { + if (len != charPtr->length) { resultPtr = Tcl_NewStringObj( "expected a single character but got \"", -1); - Tcl_AppendLimitedToObj(resultPtr, Tcl_GetString(charPtr), + Tcl_AppendLimitedToObj(resultPtr, string, -1, 40, "..."); Tcl_AppendToObj(resultPtr, "\"", -1); Tcl_SetObjResult(interp, resultPtr); Tcl_SetErrorCode(interp, "TK", "VALUE", "FONT_SAMPLE", NULL); return TCL_ERROR; } -#if TCL_UTF_MAX == 4 - ucPtr = Tcl_GetUnicodeFromObj(charPtr, NULL); - uniChar = *ucPtr; - if (((uniChar & 0xFC00) == 0xD800) && (ucPtr[1] != 0x000)) { - if ((ucPtr[1] & 0xFC00) == 0xDC00) { - uniChar = ((uniChar & 0x3FF) << 10) + (ucPtr[1] & 0x3FF); - uniChar += 0x10000; - } - } -#else - uniChar = Tcl_GetUniChar(charPtr, 0); -#endif } /* @@ -1713,11 +1696,7 @@ Tk_PostscriptFontName( } else if (strcasecmp(family, "ZapfDingbats") == 0) { family = "ZapfDingbats"; } else { -#if TCL_UTF_MAX == 4 int ch; -#else - Tcl_UniChar ch; -#endif /* * Inline, capitalize the first letter of each word, lowercase the @@ -1735,11 +1714,7 @@ Tk_PostscriptFontName( src++; upper = 1; } -#if TCL_UTF_MAX == 4 - src += TkUtfToUniChar32(src, &ch); -#else - src += Tcl_UtfToUniChar(src, &ch); -#endif + src += TkUtfToUniChar2(src, &ch); if (upper) { ch = (Tcl_UniChar) Tcl_UniCharToUpper(ch); upper = 0; @@ -3276,11 +3251,7 @@ Tk_TextLayoutToPostscript( int i, j, len; const char *p, *glyphname; char uindex[5], c, *ps; -#if TCL_UTF_MAX == 4 int ch; -#else - Tcl_UniChar ch; -#endif Tcl_AppendToObj(psObj, "[(", -1); for (i = 0; i < layoutPtr->numChunks; i++, chunkPtr++) { @@ -3303,11 +3274,7 @@ Tk_TextLayoutToPostscript( * international postscript fonts. */ -#if TCL_UTF_MAX == 4 - p += TkUtfToUniChar32(p, &ch); -#else - p += Tcl_UtfToUniChar(p, &ch); -#endif + p += TkUtfToUniChar2(p, &ch); if ((ch == '(') || (ch == ')') || (ch == '\\') || (ch < 0x20)) { /* * Tricky point: the "03" is necessary in the sprintf below, @@ -3333,11 +3300,9 @@ Tk_TextLayoutToPostscript( * use the full glyph name. */ -#if TCL_UTF_MAX > 3 if (ch > 0xffff) { goto noMapping; } -#endif sprintf(uindex, "%04X", ch); /* endianness? */ glyphname = Tcl_GetVar2(interp, "::tk::psglyphs", uindex, 0); if (glyphname) { diff --git a/generic/tkInt.h b/generic/tkInt.h index 367ef3a..6d86e08 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1232,8 +1232,10 @@ MODULE_SCOPE Status TkParseColor (Display * display, MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); #endif -#if TCL_UTF_MAX == 4 -MODULE_SCOPE int TkUtfToUniChar32(const char *src, int *chPtr); +#if TCL_UTF_MAX > 4 +# define TkUtfToUniChar2 Tcl_UtfToUniChar +#else + MODULE_SCOPE int TkUtfToUniChar2(const char *src, int *chPtr); #endif /* diff --git a/generic/tkText.c b/generic/tkText.c index 834e842..dacadbe 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -4508,11 +4508,7 @@ TkTextGetTabs( Tcl_Obj **objv; TkTextTabArray *tabArrayPtr; TkTextTab *tabPtr; -#if TCL_UTF_MAX == 4 int ch; -#else - Tcl_UniChar ch; -#endif double prevStop, lastStop; /* * Map these strings to TkTextTabAlign values. @@ -4619,11 +4615,7 @@ TkTextGetTabs( * There may be a more efficient way of getting this. */ -#if TCL_UTF_MAX == 4 - TkUtfToUniChar32(Tcl_GetString(objv[i+1]), &ch); -#else - Tcl_UtfToUniChar(Tcl_GetString(objv[i+1]), &ch); -#endif + TkUtfToUniChar2(Tcl_GetString(objv[i+1]), &ch); if (!Tcl_UniCharIsAlpha(ch)) { continue; } diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 5faab36..026023e 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -7581,8 +7581,8 @@ TkTextCharLayoutProc( if (bytesThatFit < maxBytes) { if ((bytesThatFit == 0) && noCharsYet) { - Tcl_UniChar ch; - int chLen = Tcl_UtfToUniChar(p, &ch); + int ch; + int chLen = TkUtfToUniChar2(p, &ch); #if TK_LAYOUT_WITH_BASE_CHUNKS bytesThatFit = CharChunkMeasureChars(chunkPtr, line, diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index f64a6d2..b794cdb 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -2298,13 +2298,9 @@ StartEnd( int chSize = 1; if (segPtr->typePtr == &tkTextCharType) { -#if TCL_UTF_MAX == 4 int ch; - chSize = TkUtfToUniChar32(segPtr->body.chars + offset, &ch); -#else - Tcl_UniChar ch; - chSize = Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch); -#endif + + chSize = TkUtfToUniChar2(segPtr->body.chars + offset, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } @@ -2347,13 +2343,9 @@ StartEnd( int chSize = 1; if (segPtr->typePtr == &tkTextCharType) { -#if TCL_UTF_MAX == 4 + int ch; - TkUtfToUniChar32(segPtr->body.chars + offset, &ch); -#else - Tcl_UniChar ch; - Tcl_UtfToUniChar(segPtr->body.chars + offset, &ch); -#endif + TkUtfToUniChar2(segPtr->body.chars + offset, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } diff --git a/generic/tkUtil.c b/generic/tkUtil.c index fb796fd..a266cb3 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1193,24 +1193,15 @@ TkSendVirtualEvent( Tk_QueueWindowEvent(&event.general, TCL_QUEUE_TAIL); } -#if TCL_UTF_MAX == 4 +#if TCL_UTF_MAX <= 4 /* *--------------------------------------------------------------------------- * - * TkUtfToUniChar32 -- + * TkUtfToUniChar2 -- * - * Copied from Tcl_UtfToUniChar but using int instead of Tcl_UniChar! - * - * Extract the Tcl_UniChar represented by the UTF-8 string. Bad UTF-8 - * sequences are converted to valid Tcl_UniChars and processing - * continues. Equivalent to Plan 9 chartorune(). - * - * The caller must ensure that the source buffer is long enough that this - * routine does not run off the end and dereference non-existent memory - * looking for trail bytes. If the source buffer is known to be '\0' - * terminated, this cannot happen. Otherwise, the caller should call - * Tcl_UtfCharComplete() before calling this routine to ensure that - * enough bytes remain in the string. + * Almost the same as Tcl_UtfToUniChar but using int instead of Tcl_UniChar. + * This function is capable of collapsing a upper/lower pair to a single + * unicode character. So, up to 6 bytes (two UTF-8 characters) might be read. * * Results: * *chPtr is filled with the Tcl_UniChar, and the return value is the @@ -1223,75 +1214,29 @@ TkSendVirtualEvent( */ int -TkUtfToUniChar32( +TkUtfToUniChar2( const char *src, /* The UTF-8 string. */ int *chPtr) /* Filled with the Tcl_UniChar represented by * the UTF-8 string. */ { - int byte; - - /* - * Unroll 1 to 3 byte UTF-8 sequences, use loop to handle longer ones. - */ - - byte = *((unsigned char *) src); - if (byte < 0xC0) { - /* - * Handles properly formed UTF-8 characters between 0x01 and 0x7F. - * Also treats \0 and naked trail bytes 0x80 to 0xBF as valid - * characters representing themselves. - */ - - *chPtr = byte; - return 1; - } else if (byte < 0xE0) { - if ((src[1] & 0xC0) == 0x80) { - /* - * Two-byte-character lead-byte followed by a trail-byte. - */ - - *chPtr = ((byte & 0x1F) << 6) | (src[1] & 0x3F); - return 2; + Tcl_UniChar uniChar = 0; + + int len = Tcl_UtfToUniChar(src, &uniChar); + if ((uniChar & 0xfc00) == 0xd800) { + Tcl_UniChar high = uniChar; + /* This can only happen when Tcl is compiled with TCL_UTF_MAX=4, + * or when a high surrogate character is detected */ + int len2 = Tcl_UtfToUniChar(src+len, &uniChar); + if ((uniChar & 0xfc00) == 0xdc00) { + *chPtr = ((high & 0x3ff) << 10) | (uniChar & 0x3ff) | 0x10000; + len += len2; + } else { + *chPtr = high; } - - /* - * A two-byte-character lead-byte not followed by trail-byte - * represents itself. - */ - } else if (byte < 0xF0) { - if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80)) { - /* - * Three-byte-character lead byte followed by two trail bytes. - */ - - *chPtr = ((byte & 0x0F) << 12) - | ((src[1] & 0x3F) << 6) | (src[2] & 0x3F); - return 3; - } - - /* - * A three-byte-character lead-byte not followed by two trail-bytes - * represents itself. - */ - } else if (byte < 0xF8) { - if (((src[1] & 0xC0) == 0x80) && ((src[2] & 0xC0) == 0x80) && ((src[3] & 0xC0) == 0x80)) { - /* - * Four-byte-character lead byte followed by three trail bytes. - */ - - *chPtr = ((byte & 0x0E) << 18) | ((src[1] & 0x3F) << 12) - | ((src[2] & 0x3F) << 6) | (src[3] & 0x3F); - return 4; - } - - /* - * A three-byte-character lead-byte not followed by two trail-bytes - * represents itself. - */ + } else { + *chPtr = uniChar; } - - *chPtr = byte; - return 1; + return len; } #endif /* diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c index 63ebc5f..d80e1fd 100644 --- a/generic/ttk/ttkEntry.c +++ b/generic/ttk/ttkEntry.c @@ -282,16 +282,10 @@ static char *EntryDisplayString(const char *showChar, int numChars) { char *displayString, *p; int size; - char buf[4]; -#if TCL_UTF_MAX == 4 - int ch; - - TkUtfToUniChar32(showChar, &ch); -#else Tcl_UniChar ch; + char buf[4]; Tcl_UtfToUniChar(showChar, &ch); -#endif size = Tcl_UniCharToUtf(ch, buf); p = displayString = ckalloc(numChars * size + 1); @@ -412,11 +406,7 @@ ExpandPercents( int number, length; const char *string; int stringLength; -#if TCL_UTF_MAX == 4 - int ch; -#else Tcl_UniChar ch; -#endif char numStorage[2*TCL_INTEGER_SPACE]; while (*template) { @@ -440,11 +430,7 @@ ExpandPercents( */ ++template; /* skip over % */ if (*template != '\0') { -#if TCL_UTF_MAX == 4 - template += TkUtfToUniChar32(template, &ch); -#else template += Tcl_UtfToUniChar(template, &ch); -#endif } else { ch = '%'; } diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index b10fddd..681d1d1 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -35,13 +35,8 @@ static const char *const encodingList[] = { * family": the foundry, face name, and charset. */ -#if TCL_UTF_MAX > 3 #define FONTMAP_SHIFT 12 #define FONTMAP_PAGES (1 << (21 - FONTMAP_SHIFT)) -#else -#define FONTMAP_SHIFT 10 -#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) -#endif #define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) typedef struct FontFamily { diff --git a/unix/tkUnixRFont.c b/unix/tkUnixRFont.c index ee87657..0ea1ec6 100644 --- a/unix/tkUnixRFont.c +++ b/unix/tkUnixRFont.c @@ -54,12 +54,6 @@ typedef struct ThreadSpecificData { Region clipRegion; /* The clipping region, or None. */ } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; - -#if TCL_UTF_MAX == 4 -#define UtfToUniChar(src, chPtr) TkUtfToUniChar32(src, chPtr) -#else -#define UtfToUniChar(src, chPtr) Tcl_UtfToUniChar(src, chPtr) -#endif /* * Package initialization: @@ -674,13 +668,9 @@ Tk_MeasureChars( curByte = 0; sawNonSpace = 0; while (numBytes > 0) { -#if TCL_UTF_MAX == 4 int unichar; -#else - Tcl_UniChar unichar; -#endif - clen = UtfToUniChar(source, &unichar); + clen = TkUtfToUniChar2(source, &unichar); c = (FcChar32) unichar; if (clen <= 0) { diff --git a/win/tkWinFont.c b/win/tkWinFont.c index e413e7d..30638ca 100644 --- a/win/tkWinFont.c +++ b/win/tkWinFont.c @@ -26,13 +26,8 @@ * Under Windows, a "font family" is uniquely identified by its face name. */ -#if TCL_UTF_MAX > 3 #define FONTMAP_SHIFT 12 #define FONTMAP_PAGES (1 << (21 - FONTMAP_SHIFT)) -#else -#define FONTMAP_SHIFT 10 -#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) -#endif #define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) typedef struct FontFamily { @@ -233,11 +228,6 @@ static inline HFONT SelectFont(HDC hdc, WinFont *fontPtr, SubFont *subFontPtr, double angle); static inline void SwapLong(PULONG p); static inline void SwapShort(USHORT *p); -#if TCL_UTF_MAX == 4 -#define UtfToUniChar(src, chPtr) TkUtfToUniChar32(src, chPtr) -#else -#define UtfToUniChar(src, chPtr) Tcl_UtfToUniChar(src, chPtr) -#endif static int CALLBACK WinFontCanUseProc(ENUMLOGFONT *lfPtr, NEWTEXTMETRIC *tmPtr, int fontType, LPARAM lParam); @@ -837,11 +827,7 @@ Tk_MeasureChars( HFONT oldFont; WinFont *fontPtr; int curX, moretomeasure; -#if TCL_UTF_MAX == 4 int ch; -#else - Tcl_UniChar ch; -#endif SIZE size; FontFamily *familyPtr; Tcl_DString runString; @@ -872,7 +858,7 @@ Tk_MeasureChars( start = source; end = start + numBytes; for (p = start; p < end; ) { - next = p + UtfToUniChar(p, &ch); + next = p + TkUtfToUniChar2(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { familyPtr = lastSubFontPtr->familyPtr; @@ -934,7 +920,7 @@ Tk_MeasureChars( familyPtr = lastSubFontPtr->familyPtr; Tcl_DStringInit(&runString); for (p = start; p < end; ) { - next = p + UtfToUniChar(p, &ch); + next = p + TkUtfToUniChar2(p, &ch); Tcl_UtfToExternal(NULL, familyPtr->encoding, p, (int) (next - p), 0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL); @@ -983,17 +969,13 @@ Tk_MeasureChars( */ const char *lastWordBreak = NULL; -#if TCL_UTF_MAX == 4 int ch2; -#else - Tcl_UniChar ch2; -#endif end = p; p = source; ch = ' '; while (p < end) { - next = p + UtfToUniChar(p, &ch2); + next = p + TkUtfToUniChar2(p, &ch2); if ((ch != ' ') && (ch2 == ' ')) { lastWordBreak = p; } @@ -1460,11 +1442,7 @@ MultiFontTextOut( * string when drawing. */ double angle) { -#if TCL_UTF_MAX == 4 int ch; -#else - Tcl_UniChar ch; -#endif SIZE size; HFONT oldFont; FontFamily *familyPtr; @@ -1479,7 +1457,7 @@ MultiFontTextOut( end = source + numBytes; for (p = source; p < end; ) { - next = p + UtfToUniChar(p, &ch); + next = p + TkUtfToUniChar2(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { if (p > source) { @@ -2209,7 +2187,7 @@ FontMapLoadPage( { FontFamily *familyPtr; Tcl_Encoding encoding; - char src[TCL_UTF_MAX], buf[16]; + char src[XMaxTransChars], buf[16]; USHORT *startCount, *endCount; int i, j, bitOffset, end, segCount; diff --git a/win/tkWinKey.c b/win/tkWinKey.c index 815ff3b..2698c4d 100644 --- a/win/tkWinKey.c +++ b/win/tkWinKey.c @@ -113,6 +113,7 @@ TkpGetString( Tcl_DStringAppend(dsPtr, buf, len); } else if (keyEv->send_event == -3) { + char buf[XMaxTransChars]; int len; @@ -121,7 +122,7 @@ TkpGetString( */ len = Tcl_UniCharToUtf(keyEv->keycode, buf); - if ((keyEv->keycode <= 0xffff) || (len > 3)) { + if ((keyEv->keycode <= 0xffff) || (len == XMaxTransChars)) { Tcl_DStringAppend(dsPtr, buf, len); } else { Tcl_UniCharToUtf(((keyEv->keycode - 0x10000) >> 10) | 0xd800, buf); -- cgit v0.12 From bdb580aa2563588e6e1f5090bb9b89369e2f2d4f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Mon, 19 Sep 2016 10:14:52 +0000 Subject: More simplifications --- generic/tkEntry.c | 8 ++++---- generic/tkFont.c | 6 +++--- generic/tkInt.h | 6 ++++-- generic/tkText.c | 6 +++--- generic/tkTextDisp.c | 2 +- generic/tkTextIndex.c | 4 ++-- generic/tkUtil.c | 37 +++++++++++++++++++++++++++++++++++-- generic/ttk/ttkEntry.c | 14 +++++++------- unix/tkUnixFont.c | 20 ++++++++++++++------ unix/tkUnixRFont.c | 2 +- win/tkWinFont.c | 8 ++++---- win/tkWinKey.c | 38 +++++--------------------------------- win/tkWinX.c | 13 ++++++------- 13 files changed, 89 insertions(+), 75 deletions(-) diff --git a/generic/tkEntry.c b/generic/tkEntry.c index a66cf18..34f11d2 100644 --- a/generic/tkEntry.c +++ b/generic/tkEntry.c @@ -1926,8 +1926,8 @@ EntryComputeGeometry( */ if (entryPtr->showChar != NULL) { - Tcl_UniChar ch; - char buf[4]; + int ch; + char buf[6]; int size; /* @@ -1937,8 +1937,8 @@ EntryComputeGeometry( * resulting string. */ - Tcl_UtfToUniChar(entryPtr->showChar, &ch); - size = Tcl_UniCharToUtf(ch, buf); + TkUtfToUniChar(entryPtr->showChar, &ch); + size = TkUniCharToUtf(ch, buf); entryPtr->numDisplayBytes = entryPtr->numChars * size; p = ckalloc(entryPtr->numDisplayBytes + 1); diff --git a/generic/tkFont.c b/generic/tkFont.c index 4a45691..1947666 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -563,7 +563,7 @@ Tk_FontObjCmd( if (charPtr != NULL) { const char *string = Tcl_GetString(charPtr); - int len = TkUtfToUniChar2(string, &uniChar); + int len = TkUtfToUniChar(string, &uniChar); if (len != charPtr->length) { resultPtr = Tcl_NewStringObj( @@ -1714,7 +1714,7 @@ Tk_PostscriptFontName( src++; upper = 1; } - src += TkUtfToUniChar2(src, &ch); + src += TkUtfToUniChar(src, &ch); if (upper) { ch = (Tcl_UniChar) Tcl_UniCharToUpper(ch); upper = 0; @@ -3274,7 +3274,7 @@ Tk_TextLayoutToPostscript( * international postscript fonts. */ - p += TkUtfToUniChar2(p, &ch); + p += TkUtfToUniChar(p, &ch); if ((ch == '(') || (ch == ')') || (ch == '\\') || (ch < 0x20)) { /* * Tricky point: the "03" is necessary in the sprintf below, diff --git a/generic/tkInt.h b/generic/tkInt.h index 6d86e08..1615a81 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1233,9 +1233,11 @@ MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); #endif #if TCL_UTF_MAX > 4 -# define TkUtfToUniChar2 Tcl_UtfToUniChar +# define TkUtfToUniChar Tcl_UtfToUniChar +# define TkUniCharToUtf Tcl_UniCharToUtf #else - MODULE_SCOPE int TkUtfToUniChar2(const char *src, int *chPtr); + MODULE_SCOPE int TkUtfToUniChar(const char *, int *); + MODULE_SCOPE int TkUniCharToUtf(int, char *); #endif /* diff --git a/generic/tkText.c b/generic/tkText.c index dacadbe..8ae17a5 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -4615,7 +4615,7 @@ TkTextGetTabs( * There may be a more efficient way of getting this. */ - TkUtfToUniChar2(Tcl_GetString(objv[i+1]), &ch); + TkUtfToUniChar(Tcl_GetString(objv[i+1]), &ch); if (!Tcl_UniCharIsAlpha(ch)) { continue; } @@ -5966,7 +5966,7 @@ SearchCore( CLANG_ASSERT(pattern); do { - Tcl_UniChar ch; + int ch; const char *p; int lastFullLine = lastOffset; @@ -6196,7 +6196,7 @@ SearchCore( } } else { firstOffset = p - startOfLine + - Tcl_UtfToUniChar(startOfLine+matchOffset,&ch); + TkUtfToUniChar(startOfLine+matchOffset,&ch); } } } while (searchSpecPtr->all); diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 026023e..eb917cf 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -7582,7 +7582,7 @@ TkTextCharLayoutProc( if (bytesThatFit < maxBytes) { if ((bytesThatFit == 0) && noCharsYet) { int ch; - int chLen = TkUtfToUniChar2(p, &ch); + int chLen = TkUtfToUniChar(p, &ch); #if TK_LAYOUT_WITH_BASE_CHUNKS bytesThatFit = CharChunkMeasureChars(chunkPtr, line, diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index b794cdb..7aebc84 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -2300,7 +2300,7 @@ StartEnd( if (segPtr->typePtr == &tkTextCharType) { int ch; - chSize = TkUtfToUniChar2(segPtr->body.chars + offset, &ch); + chSize = TkUtfToUniChar(segPtr->body.chars + offset, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } @@ -2345,7 +2345,7 @@ StartEnd( if (segPtr->typePtr == &tkTextCharType) { int ch; - TkUtfToUniChar2(segPtr->body.chars + offset, &ch); + TkUtfToUniChar(segPtr->body.chars + offset, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } diff --git a/generic/tkUtil.c b/generic/tkUtil.c index a266cb3..19b343e 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1197,7 +1197,7 @@ TkSendVirtualEvent( /* *--------------------------------------------------------------------------- * - * TkUtfToUniChar2 -- + * TkUtfToUniChar -- * * Almost the same as Tcl_UtfToUniChar but using int instead of Tcl_UniChar. * This function is capable of collapsing a upper/lower pair to a single @@ -1214,7 +1214,7 @@ TkSendVirtualEvent( */ int -TkUtfToUniChar2( +TkUtfToUniChar( const char *src, /* The UTF-8 string. */ int *chPtr) /* Filled with the Tcl_UniChar represented by * the UTF-8 string. */ @@ -1238,6 +1238,39 @@ TkUtfToUniChar2( } return len; } + +/* + *--------------------------------------------------------------------------- + * + * TkUniCharToUtf -- + * + * Almost the same as Tcl_UniCharToUtf but producing surrogates if + * TCL_UTF_MAX==3. + * + * Results: + * *buf is filled with the UTF-8 string, and the return value is the + * number of bytes produced. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int TkUniCharToUtf(int ch, char *buf) +{ + int size = Tcl_UniCharToUtf(ch, buf); + if ((ch > 0xffff) && (size < 4)) { + /* Hey, this is wrong, we must be running TCL_UTF_MAX==3 + * The best thing we can do is spit out 2 surrogates */ + ch -= 0x10000; + size = Tcl_UniCharToUtf(((ch >> 10) | 0xd800), buf); + size += Tcl_UniCharToUtf(((ch & 0x3ff) | 0xdc00), buf+size); + } + return size; +} + + #endif /* * Local Variables: diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c index d80e1fd..a25574a 100644 --- a/generic/ttk/ttkEntry.c +++ b/generic/ttk/ttkEntry.c @@ -282,11 +282,11 @@ static char *EntryDisplayString(const char *showChar, int numChars) { char *displayString, *p; int size; - Tcl_UniChar ch; - char buf[4]; + int ch; + char buf[6]; - Tcl_UtfToUniChar(showChar, &ch); - size = Tcl_UniCharToUtf(ch, buf); + TkUtfToUniChar(showChar, &ch); + size = TkUniCharToUtf(ch, buf); p = displayString = ckalloc(numChars * size + 1); while (numChars--) { @@ -406,7 +406,7 @@ ExpandPercents( int number, length; const char *string; int stringLength; - Tcl_UniChar ch; + int ch; char numStorage[2*TCL_INTEGER_SPACE]; while (*template) { @@ -430,7 +430,7 @@ ExpandPercents( */ ++template; /* skip over % */ if (*template != '\0') { - template += Tcl_UtfToUniChar(template, &ch); + template += TkUtfToUniChar(template, &ch); } else { ch = '%'; } @@ -480,7 +480,7 @@ ExpandPercents( string = Tk_PathName(entryPtr->core.tkwin); break; default: - length = Tcl_UniCharToUtf(ch, numStorage); + length = TkUniCharToUtf(ch, numStorage); numStorage[length] = '\0'; string = numStorage; break; diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c index 681d1d1..ce4eca9 100644 --- a/unix/tkUnixFont.c +++ b/unix/tkUnixFont.c @@ -405,7 +405,7 @@ ControlUtfProc( { const char *srcStart, *srcEnd; char *dstStart, *dstEnd; - Tcl_UniChar ch; + int ch; int result; static char hexChars[] = "0123456789abcdef"; static char mapChars[] = { @@ -426,9 +426,9 @@ ControlUtfProc( result = TCL_CONVERT_NOSPACE; break; } - src += Tcl_UtfToUniChar(src, &ch); + src += TkUtfToUniChar(src, &ch); dst[0] = '\\'; - if ((ch < sizeof(mapChars)) && (mapChars[ch] != 0)) { + if (((size_t) ch < sizeof(mapChars)) && (mapChars[ch] != 0)) { dst[1] = mapChars[ch]; dst += 2; } else if (ch < 256) { @@ -436,13 +436,21 @@ ControlUtfProc( dst[2] = hexChars[(ch >> 4) & 0xf]; dst[3] = hexChars[ch & 0xf]; dst += 4; - } else { + } else if (ch < 0x10000) { dst[1] = 'u'; dst[2] = hexChars[(ch >> 12) & 0xf]; dst[3] = hexChars[(ch >> 8) & 0xf]; dst[4] = hexChars[(ch >> 4) & 0xf]; dst[5] = hexChars[ch & 0xf]; dst += 6; + } else { + /* TODO we can do better here */ + dst[1] = 'u'; + dst[2] = 'f'; + dst[3] = 'f'; + dst[4] = 'f'; + dst[5] = 'd'; + dst += 6; } } *srcReadPtr = src - srcStart; @@ -1027,7 +1035,7 @@ Tk_MeasureChars( curByte = 0; } else if (maxLength < 0) { const char *p, *end, *next; - Tcl_UniChar ch; + int ch; SubFont *thisSubFontPtr; FontFamily *familyPtr; Tcl_DString runString; @@ -1043,7 +1051,7 @@ Tk_MeasureChars( curX = 0; end = source + numBytes; for (p = source; p < end; ) { - next = p + Tcl_UtfToUniChar(p, &ch); + next = p + TkUtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { familyPtr = lastSubFontPtr->familyPtr; diff --git a/unix/tkUnixRFont.c b/unix/tkUnixRFont.c index 0ea1ec6..cf4127d 100644 --- a/unix/tkUnixRFont.c +++ b/unix/tkUnixRFont.c @@ -670,7 +670,7 @@ Tk_MeasureChars( while (numBytes > 0) { int unichar; - clen = TkUtfToUniChar2(source, &unichar); + clen = TkUtfToUniChar(source, &unichar); c = (FcChar32) unichar; if (clen <= 0) { diff --git a/win/tkWinFont.c b/win/tkWinFont.c index 30638ca..7c6c0ba 100644 --- a/win/tkWinFont.c +++ b/win/tkWinFont.c @@ -858,7 +858,7 @@ Tk_MeasureChars( start = source; end = start + numBytes; for (p = start; p < end; ) { - next = p + TkUtfToUniChar2(p, &ch); + next = p + TkUtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { familyPtr = lastSubFontPtr->familyPtr; @@ -920,7 +920,7 @@ Tk_MeasureChars( familyPtr = lastSubFontPtr->familyPtr; Tcl_DStringInit(&runString); for (p = start; p < end; ) { - next = p + TkUtfToUniChar2(p, &ch); + next = p + TkUtfToUniChar(p, &ch); Tcl_UtfToExternal(NULL, familyPtr->encoding, p, (int) (next - p), 0, NULL, buf, sizeof(buf), NULL, &dstWrote, NULL); @@ -975,7 +975,7 @@ Tk_MeasureChars( p = source; ch = ' '; while (p < end) { - next = p + TkUtfToUniChar2(p, &ch2); + next = p + TkUtfToUniChar(p, &ch2); if ((ch != ' ') && (ch2 == ' ')) { lastWordBreak = p; } @@ -1457,7 +1457,7 @@ MultiFontTextOut( end = source + numBytes; for (p = source; p < end; ) { - next = p + TkUtfToUniChar2(p, &ch); + next = p + TkUtfToUniChar(p, &ch); thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); if (thisSubFontPtr != lastSubFontPtr) { if (p > source) { diff --git a/win/tkWinKey.c b/win/tkWinKey.c index 2698c4d..7fee101 100644 --- a/win/tkWinKey.c +++ b/win/tkWinKey.c @@ -88,6 +88,8 @@ TkpGetString( * result. */ { XKeyEvent *keyEv = &eventPtr->xkey; + int len; + char buf[6]; Tcl_DStringInit(dsPtr); if (keyEv->send_event == -1) { @@ -95,41 +97,14 @@ TkpGetString( Tcl_ExternalToUtfDString(TkWinGetKeyInputEncoding(), keyEv->trans_chars, keyEv->nbytes, dsPtr); } - } else if (keyEv->send_event == -2) { - /* - * Special case for win2000 multi-lingal IME input. xkey.trans_chars[] - * already contains a UNICODE char. - */ - - int unichar; - char buf[XMaxTransChars]; - int len; - - unichar = keyEv->trans_chars[1] & 0xff; - unichar <<= 8; - unichar |= keyEv->trans_chars[0] & 0xff; - - len = Tcl_UniCharToUtf((Tcl_UniChar) unichar, buf); - - Tcl_DStringAppend(dsPtr, buf, len); } else if (keyEv->send_event == -3) { - char buf[XMaxTransChars]; - int len; - /* - * Special case for WM_UNICHAR. + * Special case for WM_UNICHAR and win2000 multi-lingal IME input */ - len = Tcl_UniCharToUtf(keyEv->keycode, buf); - if ((keyEv->keycode <= 0xffff) || (len == XMaxTransChars)) { - Tcl_DStringAppend(dsPtr, buf, len); - } else { - Tcl_UniCharToUtf(((keyEv->keycode - 0x10000) >> 10) | 0xd800, buf); - Tcl_DStringAppend(dsPtr, buf, 3); - Tcl_UniCharToUtf(((keyEv->keycode - 0x10000) & 0x3ff) | 0xdc00, buf); - Tcl_DStringAppend(dsPtr, buf, 3); - } + len = TkUniCharToUtf(keyEv->keycode, buf); + Tcl_DStringAppend(dsPtr, buf, len); } else { /* * This is an event generated from generic code. It has no nchars or @@ -140,9 +115,6 @@ TkpGetString( if (((keysym != NoSymbol) && (keysym > 0) && (keysym < 256)) || (keysym == XK_Return) || (keysym == XK_Tab)) { - char buf[XMaxTransChars]; - int len; - len = Tcl_UniCharToUtf((Tcl_UniChar) (keysym & 255), buf); Tcl_DStringAppend(dsPtr, buf, len); } diff --git a/win/tkWinX.c b/win/tkWinX.c index 6be54e2..ce73aac 100644 --- a/win/tkWinX.c +++ b/win/tkWinX.c @@ -1580,8 +1580,8 @@ HandleIMEComposition( /* * Set up the fields pertinent to key event. * - * We set send_event to the special value of -2, so that TkpGetString - * in tkWinKey.c knows that trans_chars[] already contains a UNICODE + * We set send_event to the special value of -3, so that TkpGetString + * in tkWinKey.c knows that keycode already contains a UNICODE * char and there's no need to do encoding conversion. * * Note that the event *must* be zeroed out first; Tk plays cunning @@ -1592,7 +1592,7 @@ HandleIMEComposition( memset(&event, 0, sizeof(XEvent)); event.xkey.serial = winPtr->display->request++; - event.xkey.send_event = -2; + event.xkey.send_event = -3; event.xkey.display = winPtr->display; event.xkey.window = winPtr->window; event.xkey.root = RootWindow(winPtr->display, winPtr->screenNum); @@ -1600,8 +1600,7 @@ HandleIMEComposition( event.xkey.state = TkWinGetModifierState(); event.xkey.time = TkpGetMS(); event.xkey.same_screen = True; - event.xkey.keycode = 0; - event.xkey.nbytes = 2; + event.xkey.nbytes = 0; for (i=0; i