From d71a648c788bdbe6f17db19dcb90cafb174abc83 Mon Sep 17 00:00:00 2001 From: culler Date: Tue, 19 May 2020 02:31:05 +0000 Subject: Add surrogate-aware TkUtfAtIndex and TkNumUtfChars. This is enough to allow editing emoji text (without variant selectors) in Entry widgets on macOS. --- generic/tkEntry.c | 18 ++++++------ generic/tkFont.c | 20 ++++++------- generic/tkInt.h | 4 +++ generic/tkUtil.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ generic/ttk/ttkEntry.c | 22 +++++++------- macosx/tkMacOSXFont.c | 4 +-- 6 files changed, 115 insertions(+), 31 deletions(-) diff --git a/generic/tkEntry.c b/generic/tkEntry.c index 9c53769..af796eb 100644 --- a/generic/tkEntry.c +++ b/generic/tkEntry.c @@ -2049,7 +2049,7 @@ InsertChars( char *newStr; string = entryPtr->string; - byteIndex = Tcl_UtfAtIndex(string, index) - string; + byteIndex = TkUtfAtIndex(string, index) - string; byteCount = strlen(value); if (byteCount == 0) { return TCL_OK; @@ -2073,16 +2073,18 @@ InsertChars( entryPtr->string = newStr; /* + * ??? Is this construction still needed with TkNumUtfChars ??? + * * The following construction is used because inserting improperly formed * UTF-8 sequences between other improperly formed UTF-8 sequences could * result in actually forming valid UTF-8 sequences; the number of - * characters added may not be Tcl_NumUtfChars(string, -1), because of + * characters added may not be TkNumUtfChars(string, -1), because of * context. The actual number of characters added is how many characters * are in the string now minus the number that used to be there. */ oldChars = entryPtr->numChars; - entryPtr->numChars = Tcl_NumUtfChars(newStr, -1); + entryPtr->numChars = TkNumUtfChars(newStr, -1); charsAdded = entryPtr->numChars - oldChars; entryPtr->numBytes += byteCount; @@ -2153,8 +2155,8 @@ DeleteChars( } string = entryPtr->string; - byteIndex = Tcl_UtfAtIndex(string, index) - string; - byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string+byteIndex); + byteIndex = TkUtfAtIndex(string, index) - string; + byteCount = TkUtfAtIndex(string + byteIndex, count) - (string+byteIndex); newByteCount = entryPtr->numBytes + 1 - byteCount; newStr = ckalloc(newByteCount); @@ -2382,7 +2384,7 @@ EntrySetValue( entryPtr->string = tmp; } entryPtr->numBytes = valueLen; - entryPtr->numChars = Tcl_NumUtfChars(value, valueLen); + entryPtr->numChars = TkNumUtfChars(value, valueLen); if (entryPtr->displayString == oldSource) { entryPtr->displayString = entryPtr->string; @@ -2815,8 +2817,8 @@ EntryFetchSelection( return -1; } string = entryPtr->displayString; - selStart = Tcl_UtfAtIndex(string, entryPtr->selectFirst); - selEnd = Tcl_UtfAtIndex(selStart, + selStart = TkUtfAtIndex(string, entryPtr->selectFirst); + selEnd = TkUtfAtIndex(selStart, entryPtr->selectLast - entryPtr->selectFirst); byteCount = selEnd - selStart - offset; if (byteCount > maxBytes) { diff --git a/generic/tkFont.c b/generic/tkFont.c index bf35626..5a8e87f 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -1998,7 +1998,7 @@ Tk_ComputeTextLayout( height = fmPtr->ascent + fmPtr->descent; if (numChars < 0) { - numChars = Tcl_NumUtfChars(string, -1); + numChars = TkNumUtfChars(string, -1); } if (wrapLength == 0) { wrapLength = -1; @@ -2021,7 +2021,7 @@ Tk_ComputeTextLayout( curX = 0; - end = Tcl_UtfAtIndex(string, numChars); + end = TkUtfAtIndex(string, numChars); special = string; flags &= TK_IGNORE_TABS | TK_IGNORE_NEWLINES; @@ -2138,7 +2138,7 @@ Tk_ComputeTextLayout( bytesThisChunk = Tk_MeasureChars(tkfont, end, bytesThisChunk, -1, 0, &chunkPtr->totalWidth); chunkPtr->numBytes += bytesThisChunk; - chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk); + chunkPtr->numChars += TkNumUtfChars(end, bytesThisChunk); chunkPtr->totalWidth += curX; } } @@ -2325,14 +2325,14 @@ Tk_DrawTextLayout( firstChar = 0; firstByte = chunkPtr->start; } else { - firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar); + firstByte = TkUtfAtIndex(chunkPtr->start, firstChar); Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstByte - chunkPtr->start, -1, 0, &drawX); } if (lastChar < numDisplayChars) { numDisplayChars = lastChar; } - lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars); + lastByte = TkUtfAtIndex(chunkPtr->start, numDisplayChars); Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont, firstByte, lastByte - firstByte, x+chunkPtr->x+drawX, y+chunkPtr->y); } @@ -2387,14 +2387,14 @@ TkDrawAngledTextLayout( firstChar = 0; firstByte = chunkPtr->start; } else { - firstByte = Tcl_UtfAtIndex(chunkPtr->start, firstChar); + firstByte = TkUtfAtIndex(chunkPtr->start, firstChar); Tk_MeasureChars(layoutPtr->tkfont, chunkPtr->start, firstByte - chunkPtr->start, -1, 0, &drawX); } if (lastChar < numDisplayChars) { numDisplayChars = lastChar; } - lastByte = Tcl_UtfAtIndex(chunkPtr->start, numDisplayChars); + lastByte = TkUtfAtIndex(chunkPtr->start, numDisplayChars); dx = cosA * (chunkPtr->x + drawX) + sinA * (chunkPtr->y); dy = -sinA * (chunkPtr->x + drawX) + cosA * (chunkPtr->y); if (angle == 0.0) { @@ -2631,7 +2631,7 @@ Tk_PointToChar( } n = Tk_MeasureChars((Tk_Font) fontPtr, chunkPtr->start, chunkPtr->numBytes, x - chunkPtr->x, 0, &dummy); - return numChars + Tcl_NumUtfChars(chunkPtr->start, n); + return numChars + TkNumUtfChars(chunkPtr->start, n); } numChars += chunkPtr->numChars; lastPtr = chunkPtr; @@ -2736,7 +2736,7 @@ Tk_CharBbox( goto check; } } else if (index < chunkPtr->numChars) { - end = Tcl_UtfAtIndex(chunkPtr->start, index); + end = TkUtfAtIndex(chunkPtr->start, index); if (xPtr != NULL) { Tk_MeasureChars(tkfont, chunkPtr->start, end - chunkPtr->start, -1, 0, &x); @@ -3757,7 +3757,7 @@ NewChunk( *layoutPtrPtr = layoutPtr; *maxPtr = maxChunks; } - numChars = Tcl_NumUtfChars(start, numBytes); + numChars = TkNumUtfChars(start, numBytes); chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks]; chunkPtr->start = start; chunkPtr->numBytes = numBytes; diff --git a/generic/tkInt.h b/generic/tkInt.h index c63748a..d426d55 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1291,10 +1291,14 @@ MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); # define TkUtfToUniChar Tcl_UtfToUniChar # define TkUniCharToUtf Tcl_UniCharToUtf # define TkUtfPrev Tcl_UtfPrev +# define TkUtfAtIndex Tcl_UtfAtIndex +# define TkNumUtfChars Tcl_NumUtfChars #else MODULE_SCOPE int TkUtfToUniChar(const char *, int *); MODULE_SCOPE int TkUniCharToUtf(int, char *); MODULE_SCOPE const char *TkUtfPrev(const char *, const char *); + MODULE_SCOPE const char *TkUtfAtIndex(const char *src, int index); + MODULE_SCOPE int TkNumUtfChars(const char *src, int length); #endif /* diff --git a/generic/tkUtil.c b/generic/tkUtil.c index 49fd118..5d9d966 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1295,7 +1295,85 @@ TkUtfPrev( return (first + TkUtfToUniChar(first, &ch) >= src) ? first : p ; } +/* + *--------------------------------------------------------------------------- + * + * TkUtfAtIndex -- + * + * Returns a pointer to the specified character (not byte) position in + * a CESU-8 string. That is, a pair of CESU-8 encoded surrogates counts + * as a single character. + * + * Results: + * As above. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +const char * +TkUtfAtIndex( + const char *src, /* The UTF-8 string. */ + int index) /* The position of the desired character. */ +{ + int len = 0; + int ch; + + while (index-- > 0) { + len = TkUtfToUniChar(src, &ch); + src += len; + } + return src; +} + +/* + *--------------------------------------------------------------------------- + * + * TkNumUtfChars -- + * + * Returns the number of characters (not bytes) in the UTF-8 string, not + * including the terminating NULL byte. This differs from Tcl_NumUtfChars + * in that a pair of CESU-8 encoded surrogates counts as one unicode + * character. + * + * Results: + * As above. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkNumUtfChars( + const char *src, /* The UTF-8 string to measure. */ + int length) /* The length of the string in bytes, or -1 + * for strlen(string). */ +{ + int ch = 0; + int i = 0; + + if (length < 0) { + /* string is NUL-terminated, so TclUtfToUniChar calls are safe. */ + while ((*src != '\0') && (i < INT_MAX)) { + src += TkUtfToUniChar(src, &ch); + i++; + } + } else { + /* Pointer to the end of string. Never read endPtr[0] */ + const char *endPtr = src + length; + while (src < endPtr) { + src += TkUtfToUniChar(src, &ch); + i++; + } + } + return i; +} #endif + /* * Local Variables: * mode: c diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c index 96f3cf2..9a91ecc 100644 --- a/generic/ttk/ttkEntry.c +++ b/generic/ttk/ttkEntry.c @@ -339,8 +339,8 @@ EntryFetchSelection( } string = entryPtr->entry.displayString; - selStart = Tcl_UtfAtIndex(string, entryPtr->entry.selectFirst); - selEnd = Tcl_UtfAtIndex(selStart, + selStart = TkUtfAtIndex(string, entryPtr->entry.selectFirst); + selEnd = TkUtfAtIndex(selStart, entryPtr->entry.selectLast - entryPtr->entry.selectFirst); byteCount = selEnd - selStart - offset; if (byteCount > maxBytes) { @@ -458,11 +458,11 @@ ExpandPercents( break; case 'S': /* string to be inserted/deleted, if any */ if (reason == VALIDATE_INSERT) { - string = Tcl_UtfAtIndex(new, index); - stringLength = Tcl_UtfAtIndex(string, count) - string; + string = TkUtfAtIndex(new, index); + stringLength = TkUtfAtIndex(string, count) - string; } else if (reason == VALIDATE_DELETE) { - string = Tcl_UtfAtIndex(entryPtr->entry.string, index); - stringLength = Tcl_UtfAtIndex(string, count) - string; + string = TkUtfAtIndex(entryPtr->entry.string, index); + stringLength = TkUtfAtIndex(string, count) - string; } else { string = ""; stringLength = 0; @@ -707,7 +707,7 @@ static void EntryStoreValue(Entry *entryPtr, const char *value) { size_t numBytes = strlen(value); - int numChars = Tcl_NumUtfChars(value, numBytes); + int numChars = TkNumUtfChars(value, numBytes); if (entryPtr->core.flags & VALIDATING) entryPtr->core.flags |= VALIDATION_SET_VALUE; @@ -812,9 +812,9 @@ InsertChars( const char *value) /* New characters to add */ { char *string = entryPtr->entry.string; - size_t byteIndex = Tcl_UtfAtIndex(string, index) - string; + size_t byteIndex = TkUtfAtIndex(string, index) - string; size_t byteCount = strlen(value); - int charsAdded = Tcl_NumUtfChars(value, byteCount); + int charsAdded = TkNumUtfChars(value, byteCount); size_t newByteCount = entryPtr->entry.numBytes + byteCount + 1; char *new; int code; @@ -866,8 +866,8 @@ DeleteChars( return TCL_OK; } - byteIndex = Tcl_UtfAtIndex(string, index) - string; - byteCount = Tcl_UtfAtIndex(string+byteIndex, count) - (string+byteIndex); + byteIndex = TkUtfAtIndex(string, index) - string; + byteCount = TkUtfAtIndex(string+byteIndex, count) - (string+byteIndex); newByteCount = entryPtr->entry.numBytes + 1 - byteCount; new = ckalloc(newByteCount); diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index 7fc0113..70bb9e8 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -105,7 +105,7 @@ static void DrawCharsInContext(Display *display, Drawable drawable, * To avoid an extra copy, a TKNSString object wraps a Tcl_DString with an * NSString that uses the DString's buffer as its character buffer. It can be * constructed from a Tcl_DString and it has a DString property that handles - * converting from an NSString to a Tcl_DString + * converting from an NSString to a Tcl_DString. */ @implementation TKNSString @@ -1056,7 +1056,7 @@ TkpMeasureCharsInContext( [attributedString release]; [string release]; length = ceil(width - offset); - fit = (Tcl_UtfAtIndex(source, index) - source) - rangeStart; + fit = (TkUtfAtIndex(source, index) - source) - rangeStart; done: #ifdef TK_MAC_DEBUG_FONTS TkMacOSXDbgMsg("measure: source=\"%s\" range=\"%.*s\" maxLength=%d " -- cgit v0.12 From 51d384bfda7a3eb5a915b19d343c8bb657268d75 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 19 May 2020 14:02:02 +0000 Subject: New (internal) function TkUtfPrev(). This function can handle 4-byte (illegal) byte-sequences, arising when pasting Emoji on a Text widget on MacOS (for example) --- generic/tkInt.h | 2 ++ generic/tkTextIndex.c | 6 +++--- generic/tkUtil.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/generic/tkInt.h b/generic/tkInt.h index d87cd03..c63748a 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1290,9 +1290,11 @@ MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); #if TCL_UTF_MAX > 4 # define TkUtfToUniChar Tcl_UtfToUniChar # define TkUniCharToUtf Tcl_UniCharToUtf +# define TkUtfPrev Tcl_UtfPrev #else MODULE_SCOPE int TkUtfToUniChar(const char *, int *); MODULE_SCOPE int TkUniCharToUtf(int, char *); + MODULE_SCOPE const char *TkUtfPrev(const char *, const char *); #endif /* diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index 056902a..8f30b7d 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -436,7 +436,7 @@ TkTextMakeByteIndex( */ start = segPtr->body.chars + (byteIndex - index); - p = Tcl_UtfPrev(start, segPtr->body.chars); + p = TkUtfPrev(start, segPtr->body.chars); p += TkUtfToUniChar(p, &ch); indexPtr->byteIndex += p - start; } @@ -2125,7 +2125,7 @@ TkTextIndexBackChars( if (segPtr->typePtr == &tkTextCharType) { start = segPtr->body.chars; end = segPtr->body.chars + segSize; - for (p = end; ; p = Tcl_UtfPrev(p, start)) { + for (p = end; ; p = TkUtfPrev(p, start)) { if (charCount == 0) { dstPtr->byteIndex -= (end - p); goto backwardCharDone; @@ -2366,7 +2366,7 @@ StartEnd( } if (offset > 0) { chSize = (segPtr->body.chars + offset - - Tcl_UtfPrev(segPtr->body.chars + offset, + - TkUtfPrev(segPtr->body.chars + offset, segPtr->body.chars)); } firstChar = 0; diff --git a/generic/tkUtil.c b/generic/tkUtil.c index 7a15410..805a47c 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1264,9 +1264,46 @@ int TkUniCharToUtf(int ch, char *buf) } return Tcl_UniCharToUtf(ch, buf); } +/* + *--------------------------------------------------------------------------- + * + * TkUtfPrev -- + * + * Almost the same as Tcl_UtfPrev. + * This function is capable of jumping over a upper/lower surrogate pair. + * So, might jump back up to 6 bytes. + * + * Results: + * pointer to the first byte of the current UTF-8 character. A surrogate + * pair is also handled as being a single entity. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +const char * +TkUtfPrev( + const char *src, /* The UTF-8 string. */ + const char *start) /* Start position of string */ +{ + const char *p = Tcl_UtfPrev(src, start); + const char *first = Tcl_UtfPrev(p, start); + int ch; + +#if TCL_UTF_MAX == 3 + if ((src - start > 3) && ((src[-1] & 0xC0) == 0x80) && ((src[-2] & 0xC0) == 0x80) + && ((src[-3] & 0xC0) == 0x80) && (UCHAR(src[-4]) >= 0xF0)) { + return src - 4; + } +#endif + return (first + TkUtfToUniChar(first, &ch) >= src) ? first : p ; +} #endif + /* * Local Variables: * mode: c -- cgit v0.12 From e2d53f617b2bc55da830e4b7ba566d920873e83e Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 19 May 2020 19:44:51 +0000 Subject: Use Glyph indexing in more places (underscoring and canvas text and some more) --- generic/tkCanvText.c | 14 +++++++------- generic/tkMessage.c | 4 ++-- generic/tkSelect.c | 6 +++--- generic/tkText.c | 14 +++++++------- generic/tkTextIndex.c | 10 +++++----- macosx/tkMacOSXFont.c | 8 ++++---- unix/tkUnixMenu.c | 2 +- win/tkWinMenu.c | 2 +- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/generic/tkCanvText.c b/generic/tkCanvText.c index 1e58ce9..6a6de21 100644 --- a/generic/tkCanvText.c +++ b/generic/tkCanvText.c @@ -504,7 +504,7 @@ ConfigureText( */ textPtr->numBytes = strlen(textPtr->text); - textPtr->numChars = Tcl_NumUtfChars(textPtr->text, textPtr->numBytes); + textPtr->numChars = TkNumUtfChars(textPtr->text, textPtr->numBytes); if (textInfoPtr->selItemPtr == itemPtr) { if (textInfoPtr->selectFirst >= textPtr->numChars) { @@ -1025,7 +1025,7 @@ TextInsert( if (index > textPtr->numChars) { index = textPtr->numChars; } - byteIndex = Tcl_UtfAtIndex(text, index) - text; + byteIndex = TkUtfAtIndex(text, index) - text; byteCount = strlen(string); if (byteCount == 0) { return; @@ -1038,7 +1038,7 @@ TextInsert( ckfree(text); textPtr->text = newStr; - charsAdded = Tcl_NumUtfChars(string, byteCount); + charsAdded = TkNumUtfChars(string, byteCount); textPtr->numChars += charsAdded; textPtr->numBytes += byteCount; @@ -1108,8 +1108,8 @@ TextDeleteChars( } charsRemoved = last + 1 - first; - byteIndex = Tcl_UtfAtIndex(text, first) - text; - byteCount = Tcl_UtfAtIndex(text + byteIndex, charsRemoved) + byteIndex = TkUtfAtIndex(text, first) - text; + byteCount = TkUtfAtIndex(text + byteIndex, charsRemoved) - (text + byteIndex); newStr = ckalloc(textPtr->numBytes + 1 - byteCount); @@ -1497,8 +1497,8 @@ GetSelText( return 0; } text = textPtr->text; - selStart = Tcl_UtfAtIndex(text, textInfoPtr->selectFirst); - selEnd = Tcl_UtfAtIndex(selStart, + selStart = TkUtfAtIndex(text, textInfoPtr->selectFirst); + selEnd = TkUtfAtIndex(selStart, textInfoPtr->selectLast + 1 - textInfoPtr->selectFirst); byteCount = selEnd - selStart - offset; if (byteCount > maxBytes) { diff --git a/generic/tkMessage.c b/generic/tkMessage.c index 1a3c6de..f631ca5 100644 --- a/generic/tkMessage.c +++ b/generic/tkMessage.c @@ -491,7 +491,7 @@ ConfigureMessage( * be specified to Tk_ConfigureWidget. */ - msgPtr->numChars = Tcl_NumUtfChars(msgPtr->string, -1); + msgPtr->numChars = TkNumUtfChars(msgPtr->string, -1); if (msgPtr->highlightWidth < 0) { msgPtr->highlightWidth = 0; @@ -881,7 +881,7 @@ MessageTextVarProc( if (msgPtr->string != NULL) { ckfree(msgPtr->string); } - msgPtr->numChars = Tcl_NumUtfChars(value, -1); + msgPtr->numChars = TkNumUtfChars(value, -1); msgPtr->string = ckalloc(strlen(value) + 1); strcpy(msgPtr->string, value); ComputeMessageGeometry(msgPtr); diff --git a/generic/tkSelect.c b/generic/tkSelect.c index 5c71465..396ffe3 100644 --- a/generic/tkSelect.c +++ b/generic/tkSelect.c @@ -1397,15 +1397,15 @@ HandleTclCommand( if (cmdInfoPtr->interp != NULL) { if (length <= maxBytes) { - cmdInfoPtr->charOffset += Tcl_NumUtfChars(string, -1); + cmdInfoPtr->charOffset += TkNumUtfChars(string, -1); cmdInfoPtr->buffer[0] = '\0'; } else { - Tcl_UniChar ch = 0; + int ch; p = string; string += count; numChars = 0; while (p < string) { - p += Tcl_UtfToUniChar(p, &ch); + p += TkUtfToUniChar(p, &ch); numChars++; } cmdInfoPtr->charOffset += numChars; diff --git a/generic/tkText.c b/generic/tkText.c index b696647..a97f8cd 100644 --- a/generic/tkText.c +++ b/generic/tkText.c @@ -4056,12 +4056,12 @@ TextSearchIndexInLine( if (searchSpecPtr->exact) { index += leftToScan; } else { - index += Tcl_NumUtfChars(segPtr->body.chars, leftToScan); + index += TkNumUtfChars(segPtr->body.chars, leftToScan); } } else if (searchSpecPtr->exact) { index += segPtr->size; } else { - index += Tcl_NumUtfChars(segPtr->body.chars, -1); + index += TkNumUtfChars(segPtr->body.chars, -1); } } leftToScan -= segPtr->size; @@ -4256,7 +4256,7 @@ TextSearchFoundMatch( if (searchSpecPtr->exact) { const char *startOfLine = Tcl_GetString(theLine); - numChars = Tcl_NumUtfChars(startOfLine + matchOffset, matchLength); + numChars = TkNumUtfChars(startOfLine + matchOffset, matchLength); } else { numChars = matchLength; } @@ -4315,13 +4315,13 @@ TextSearchFoundMatch( if (searchSpecPtr->exact) { matchOffset += segPtr->size; } else { - matchOffset += Tcl_NumUtfChars(segPtr->body.chars, -1); + matchOffset += TkNumUtfChars(segPtr->body.chars, -1); } } else { if (searchSpecPtr->exact) { leftToScan -= segPtr->size; } else { - leftToScan -= Tcl_NumUtfChars(segPtr->body.chars, -1); + leftToScan -= TkNumUtfChars(segPtr->body.chars, -1); } } curIndex.byteIndex += segPtr->size; @@ -4406,13 +4406,13 @@ TextSearchFoundMatch( continue; } else if (!searchSpecPtr->searchElide && TkTextIsElided(textPtr, &curIndex, NULL)) { - numChars += Tcl_NumUtfChars(segPtr->body.chars, -1); + numChars += TkNumUtfChars(segPtr->body.chars, -1); continue; } if (searchSpecPtr->exact) { leftToScan -= segPtr->size; } else { - leftToScan -= Tcl_NumUtfChars(segPtr->body.chars, -1); + leftToScan -= TkNumUtfChars(segPtr->body.chars, -1); } } diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index 8f30b7d..ef6df0d 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -1053,7 +1053,7 @@ TkTextPrintIndex( break; } if (segPtr->typePtr == &tkTextCharType) { - charIndex += Tcl_NumUtfChars(segPtr->body.chars, segPtr->size); + charIndex += TkNumUtfChars(segPtr->body.chars, segPtr->size); } else { charIndex += segPtr->size; } @@ -1061,7 +1061,7 @@ TkTextPrintIndex( } if (segPtr->typePtr == &tkTextCharType) { - charIndex += Tcl_NumUtfChars(segPtr->body.chars, numBytes); + charIndex += TkNumUtfChars(segPtr->body.chars, numBytes); } else { charIndex += numBytes; } @@ -1858,11 +1858,11 @@ TkTextIndexCount( /* * This is a speed sensitive function, so run specially over * the string to count continuous ascii characters before - * resorting to the Tcl_NumUtfChars call. This is a long form + * resorting to the TkNumUtfChars call. This is a long form * of: * * stringPtr->numChars = - * Tcl_NumUtfChars(objPtr->bytes, objPtr->length); + * TkNumUtfChars(objPtr->bytes, objPtr->length); */ while (i && (*str < 0xC0)) { @@ -1871,7 +1871,7 @@ TkTextIndexCount( } count += byteLen - i; if (i) { - count += Tcl_NumUtfChars(segPtr->body.chars + byteOffset + count += TkNumUtfChars(segPtr->body.chars + byteOffset + (byteLen - i), i); } } else { diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index 70bb9e8..6f1ae77 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -954,8 +954,8 @@ TkpMeasureCharsInContext( attributes:fontPtr->nsAttributes]; typesetter = CTTypesetterCreateWithAttributedString( (CFAttributedStringRef)attributedString); - start = Tcl_NumUtfChars(source, rangeStart); - len = Tcl_NumUtfChars(source + rangeStart, rangeLength); + start = TkNumUtfChars(source, rangeStart); + len = TkNumUtfChars(source + rangeStart, rangeLength); if (start > 0) { range.length = start; line = CTTypesetterCreateLine(typesetter, range); @@ -1252,8 +1252,8 @@ DrawCharsInContext( -textX, -textY); } CGContextConcatCTM(context, t); - start = Tcl_NumUtfChars(source, rangeStart); - length = Tcl_NumUtfChars(source, rangeStart + rangeLength) - start; + start = TkNumUtfChars(source, rangeStart); + length = TkNumUtfChars(source, rangeStart + rangeLength) - start; line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, length)); if (start > 0) { diff --git a/unix/tkUnixMenu.c b/unix/tkUnixMenu.c index 40e3f94..aa54897 100644 --- a/unix/tkUnixMenu.c +++ b/unix/tkUnixMenu.c @@ -863,7 +863,7 @@ DrawMenuUnderline( const char *label, *start, *end; label = Tcl_GetString(mePtr->labelPtr); - start = Tcl_UtfAtIndex(label, mePtr->underline); + start = TkUtfAtIndex(label, mePtr->underline); end = start + TkUtfToUniChar(start, &ch); Tk_GetPixelsFromObj(NULL, menuPtr->tkwin, diff --git a/win/tkWinMenu.c b/win/tkWinMenu.c index 1f345ff..6c55164 100644 --- a/win/tkWinMenu.c +++ b/win/tkWinMenu.c @@ -2138,7 +2138,7 @@ DrawMenuUnderline( int ch; label = Tcl_GetString(mePtr->labelPtr); - start = Tcl_UtfAtIndex(label, mePtr->underline); + start = TkUtfAtIndex(label, mePtr->underline); end = start + TkUtfToUniChar(start, &ch); Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, label, x + mePtr->indicatorSpace, -- cgit v0.12 From 5f29a0196dbc94ae23df0ff6d5d9b5d1ffbd7d7f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 19 May 2020 20:10:17 +0000 Subject: Little variation on bug-a179564826, in which Character indexing is kept, but with surrogate protection --- generic/tkInt.h | 3 +-- generic/tkUtil.c | 71 ++++---------------------------------------------------- 2 files changed, 6 insertions(+), 68 deletions(-) diff --git a/generic/tkInt.h b/generic/tkInt.h index a6304f8..c27bede 100644 --- a/generic/tkInt.h +++ b/generic/tkInt.h @@ -1287,19 +1287,18 @@ MODULE_SCOPE void TkUnixSetXftClipRegion(TkRegion clipRegion); # define c_class class #endif +#define TkNumUtfChars Tcl_NumUtfChars #if TCL_UTF_MAX > 4 # define TkUtfToUniChar Tcl_UtfToUniChar # define TkUniCharToUtf Tcl_UniCharToUtf # define TkUtfPrev Tcl_UtfPrev # define TkUtfAtIndex Tcl_UtfAtIndex -# define TkNumUtfChars Tcl_NumUtfChars # define TkUtfCharComplete Tcl_UtfCharComplete #else MODULE_SCOPE int TkUtfToUniChar(const char *, int *); MODULE_SCOPE int TkUniCharToUtf(int, char *); MODULE_SCOPE const char *TkUtfPrev(const char *, const char *); MODULE_SCOPE const char *TkUtfAtIndex(const char *src, int index); - MODULE_SCOPE int TkNumUtfChars(const char *src, int length); # define TkUtfCharComplete(src, length) (((unsigned)(UCHAR(*(src)) - 0xF0) < 5) \ ? ((length) >= 4) : (UCHAR(*(src)) == 0xED) ? ((length) >= 6) : Tcl_UtfCharComplete((src), (length))) #endif diff --git a/generic/tkUtil.c b/generic/tkUtil.c index e055b0d..172bf23 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1308,8 +1308,7 @@ TkUtfPrev( * TkUtfAtIndex -- * * Returns a pointer to the specified character (not byte) position in - * a CESU-8 string. That is, a pair of CESU-8 encoded surrogates counts - * as a single character. + * a CESU-8 string. This will never point at a low surrogate. * * Results: * As above. @@ -1325,72 +1324,12 @@ TkUtfAtIndex( const char *src, /* The UTF-8 string. */ int index) /* The position of the desired character. */ { - int len = 0; int ch; - - while (index-- > 0) { - len = TkUtfToUniChar(src, &ch); - src += len; + const char *p = Tcl_UtfAtIndex(src, index); + if ((p > src) && (UCHAR(p[-1]) > 0xF0)) { + return p + TkUtfToUniChar(p - 1, &ch); } - return src; -} - -/* - *--------------------------------------------------------------------------- - * - * TkNumUtfChars -- - * - * Returns the number of characters (not bytes) in the UTF-8 string, not - * including the terminating NULL byte. This differs from Tcl_NumUtfChars - * in that a pair of CESU-8 encoded surrogates counts as one unicode - * character. - * - * Results: - * As above. - * - * Side effects: - * None. - * - *--------------------------------------------------------------------------- - */ - -int -TkNumUtfChars( - const char *src, /* The UTF-8 string to measure. */ - int length) /* The length of the string in bytes, or -1 - * for strlen(string). */ -{ - int ch; - int i = 0; - Tcl_UniChar ch2 = 0; - - if (length < 0) { - /* string is NUL-terminated, so TclUtfToUniChar calls are safe. */ - while ((*src != '\0') && (i < INT_MAX)) { - src += TkUtfToUniChar(src, &ch); - i++; - } - } else { - /* No need to call TkUtfCharComplete() up to endPtr */ - const char *endPtr = src + length - 6; - while (src < endPtr) { - src += TkUtfToUniChar(src, &ch); - i++; - } - /* Pointer to the end of string. Never read endPtr[0] */ - endPtr += 6; - while (src < endPtr) { - if (TkUtfCharComplete(src, endPtr - src)) { - src += TkUtfToUniChar(src, &ch); - } else if (Tcl_UtfCharComplete(src, endPtr - src)) { - src += Tcl_UtfToUniChar(src, &ch2); - } else { - src++; - } - i++; - } - } - return i; + return p; } #endif -- cgit v0.12 From 5e2cf435c8008214b25114de4509a7facd345dbd Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 19 May 2020 20:13:03 +0000 Subject: > ... >= --- generic/tkUtil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tkUtil.c b/generic/tkUtil.c index 172bf23..8d2f42e 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1326,7 +1326,7 @@ TkUtfAtIndex( { int ch; const char *p = Tcl_UtfAtIndex(src, index); - if ((p > src) && (UCHAR(p[-1]) > 0xF0)) { + if ((p > src) && (UCHAR(p[-1]) >= 0xF0)) { return p + TkUtfToUniChar(p - 1, &ch); } return p; -- cgit v0.12 From 75d83ece93c7c0f3a2a3c8e3a925b35353ccddeb Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 20 May 2020 10:10:40 +0000 Subject: Fix TkUtfAtIndex(), still was not 100% correct. Simplify TKNSString::DString, since Tcl_UniCharToUtf() is already capable of surrogate handling with a proper Tcl version. --- generic/tkUtil.c | 3 ++- macosx/tkMacOSXBitmap.c | 1 - macosx/tkMacOSXFont.c | 29 ++++++----------------------- macosx/tkMacOSXPrivate.h | 2 +- 4 files changed, 9 insertions(+), 26 deletions(-) diff --git a/generic/tkUtil.c b/generic/tkUtil.c index 8d2f42e..17ba443 100644 --- a/generic/tkUtil.c +++ b/generic/tkUtil.c @@ -1327,7 +1327,8 @@ TkUtfAtIndex( int ch; const char *p = Tcl_UtfAtIndex(src, index); if ((p > src) && (UCHAR(p[-1]) >= 0xF0)) { - return p + TkUtfToUniChar(p - 1, &ch); + --p; + return p + TkUtfToUniChar(p, &ch); } return p; } diff --git a/macosx/tkMacOSXBitmap.c b/macosx/tkMacOSXBitmap.c index 615192b..2b08235 100644 --- a/macosx/tkMacOSXBitmap.c +++ b/macosx/tkMacOSXBitmap.c @@ -317,7 +317,6 @@ TkpGetNativeAppBitmap( OSType iconType; if (OSTypeFromString(name, &iconType) == TCL_OK) { NSString *iconUTI = OSTYPE_TO_UTI(iconType); - printf("Found image for UTI %s\n", iconUTI.UTF8String); NSImage *iconImage = [[NSWorkspace sharedWorkspace] iconForFileType: iconUTI]; pixmap = PixmapFromImage(display, iconImage, NSSizeToCGSize(size)); diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index 6f1ae77..44e25d2 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -133,7 +133,6 @@ static void DrawCharsInContext(Display *display, Drawable drawable, _string = [[NSString alloc] initWithString:aString]; self.UTF8String = _string.UTF8String; } - printf("Initialized with string %s\n", self.UTF8String); return self; } @@ -166,32 +165,16 @@ static void DrawCharsInContext(Display *display, Drawable drawable, * The DString has not been initialized. Construct it from * our string's unicode characters. */ - - char buffer[2*TCL_UTF_MAX]; - unsigned int index, length, ch; + char *p; + int index; Tcl_DStringInit(&_ds); -#if TCL_UTF_MAX == 3 - for (index = 0; index < [_string length]; index++) { - UniChar uni = [_string characterAtIndex: index]; - - if (CFStringIsSurrogateHighCharacter(uni)) { - UniChar low = [_string characterAtIndex: ++index]; - ch = CFStringGetLongCharacterForSurrogatePair(uni, low); - } else { - ch = uni; - } - length = TkUniCharToUtf(ch, buffer); - Tcl_DStringAppend(&_ds, buffer, length); - } -#else + Tcl_DStringSetLength(&_ds, 3 * [_string length]); + p = Tcl_DStringValue(&_ds); for (index = 0; index < [_string length]; index++) { - ch = (int) [_string characterAtIndex: index]; - length = Tcl_UniCharToUtf(ch, buffer); - Tcl_DStringAppend(&_ds, buffer, length); + p += Tcl_UniCharToUtf([_string characterAtIndex: index], p); } - -#endif + Tcl_DStringSetLength(&_ds, p - Tcl_DStringValue(&_ds)); } return _ds; } diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h index a0645f7..be69fcd 100644 --- a/macosx/tkMacOSXPrivate.h +++ b/macosx/tkMacOSXPrivate.h @@ -527,7 +527,7 @@ VISIBILITY_HIDDEN * byte sequence as initial data. So we add a new class which does provide * such a constructor. It also has a DString property which is a DString whose * string pointer is a byte sequence encoding the NSString with the current Tk - * encoding, namely UTF-8 if TCL_MAX >= 4 or CESU-8 if TCL_MAX = 3. + * encoding, namely UTF-8 if TCL_UTF_MAX >= 4 or CESU-8 if TCL_UTF_MAX = 3. * *--------------------------------------------------------------------------- */ -- cgit v0.12 -- cgit v0.12 From d6e49238a94a313ec7fa612387ad1c72211ea479 Mon Sep 17 00:00:00 2001 From: bll Date: Wed, 20 May 2020 15:43:52 +0000 Subject: Work around a windows vsapi issue where the size returned for a vsapi element changes upon fetching the size a second time. --- win/ttkWinXPTheme.c | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/win/ttkWinXPTheme.c b/win/ttkWinXPTheme.c index 4391fdd..c32d3cd 100644 --- a/win/ttkWinXPTheme.c +++ b/win/ttkWinXPTheme.c @@ -373,6 +373,10 @@ static Ttk_StateTable tabitem_statemap[] = * <>: * This gives bogus metrics for some parts (in particular, * BP_PUSHBUTTONS). Set the IGNORE_THEMESIZE flag to skip this call. + * + * <>: + * It seems that fetching the part size more than once on checkbuttons + * and radiobuttons does not work properly. */ typedef struct /* XP element specifications */ @@ -390,6 +394,7 @@ typedef struct /* XP element specifications */ # define HEAP_ELEMENT 0x20000000U /* ElementInfo is on heap */ # define HALF_HEIGHT 0x10000000U /* Used by GenericSizedElements */ # define HALF_WIDTH 0x08000000U /* Used by GenericSizedElements */ +# define FETCH_ONCE 0x04000000U /* Used by GenericElementSize See NOTE-GetThemePartSize-2 */ } ElementInfo; typedef struct @@ -407,6 +412,12 @@ typedef struct HDC hDC; HWND hwnd; + /* + * To work around fetch of size returning wrong data + */ + SIZE origSize; + int fetched; + /* For TkWinDrawableReleaseDC: */ Drawable drawable; TkWinDCState dcState; @@ -420,6 +431,7 @@ NewElementData(XPThemeProcs *procs, ElementInfo *info) elementData->procs = procs; elementData->info = info; elementData->hTheme = elementData->hDC = 0; + elementData->fetched = 0; return elementData; } @@ -508,19 +520,28 @@ static void GenericElementSize( return; if (!(elementData->info->flags & IGNORE_THEMESIZE)) { - result = elementData->procs->GetThemePartSize( - elementData->hTheme, - elementData->hDC, - elementData->info->partId, - Ttk_StateTableLookup(elementData->info->statemap, 0), - NULL /*RECT *prc*/, - TS_TRUE, - &size); + if ((elementData->info->flags & FETCH_ONCE) && elementData->fetched) { + size = elementData->origSize; + result = 0; /* non-negative is success */ + } else { + result = elementData->procs->GetThemePartSize( + elementData->hTheme, + elementData->hDC, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, 0), + NULL /*RECT *prc*/, + TS_TRUE, + &size); + } if (SUCCEEDED(result)) { *widthPtr = size.cx; *heightPtr = size.cy; } + if ((elementData->info->flags & FETCH_ONCE) && ! elementData->fetched) { + elementData->origSize = size; + elementData->fetched = 1; + } } /* See NOTE-GetThemeMargins @@ -966,9 +987,9 @@ TTK_END_LAYOUT_TABLE static ElementInfo ElementInfoTable[] = { { "Checkbutton.indicator", &GenericElementSpec, L"BUTTON", - BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, + BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS | FETCH_ONCE }, { "Radiobutton.indicator", &GenericElementSpec, L"BUTTON", - BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, + BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS | FETCH_ONCE }, { "Button.button", &GenericElementSpec, L"BUTTON", BP_PUSHBUTTON, pushbutton_statemap, PAD(3, 3, 3, 3), IGNORE_THEMESIZE }, { "Labelframe.border", &GenericElementSpec, L"BUTTON", -- cgit v0.12 From 856873628c3fc2a1616b88ccf1163ba60477f49d Mon Sep 17 00:00:00 2001 From: culler Date: Wed, 20 May 2020 19:18:22 +0000 Subject: Fix compiler warning: macOS string length is unsigned. --- macosx/tkMacOSXFont.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macosx/tkMacOSXFont.c b/macosx/tkMacOSXFont.c index d704184..13a5de0 100644 --- a/macosx/tkMacOSXFont.c +++ b/macosx/tkMacOSXFont.c @@ -166,7 +166,7 @@ static void DrawCharsInContext(Display *display, Drawable drawable, * our string's unicode characters. */ char *p; - int index; + unsigned int index; Tcl_DStringInit(&_ds); Tcl_DStringSetLength(&_ds, 3 * [_string length]); -- cgit v0.12 From 548dc002dda31d26cee170eb6c13cc96d134e52f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 22 May 2020 13:17:19 +0000 Subject: Remove incorrect reference to tcl_wordchars, it isn't used any more. --- doc/text.n | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/text.n b/doc/text.n index 90834df..6489d1a 100644 --- a/doc/text.n +++ b/doc/text.n @@ -2025,10 +2025,7 @@ integer. This command used to be used for scrolling, but now it is obsolete. .SH BINDINGS .PP Tk automatically creates class bindings for texts that give them the following -default behavior. In the descriptions below, -.QW word -is dependent on the value of the \fBtcl_wordchars\fR variable. See -\fBtclvars\fR(n). +default behavior. .IP [1] Clicking mouse button 1 positions the insertion cursor just before the character underneath the mouse cursor, sets the input focus to this widget, -- cgit v0.12 From 94ce35fac8fa42a081fb209997608c957ba8c9d7 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 22 May 2020 13:33:09 +0000 Subject: Update MacOSX build from xcode11.4 to xcode11.5 --- .travis.yml | 27 +++++++++++++++------------ doc/text.n | 4 ---- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3c03cf4..9c8a9dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,13 +3,21 @@ language: c addons: apt: packages: + - binutils-mingw-w64-i686 + - binutils-mingw-w64-x86-64 + - gcc-mingw-w64 + - gcc-mingw-w64-base + - gcc-mingw-w64-i686 + - gcc-mingw-w64-x86-64 + - gcc-multilib - tcl-dev - libx11-dev -# Doesn't work at the moment, see: -# homebrew: -# packages: -# - tcl-tk -# - xquartz + homebrew: + packages: + - tcl-tk + casks: + - xquartz + update: true matrix: include: # Testing on Linux with various compilers @@ -106,9 +114,9 @@ matrix: - BUILD_DIR=unix - CFGOPT="--enable-symbols" # Testing on Mac, various styles - - name: "macOS/Xcode 11.4/Shared/Unix-like" + - name: "macOS/Xcode 11.5/Shared/XQuartz" os: osx - osx_image: xcode11.4 + osx_image: xcode11.5 env: - BUILD_DIR=unix - CFGOPT="--with-tcl=/usr/local/opt/tcl-tk/lib --disable-corefoundation --x-includes=/opt/X11/include --x-libraries=/opt/X11/lib CFLAGS=-I/usr/local/opt/tcl-tk/include" @@ -131,11 +139,6 @@ before_install: windows) choco install -y magicsplat-tcl-tk ;; - osx) - brew update - brew install tcl-tk - brew cask install xquartz - ;; esac - cd ${BUILD_DIR} install: diff --git a/doc/text.n b/doc/text.n index 32019ce..f7df3ad 100644 --- a/doc/text.n +++ b/doc/text.n @@ -2077,10 +2077,6 @@ This command used to be used for scrolling, but now it is obsolete. .PP Tk automatically creates class bindings for texts that give them the following default behavior. -In the descriptions below, -.QW word -is dependent on the value of -the \fBtcl_wordchars\fR variable. See \fBtclvars\fR(n). .IP [1] Clicking mouse button 1 positions the insertion cursor just before the character underneath the mouse cursor, sets the -- cgit v0.12 From 7063328b6584b3d6923d6afd07a254ca7a99746c Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Sat, 23 May 2020 22:10:21 +0000 Subject: Put back comment, which shouldn't have been removed in the previous commit. --- doc/text.n | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/text.n b/doc/text.n index f7df3ad..32019ce 100644 --- a/doc/text.n +++ b/doc/text.n @@ -2077,6 +2077,10 @@ This command used to be used for scrolling, but now it is obsolete. .PP Tk automatically creates class bindings for texts that give them the following default behavior. +In the descriptions below, +.QW word +is dependent on the value of +the \fBtcl_wordchars\fR variable. See \fBtclvars\fR(n). .IP [1] Clicking mouse button 1 positions the insertion cursor just before the character underneath the mouse cursor, sets the -- cgit v0.12 From 36975c88f715a973f160b5f6627d89965ebbb06e Mon Sep 17 00:00:00 2001 From: fvogel Date: Sun, 24 May 2020 10:51:11 +0000 Subject: Use S_OK for result instead of directly 0. --- win/ttkWinXPTheme.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/win/ttkWinXPTheme.c b/win/ttkWinXPTheme.c index c32d3cd..f91be9b 100644 --- a/win/ttkWinXPTheme.c +++ b/win/ttkWinXPTheme.c @@ -522,7 +522,7 @@ static void GenericElementSize( if (!(elementData->info->flags & IGNORE_THEMESIZE)) { if ((elementData->info->flags & FETCH_ONCE) && elementData->fetched) { size = elementData->origSize; - result = 0; /* non-negative is success */ + result = S_OK; } else { result = elementData->procs->GetThemePartSize( elementData->hTheme, @@ -538,7 +538,7 @@ static void GenericElementSize( *widthPtr = size.cx; *heightPtr = size.cy; } - if ((elementData->info->flags & FETCH_ONCE) && ! elementData->fetched) { + if ((elementData->info->flags & FETCH_ONCE) && !elementData->fetched) { elementData->origSize = size; elementData->fetched = 1; } -- cgit v0.12 From 5da5fa8645eacfb6a876212e959f93a6603e1c5b Mon Sep 17 00:00:00 2001 From: bll Date: Sun, 24 May 2020 15:00:22 +0000 Subject: Add FETCH_ONCE to the treeview indicator also. --- win/ttkWinXPTheme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win/ttkWinXPTheme.c b/win/ttkWinXPTheme.c index f91be9b..3252a18 100644 --- a/win/ttkWinXPTheme.c +++ b/win/ttkWinXPTheme.c @@ -1058,7 +1058,7 @@ static ElementInfo ElementInfoTable[] = { { "Treeview.field", &GenericElementSpec, L"TREEVIEW", TVP_TREEITEM, treeview_statemap, PAD(1, 1, 1, 1), IGNORE_THEMESIZE }, { "Treeitem.indicator", &TreeIndicatorElementSpec, L"TREEVIEW", - TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS }, + TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS | FETCH_ONCE }, { "Treeheading.border", &GenericElementSpec, L"HEADER", HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 }, { "sizegrip", &GenericElementSpec, L"STATUS", -- cgit v0.12 From 343083448bf1002eaefaedcee770d7641a653bda Mon Sep 17 00:00:00 2001 From: bll Date: Sun, 24 May 2020 20:58:18 +0000 Subject: Implement a much simpler method to get the proper sizing (nemethi). --- win/ttkWinXPTheme.c | 43 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/win/ttkWinXPTheme.c b/win/ttkWinXPTheme.c index 3252a18..436fe00 100644 --- a/win/ttkWinXPTheme.c +++ b/win/ttkWinXPTheme.c @@ -373,10 +373,6 @@ static Ttk_StateTable tabitem_statemap[] = * <>: * This gives bogus metrics for some parts (in particular, * BP_PUSHBUTTONS). Set the IGNORE_THEMESIZE flag to skip this call. - * - * <>: - * It seems that fetching the part size more than once on checkbuttons - * and radiobuttons does not work properly. */ typedef struct /* XP element specifications */ @@ -394,7 +390,6 @@ typedef struct /* XP element specifications */ # define HEAP_ELEMENT 0x20000000U /* ElementInfo is on heap */ # define HALF_HEIGHT 0x10000000U /* Used by GenericSizedElements */ # define HALF_WIDTH 0x08000000U /* Used by GenericSizedElements */ -# define FETCH_ONCE 0x04000000U /* Used by GenericElementSize See NOTE-GetThemePartSize-2 */ } ElementInfo; typedef struct @@ -412,12 +407,6 @@ typedef struct HDC hDC; HWND hwnd; - /* - * To work around fetch of size returning wrong data - */ - SIZE origSize; - int fetched; - /* For TkWinDrawableReleaseDC: */ Drawable drawable; TkWinDCState dcState; @@ -431,7 +420,6 @@ NewElementData(XPThemeProcs *procs, ElementInfo *info) elementData->procs = procs; elementData->info = info; elementData->hTheme = elementData->hDC = 0; - elementData->fetched = 0; return elementData; } @@ -520,28 +508,19 @@ static void GenericElementSize( return; if (!(elementData->info->flags & IGNORE_THEMESIZE)) { - if ((elementData->info->flags & FETCH_ONCE) && elementData->fetched) { - size = elementData->origSize; - result = S_OK; - } else { - result = elementData->procs->GetThemePartSize( - elementData->hTheme, - elementData->hDC, - elementData->info->partId, - Ttk_StateTableLookup(elementData->info->statemap, 0), - NULL /*RECT *prc*/, - TS_TRUE, - &size); - } + result = elementData->procs->GetThemePartSize( + elementData->hTheme, + NULL, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, 0), + NULL /*RECT *prc*/, + TS_TRUE, + &size); if (SUCCEEDED(result)) { *widthPtr = size.cx; *heightPtr = size.cy; } - if ((elementData->info->flags & FETCH_ONCE) && !elementData->fetched) { - elementData->origSize = size; - elementData->fetched = 1; - } } /* See NOTE-GetThemeMargins @@ -987,9 +966,9 @@ TTK_END_LAYOUT_TABLE static ElementInfo ElementInfoTable[] = { { "Checkbutton.indicator", &GenericElementSpec, L"BUTTON", - BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS | FETCH_ONCE }, + BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, { "Radiobutton.indicator", &GenericElementSpec, L"BUTTON", - BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS | FETCH_ONCE }, + BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, { "Button.button", &GenericElementSpec, L"BUTTON", BP_PUSHBUTTON, pushbutton_statemap, PAD(3, 3, 3, 3), IGNORE_THEMESIZE }, { "Labelframe.border", &GenericElementSpec, L"BUTTON", @@ -1058,7 +1037,7 @@ static ElementInfo ElementInfoTable[] = { { "Treeview.field", &GenericElementSpec, L"TREEVIEW", TVP_TREEITEM, treeview_statemap, PAD(1, 1, 1, 1), IGNORE_THEMESIZE }, { "Treeitem.indicator", &TreeIndicatorElementSpec, L"TREEVIEW", - TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS | FETCH_ONCE }, + TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS }, { "Treeheading.border", &GenericElementSpec, L"HEADER", HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 }, { "sizegrip", &GenericElementSpec, L"STATUS", -- cgit v0.12 From 9ba4c746ec344f1455e03c2f97ca0ae7c95d139f Mon Sep 17 00:00:00 2001 From: culler Date: Fri, 29 May 2020 18:21:55 +0000 Subject: Remove some unnecessary macOS conditional code by using internal stubs. --- generic/tkFont.c | 10 ++++------ generic/tkInt.decls | 9 +++++++++ generic/tkIntDecls.h | 36 ++++++++++++++++++++++++++++++++++++ generic/tkStubInit.c | 20 ++++++++++++++++++++ generic/tkTextDisp.c | 36 +++++++++++++++++++----------------- macosx/tkMacOSXInt.h | 2 -- macosx/tkMacOSXSubwindows.c | 8 +------- macosx/tkMacOSXWindowEvent.c | 35 +++++++++++++++++++++++++---------- macosx/tkMacOSXWm.c | 34 +++++++++++++++++++++++----------- unix/tkUnixPort.h | 2 ++ win/tkWinPort.h | 7 +++++++ 11 files changed, 146 insertions(+), 53 deletions(-) diff --git a/generic/tkFont.c b/generic/tkFont.c index 53855ac..448f918 100644 --- a/generic/tkFont.c +++ b/generic/tkFont.c @@ -13,9 +13,7 @@ #include "tkInt.h" #include "tkFont.h" -#if defined(MAC_OSX_TK) -#include "tkMacOSXInt.h" -#endif + /* * The following structure is used to keep track of all the fonts that exist * in the current application. It must be stored in the TkMainInfo for the @@ -874,18 +872,18 @@ TheWorldHasChanged( ClientData clientData) /* Info about application's fonts. */ { TkFontInfo *fiPtr = clientData; -#if defined(MAC_OSX_TK) /* * On macOS it is catastrophic to recompute all widgets while the * [NSView drawRect] method is drawing. The best that we can do in * that situation is to abort the recomputation and hope for the best. + * This is ignored on other platforms. */ - if (TkpAppIsDrawing()) { + if (TkpAppCanDraw(NULL)) { return; } -#endif + fiPtr->updatePending = 0; RecomputeWidgets(fiPtr->mainPtr->winPtr); } diff --git a/generic/tkInt.decls b/generic/tkInt.decls index bb2057b..da591cb 100644 --- a/generic/tkInt.decls +++ b/generic/tkInt.decls @@ -634,6 +634,15 @@ declare 184 { Tk_Font tkfont, const char *source, int numBytes, double x, double y, double angle) } + +# Support for aqua's inability to draw outside [NSView drawRect:] +declare 185 aqua { + void TkpRedrawWidget(Tk_Window tkwin) +} +declare 186 aqua { + int TkpAppCanDraw(Tk_Window tkwin) +} + ############################################################################## diff --git a/generic/tkIntDecls.h b/generic/tkIntDecls.h index fa1833e..7bf9bd7 100644 --- a/generic/tkIntDecls.h +++ b/generic/tkIntDecls.h @@ -550,6 +550,14 @@ EXTERN void TkDrawAngledChars(Display *display, Drawable drawable, GC gc, Tk_Font tkfont, const char *source, int numBytes, double x, double y, double angle); +#ifdef MAC_OSX_TK /* AQUA */ +/* 185 */ +EXTERN void TkpRedrawWidget(Tk_Window tkwin); +#endif /* AQUA */ +#ifdef MAC_OSX_TK /* AQUA */ +/* 186 */ +EXTERN int TkpAppCanDraw(Tk_Window tkwin); +#endif /* AQUA */ typedef struct TkIntStubs { int magic; @@ -767,6 +775,26 @@ typedef struct TkIntStubs { void (*tkUnderlineAngledTextLayout) (Display *display, Drawable drawable, GC gc, Tk_TextLayout layout, int x, int y, double angle, int underline); /* 182 */ int (*tkIntersectAngledTextLayout) (Tk_TextLayout layout, int x, int y, int width, int height, double angle); /* 183 */ void (*tkDrawAngledChars) (Display *display, Drawable drawable, GC gc, Tk_Font tkfont, const char *source, int numBytes, double x, double y, double angle); /* 184 */ +#if !(defined(_WIN32) || defined(MAC_OSX_TK)) /* X11 */ + void (*reserved185)(void); +#endif /* X11 */ +#if defined(_WIN32) /* WIN */ + void (*reserved185)(void); +#endif /* WIN */ +#ifdef MAC_OSX_TK /* AQUA */ + void (*reserved185)(void); /* Dummy entry for stubs table backwards compatibility */ + void (*tkpRedrawWidget) (Tk_Window tkwin); /* 185 */ +#endif /* AQUA */ +#if !(defined(_WIN32) || defined(MAC_OSX_TK)) /* X11 */ + void (*reserved186)(void); +#endif /* X11 */ +#if defined(_WIN32) /* WIN */ + void (*reserved186)(void); +#endif /* WIN */ +#ifdef MAC_OSX_TK /* AQUA */ + void (*reserved186)(void); /* Dummy entry for stubs table backwards compatibility */ + int (*tkpAppCanDraw) (Tk_Window tkwin); /* 186 */ +#endif /* AQUA */ } TkIntStubs; extern const TkIntStubs *tkIntStubsPtr; @@ -1139,6 +1167,14 @@ extern const TkIntStubs *tkIntStubsPtr; (tkIntStubsPtr->tkIntersectAngledTextLayout) /* 183 */ #define TkDrawAngledChars \ (tkIntStubsPtr->tkDrawAngledChars) /* 184 */ +#ifdef MAC_OSX_TK /* AQUA */ +#define TkpRedrawWidget \ + (tkIntStubsPtr->tkpRedrawWidget) /* 185 */ +#endif /* AQUA */ +#ifdef MAC_OSX_TK /* AQUA */ +#define TkpAppCanDraw \ + (tkIntStubsPtr->tkpAppCanDraw) /* 186 */ +#endif /* AQUA */ #endif /* defined(USE_TK_STUBS) */ diff --git a/generic/tkStubInit.c b/generic/tkStubInit.c index 61c698a..045c86e 100644 --- a/generic/tkStubInit.c +++ b/generic/tkStubInit.c @@ -485,6 +485,26 @@ static const TkIntStubs tkIntStubs = { TkUnderlineAngledTextLayout, /* 182 */ TkIntersectAngledTextLayout, /* 183 */ TkDrawAngledChars, /* 184 */ +#if !(defined(_WIN32) || defined(MAC_OSX_TK)) /* X11 */ + 0, /* 185 */ +#endif /* X11 */ +#if defined(_WIN32) /* WIN */ + 0, /* 185 */ +#endif /* WIN */ +#ifdef MAC_OSX_TK /* AQUA */ + 0, /* 185 */ /* Dummy entry for stubs table backwards compatibility */ + TkpRedrawWidget, /* 185 */ +#endif /* AQUA */ +#if !(defined(_WIN32) || defined(MAC_OSX_TK)) /* X11 */ + 0, /* 186 */ +#endif /* X11 */ +#if defined(_WIN32) /* WIN */ + 0, /* 186 */ +#endif /* WIN */ +#ifdef MAC_OSX_TK /* AQUA */ + 0, /* 186 */ /* Dummy entry for stubs table backwards compatibility */ + TkpAppCanDraw, /* 186 */ +#endif /* AQUA */ }; static const TkIntPlatStubs tkIntPlatStubs = { diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index c848fd2..084fd64 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -20,15 +20,13 @@ #include "tkWinInt.h" #elif defined(__CYGWIN__) #include "tkUnixInt.h" +#elif defined(MAC_OSX_TK) +#include "tkMacOSXInt.h" +#define OK_TO_LOG (!TkpAppCanDraw(textPtr->tkwin)) #endif -#ifdef MAC_OSX_TK -#include "tkMacOSXInt.h" -#define OK_TO_LOG (!TkpAppIsDrawing()) -#define FORCE_DISPLAY(winPtr) TkpDisplayWindow(winPtr) -#else +#if !defined(MAC_OSX_TK) #define OK_TO_LOG 1 -#define FORCE_DISPLAY(winPtr) #endif /* @@ -3157,7 +3155,7 @@ GenerateWidgetViewSyncEvent( */ if (!tkTextDebug) { - FORCE_DISPLAY(textPtr->tkwin); + TkpRedrawWidget(textPtr->tkwin); } if (NewSyncState != OldSyncState) { @@ -4176,11 +4174,23 @@ DisplayText( * warnings. */ Tcl_Interp *interp; + + if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) { + /* + * The widget has been deleted. Don't do anything. + */ + + return; + } + #ifdef MAC_OSX_TK /* - * If drawing is disabled, all we need to do is - * clear the REDRAW_PENDING flag. + * If the toplevel is being resized it would be dangerous to try redrawing + * the widget. But we can just clear the REDRAW_PENDING flag and return. + * This display proc will be called again after the widget has been + * reconfigured. */ + TkWindow *winPtr = (TkWindow *)(textPtr->tkwin); MacDrawable *macWin = winPtr->privatePtr; if (macWin && (macWin->flags & TK_DO_NOT_DRAW)){ @@ -4189,14 +4199,6 @@ DisplayText( } #endif - if ((textPtr->tkwin == NULL) || (textPtr->flags & DESTROYED)) { - /* - * The widget has been deleted. Don't do anything. - */ - - return; - } - interp = textPtr->interp; Tcl_Preserve(interp); diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h index 9cb75d2..58761d5 100644 --- a/macosx/tkMacOSXInt.h +++ b/macosx/tkMacOSXInt.h @@ -200,8 +200,6 @@ MODULE_SCOPE void TkpClipDrawableToRect(Display *display, Drawable d, int x, MODULE_SCOPE void TkpRetainRegion(TkRegion r); MODULE_SCOPE void TkpReleaseRegion(TkRegion r); MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta); -MODULE_SCOPE Bool TkpAppIsDrawing(void); -MODULE_SCOPE void TkpDisplayWindow(Tk_Window tkwin); MODULE_SCOPE Bool TkTestLogDisplay(void); MODULE_SCOPE Bool TkMacOSXInDarkMode(Tk_Window tkwin); diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c index 5063fa3..6602564 100644 --- a/macosx/tkMacOSXSubwindows.c +++ b/macosx/tkMacOSXSubwindows.c @@ -698,16 +698,10 @@ XConfigureWindow( if (value_mask & CWStackMode) { NSView *view = TkMacOSXDrawableView(macWin); - Rect bounds; - NSRect r; if (view) { TkMacOSXInvalClipRgns((Tk_Window) winPtr->parentPtr); - TkMacOSXWinBounds(winPtr, &bounds); - r = NSMakeRect(bounds.left, - [view bounds].size.height - bounds.bottom, - bounds.right - bounds.left, bounds.bottom - bounds.top); - [view setNeedsDisplayInRect:r]; + TkpRedrawWidget((Tk_Window) winPtr); } } diff --git a/macosx/tkMacOSXWindowEvent.c b/macosx/tkMacOSXWindowEvent.c index cb4ffd1..76b2b04 100644 --- a/macosx/tkMacOSXWindowEvent.c +++ b/macosx/tkMacOSXWindowEvent.c @@ -382,26 +382,41 @@ extern NSString *NSWindowDidOrderOffScreenNotification; /* *---------------------------------------------------------------------- * - * TkpAppIsDrawing -- + * TkpAppCanDraw -- * * A widget display procedure can call this to determine whether it is - * being run inside of the drawRect method. This is needed for some tests, - * especially of the Text widget, which record data in a global Tcl - * variable and assume that display procedures will be run in a - * predictable sequence as Tcl idle tasks. + * being run inside of the drawRect method. If not, it may be desirable + * for the display procedure to simply clear the REDRAW_PENDING flag + * and return. The widget can be recorded in order to schedule a + * redraw, via and Expose event, from within drawRect. + * + * This is also needed for some tests, especially of the Text widget, + * which record data in a global Tcl variable and assume that display + * procedures will be run in a predictable sequence as Tcl idle tasks. * * Results: - * True only while running the drawRect method of a TKContentView; + * True if called from the drawRect method of a TKContentView with + * tkwin NULL or pointing to a widget in the current focusView. * * Side effects: - * None + * The tkwin parameter may be recorded to handle redrawing the widget + * later. * *---------------------------------------------------------------------- */ -MODULE_SCOPE Bool -TkpAppIsDrawing(void) { - return [NSApp isDrawing]; +int +TkpAppCanDraw(Tk_Window tkwin) { + if (![NSApp isDrawing]) { + return 0; + } + if (tkwin) { + TkWindow *winPtr = (TkWindow *)tkwin; + NSView *view = TkMacOSXDrawableView(winPtr->privatePtr); + return (view == [NSView focusView]); + } else { + return 1; + } } /* diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c index 9117159..cab2b9a 100644 --- a/macosx/tkMacOSXWm.c +++ b/macosx/tkMacOSXWm.c @@ -6174,28 +6174,40 @@ TkMacOSXMakeRealWindowExist( /* *---------------------------------------------------------------------- * - * TkpDisplayWindow -- + * TkpRedrawWidget -- * - * Mark the contentView of this window as needing display so the window - * will be drawn by the window manager. If this is called within the - * drawRect method, do nothing. + * Mark the bounding rectangle of this widget as needing display so the + * widget will be drawn by [NSView drawRect:]. If this is called within + * the drawRect method, do nothing. * * Results: * None. * * Side effects: - * The window's contentView is marked as needing display. + * The widget's bounding rectangle is marked as dirty. * *---------------------------------------------------------------------- */ -MODULE_SCOPE void -TkpDisplayWindow(Tk_Window tkwin) { - if (![NSApp isDrawing]) { - TkWindow *winPtr = (TkWindow *) tkwin; - NSWindow *w = TkMacOSXDrawableWindow(winPtr->window); +void +TkpRedrawWidget(Tk_Window tkwin) { + TkWindow *winPtr = (TkWindow *) tkwin; + NSWindow *w; + Rect tkBounds; + NSRect bounds; - [[w contentView] setNeedsDisplay: YES]; + if ([NSApp isDrawing]) { + return; + } + w = TkMacOSXDrawableWindow(winPtr->window); + if (w) { + NSView *view = [w contentView]; + TkMacOSXWinBounds(winPtr, &tkBounds); + bounds = NSMakeRect(tkBounds.left, + [view bounds].size.height - tkBounds.bottom, + tkBounds.right - tkBounds.left, + tkBounds.bottom - tkBounds.top); + [view setNeedsDisplayInRect:bounds]; } } diff --git a/unix/tkUnixPort.h b/unix/tkUnixPort.h index 09ff558..8fd56fe 100644 --- a/unix/tkUnixPort.h +++ b/unix/tkUnixPort.h @@ -167,6 +167,8 @@ #define TkpButtonSetDefaults() {} #define TkpDestroyButton(butPtr) {} +#define TkpAppCanDraw(tkwin) 1 +#define TkpRedrawWidget(tkwin) #define TkSelUpdateClipboard(a,b) {} #ifndef __CYGWIN__ #define TkSetPixmapColormap(p,c) {} diff --git a/win/tkWinPort.h b/win/tkWinPort.h index 8cc5677..4ef8680 100644 --- a/win/tkWinPort.h +++ b/win/tkWinPort.h @@ -125,4 +125,11 @@ #define TkpCreateNativeBitmap(display, source) None #define TkpGetNativeAppBitmap(display, name, w, h) None +/* + * Other functions not used under Windows + */ + +#define TkpAppCanDraw(tkwin) 1 +#define TkpRedrawWidget(tkwin) + #endif /* _WINPORT */ -- cgit v0.12