summaryrefslogtreecommitdiffstats
path: root/generic/tkTreeUtils.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkTreeUtils.c')
-rw-r--r--generic/tkTreeUtils.c877
1 files changed, 877 insertions, 0 deletions
diff --git a/generic/tkTreeUtils.c b/generic/tkTreeUtils.c
new file mode 100644
index 0000000..9c8b482
--- /dev/null
+++ b/generic/tkTreeUtils.c
@@ -0,0 +1,877 @@
+#include "tkTreeCtrl.h"
+#ifdef WIN32
+#include "tkWinInt.h"
+#endif
+
+void wipefree(char *memPtr, int size)
+{
+ memset(memPtr, 0xAA, size);
+ ckfree(memPtr);
+}
+
+void FormatResult(Tcl_Interp *interp, char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+}
+
+int Ellipsis(Tk_Font tkfont, char *string, int numBytes, int *maxPixels, char *ellipsis)
+{
+ char staticStr[256], *tmpStr = staticStr;
+ int pixels, pixelsTest, bytesThatFit, bytesTest;
+ int ellipsisNumBytes = strlen(ellipsis);
+
+ bytesThatFit = Tk_MeasureChars(tkfont, string, numBytes, *maxPixels, 0,
+ &pixels);
+
+ /* The whole string fits. No ellipsis needed */
+ if (bytesThatFit == numBytes)
+ {
+ (*maxPixels) = pixels;
+ return numBytes;
+ }
+
+ if (bytesThatFit <= 1)
+ {
+ (*maxPixels) = pixels;
+ return -bytesThatFit;
+ }
+
+ /* Strip off one character at a time, adding ellipsis, until it fits */
+ bytesTest = Tcl_UtfPrev(string + bytesThatFit, string) - string;
+ if (bytesTest + ellipsisNumBytes > sizeof(staticStr))
+ tmpStr = ckalloc(bytesTest + ellipsisNumBytes);
+ memcpy(tmpStr, string, bytesTest);
+ while (bytesTest > 0)
+ {
+ memcpy(tmpStr + bytesTest, ellipsis, ellipsisNumBytes);
+ numBytes = Tk_MeasureChars(tkfont, tmpStr,
+ bytesTest + ellipsisNumBytes,
+ *maxPixels, 0, &pixelsTest);
+ if (numBytes == bytesTest + ellipsisNumBytes)
+ {
+ (*maxPixels) = pixelsTest;
+ if (tmpStr != staticStr)
+ ckfree(tmpStr);
+ return bytesTest;
+ }
+ bytesTest = Tcl_UtfPrev(string + bytesTest, string) - string;
+ }
+
+ /* No single char + ellipsis fits. Return number of chars that fit */
+ /* Negative tells caller to not add ellipsis */
+ (*maxPixels) = pixels;
+ if (tmpStr != staticStr)
+ ckfree(tmpStr);
+ return -bytesThatFit;
+}
+
+/* Draws a horizontal 1-pixel tall dotted line */
+void HDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int x2)
+{
+#ifdef WIN32
+ TkWinDCState state;
+ HDC dc;
+ HPEN pen, oldPen;
+ int nw;
+ int wx = x1 + tree->drawableXOrigin;
+ int wy = y1 + tree->drawableYOrigin;
+
+ dc = TkWinGetDrawableDC(tree->display, drawable, &state);
+/* SetROP2(dc, R2_NOT); */
+
+ pen = CreatePen(PS_SOLID, 1, gc->foreground);
+ oldPen = SelectObject(dc, pen);
+
+ nw = !(wx & 1) == !(wy & 1);
+ for (x1 += !nw; x1 < x2; x1 += 2)
+ {
+ MoveToEx(dc, x1, y1, NULL);
+ LineTo(dc, x1 + 1, y1);
+ }
+
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+#else
+ int nw;
+ int wx = x1 + tree->drawableXOrigin;
+ int wy = y1 + tree->drawableYOrigin;
+
+ nw = !(wx & 1) == !(wy & 1);
+ for (x1 += !nw; x1 < x2; x1 += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x1, y1);
+ }
+#endif
+}
+
+/* Draws a vertical 1-pixel wide dotted line */
+void VDotLine(TreeCtrl *tree, Drawable drawable, GC gc, int x1, int y1, int y2)
+{
+#ifdef WIN32
+ TkWinDCState state;
+ HDC dc;
+ HPEN pen, oldPen;
+ int nw;
+ int wx = x1 + tree->drawableXOrigin;
+ int wy = y1 + tree->drawableYOrigin;
+
+ dc = TkWinGetDrawableDC(tree->display, drawable, &state);
+/* SetROP2(dc, R2_NOT); */
+
+ pen = CreatePen(PS_SOLID, 1, gc->foreground);
+ oldPen = SelectObject(dc, pen);
+
+ nw = !(wx & 1) == !(wy & 1);
+ for (y1 += !nw; y1 < y2; y1 += 2)
+ {
+ MoveToEx(dc, x1, y1, NULL);
+ LineTo(dc, x1 + 1, y1);
+ }
+
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+#else
+ int nw;
+ int wx = x1 + tree->drawableXOrigin;
+ int wy = y1 + tree->drawableYOrigin;
+
+ nw = !(wx & 1) == !(wy & 1);
+ for (y1 += !nw; y1 < y2; y1 += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x1, y1);
+ }
+#endif
+}
+
+/* Draws 0 or more sides of a rectangle, dot-on dot-off, XOR style */
+void DrawActiveOutline(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height, int open)
+{
+#ifdef WIN32
+ int wx = x + tree->drawableXOrigin;
+ int wy = y + tree->drawableYOrigin;
+ int w = !(open & 0x01);
+ int n = !(open & 0x02);
+ int e = !(open & 0x04);
+ int s = !(open & 0x08);
+ int nw, ne, sw, se;
+ int i;
+ TkWinDCState state;
+ HDC dc;
+
+ /* Dots on even pixels only */
+ nw = !(wx & 1) == !(wy & 1);
+ ne = !((wx + width - 1) & 1) == !(wy & 1);
+ sw = !(wx & 1) == !((wy + height - 1) & 1);
+ se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1);
+
+ dc = TkWinGetDrawableDC(tree->display, drawable, &state);
+ SetROP2(dc, R2_NOT);
+ if (w) /* left */
+ {
+ for (i = !nw; i < height; i += 2)
+ {
+ MoveToEx(dc, x, y + i, NULL);
+ LineTo(dc, x + 1, y + i);
+ }
+ }
+ if (n) /* top */
+ {
+ for (i = nw ? w * 2 : 1; i < width; i += 2)
+ {
+ MoveToEx(dc, x + i, y, NULL);
+ LineTo(dc, x + i + 1, y);
+ }
+ }
+ if (e) /* right */
+ {
+ for (i = ne ? n * 2 : 1; i < height; i += 2)
+ {
+ MoveToEx(dc, x + width - 1, y + i, NULL);
+ LineTo(dc, x + width, y + i);
+ }
+ }
+ if (s) /* bottom */
+ {
+ for (i = sw ? w * 2 : 1; i < width - (se && e); i += 2)
+ {
+ MoveToEx(dc, x + i, y + height - 1, NULL);
+ LineTo(dc, x + i + 1, y + height - 1);
+ }
+ }
+ TkWinReleaseDrawableDC(drawable, dc, &state);
+#else
+ int wx = x + tree->drawableXOrigin;
+ int wy = y + tree->drawableYOrigin;
+ int w = !(open & 0x01);
+ int n = !(open & 0x02);
+ int e = !(open & 0x04);
+ int s = !(open & 0x08);
+ int nw, ne, sw, se;
+ int i;
+ XGCValues gcValues;
+ unsigned long gcMask;
+ GC gc;
+
+ /* Dots on even pixels only */
+ nw = !(wx & 1) == !(wy & 1);
+ ne = !((wx + width - 1) & 1) == !(wy & 1);
+ sw = !(wx & 1) == !((wy + height - 1) & 1);
+ se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1);
+
+ gcValues.function = GXinvert;
+ gcMask = GCFunction;
+ gc = Tk_GetGC(tree->tkwin, gcMask, &gcValues);
+
+ if (w) /* left */
+ {
+ for (i = !nw; i < height; i += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x, y + i);
+ }
+ }
+ if (n) /* top */
+ {
+ for (i = nw ? w * 2 : 1; i < width; i += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x + i, y);
+ }
+ }
+ if (e) /* right */
+ {
+ for (i = ne ? n * 2 : 1; i < height; i += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x + width - 1, y + i);
+ }
+ }
+ if (s) /* bottom */
+ {
+ for (i = sw ? w * 2 : 1; i < width - (se && e); i += 2)
+ {
+ XDrawPoint(tree->display, drawable, gc, x + i, y + height - 1);
+ }
+ }
+
+ Tk_FreeGC(tree->display, gc);
+#endif
+}
+
+void DotRect(TreeCtrl *tree, Drawable drawable, int x, int y, int width, int height)
+{
+ DrawActiveOutline(tree, drawable, x, y, width, height, 0);
+}
+
+struct DotStatePriv
+{
+ TreeCtrl *tree;
+ Drawable drawable;
+#ifdef WIN32
+ HDC dc;
+ TkWinDCState dcState;
+ HRGN rgn;
+#else
+ GC gc;
+ TkRegion rgn;
+#endif
+};
+
+void DotRect_Setup(TreeCtrl *tree, Drawable drawable, DotState *p)
+{
+ struct DotStatePriv *dotState = (struct DotStatePriv *) p;
+#ifdef WIN32
+#else
+ XGCValues gcValues;
+ unsigned long mask;
+ XRectangle xrect;
+#endif
+
+ if (sizeof(*dotState) > sizeof(*p))
+ panic("DotRect_Setup: DotState hack is too small");
+
+ dotState->tree = tree;
+ dotState->drawable = drawable;
+#ifdef WIN32
+ dotState->dc = TkWinGetDrawableDC(tree->display, drawable, &dotState->dcState);
+
+ /* XOR drawing */
+ SetROP2(dotState->dc, R2_NOT);
+
+ /* Keep drawing inside the contentbox */
+ dotState->rgn = CreateRectRgn(
+ tree->inset,
+ tree->inset + Tree_HeaderHeight(tree),
+ Tk_Width(tree->tkwin) - tree->inset,
+ Tk_Height(tree->tkwin) - tree->inset);
+ SelectClipRgn(dotState->dc, dotState->rgn);
+#else
+ gcValues.line_style = LineOnOffDash;
+ gcValues.line_width = 1;
+ gcValues.dash_offset = 0;
+ gcValues.dashes = 1;
+ gcValues.function = GXinvert;
+ mask = GCLineWidth | GCLineStyle | GCDashList | GCDashOffset | GCFunction;
+ dotState->gc = Tk_GetGC(tree->tkwin, mask, &gcValues);
+
+ /* Keep drawing inside the contentbox */
+ dotState->rgn = TkCreateRegion();
+ xrect.x = tree->inset;
+ xrect.y = tree->inset + Tree_HeaderHeight(tree);
+ xrect.width = Tk_Width(tree->tkwin) - tree->inset - xrect.x;
+ xrect.height = Tk_Height(tree->tkwin) - tree->inset - xrect.y;
+ TkUnionRectWithRegion(&xrect, dotState->rgn, dotState->rgn);
+ TkSetRegion(tree->display, dotState->gc, dotState->rgn);
+#endif
+}
+
+void DotRect_Draw(DotState *p, int x, int y, int width, int height)
+{
+ struct DotStatePriv *dotState = (struct DotStatePriv *) p;
+#ifdef WIN32
+#if 1
+ RECT rect;
+
+ rect.left = x;
+ rect.right = x + width;
+ rect.top = y;
+ rect.bottom = y + height;
+ DrawFocusRect(dotState->dc, &rect);
+#else
+ HDC dc = dotState->dc;
+ int i;
+ int wx = x + dotState->tree->drawableXOrigin;
+ int wy = y + dotState->tree->drawableYOrigin;
+ int nw, ne, sw, se;
+
+ /* Dots on even pixels only */
+ nw = !(wx & 1) == !(wy & 1);
+ ne = !((wx + width - 1) & 1) == !(wy & 1);
+ sw = !(wx & 1) == !((wy + height - 1) & 1);
+ se = !((wx + width - 1) & 1) == !((wy + height - 1) & 1);
+
+ for (i = !nw; i < height; i += 2)
+ {
+ MoveToEx(dc, x, y + i, NULL);
+ LineTo(dc, x + 1, y + i);
+ }
+ for (i = nw ? 2 : 1; i < width; i += 2)
+ {
+ MoveToEx(dc, x + i, y, NULL);
+ LineTo(dc, x + i + 1, y);
+ }
+ for (i = ne ? 2 : 1; i < height; i += 2)
+ {
+ MoveToEx(dc, x + width - 1, y + i, NULL);
+ LineTo(dc, x + width, y + i);
+ }
+ for (i = sw ? 2 : 1; i < width - se; i += 2)
+ {
+ MoveToEx(dc, x + i, y + height - 1, NULL);
+ LineTo(dc, x + i + 1, y + height - 1);
+ }
+#endif
+#else
+ XDrawRectangle(dotState->tree->display, dotState->drawable, dotState->gc,
+ x, y, width - 1, height - 1);
+#endif
+}
+
+void DotRect_Restore(DotState *p)
+{
+ struct DotStatePriv *dotState = (struct DotStatePriv *) p;
+#ifdef WIN32
+ SelectClipRgn(dotState->dc, NULL);
+ DeleteObject(dotState->rgn);
+ TkWinReleaseDrawableDC(dotState->drawable, dotState->dc, &dotState->dcState);
+#else
+ XSetClipMask(dotState->tree->display, dotState->gc, None);
+ Tk_FreeGC(dotState->tree->display, dotState->gc);
+#endif
+}
+
+/*
+ * Replacement for Tk_TextLayout stuff. Allows the caller to break lines
+ * on character boundaries (as well as word boundaries). Allows the caller
+ * to specify the maximum number of lines to display. Will add ellipsis "..."
+ * to the end of text that is too long to fit (when max lines specified).
+ */
+
+typedef struct LayoutChunk
+{
+ CONST char *start; /* Pointer to simple string to be displayed.
+ * * This is a pointer into the TkTextLayout's
+ * * string. */
+ int numBytes; /* The number of bytes in this chunk. */
+ int numChars; /* The number of characters in this chunk. */
+ int numDisplayChars; /* The number of characters to display when
+ * * this chunk is displayed. Can be less than
+ * * numChars if extra space characters were
+ * * absorbed by the end of the chunk. This
+ * * will be < 0 if this is a chunk that is
+ * * holding a tab or newline. */
+ int x, y; /* The origin of the first character in this
+ * * chunk with respect to the upper-left hand
+ * * corner of the TextLayout. */
+ int totalWidth; /* Width in pixels of this chunk. Used
+ * * when hit testing the invisible spaces at
+ * * the end of a chunk. */
+ int displayWidth; /* Width in pixels of the displayable
+ * * characters in this chunk. Can be less than
+ * * width if extra space characters were
+ * * absorbed by the end of the chunk. */
+ int ellipsis; /* TRUE if adding "..." */
+} LayoutChunk;
+
+typedef struct LayoutInfo
+{
+ Tk_Font tkfont; /* The font used when laying out the text. */
+ CONST char *string; /* The string that was layed out. */
+ int numLines; /* Number of lines */
+ int width; /* The maximum width of all lines in the
+ * * text layout. */
+ int height;
+ int numChunks; /* Number of chunks actually used in
+ * * following array. */
+ LayoutChunk chunks[1]; /* Array of chunks. The actual size will
+ * * be maxChunks. THIS FIELD MUST BE THE LAST
+ * * IN THE STRUCTURE. */
+} LayoutInfo;
+
+static LayoutChunk *NewChunk(LayoutInfo **layoutPtrPtr, int *maxPtr,
+ CONST char *start, int numBytes, int curX, int newX, int y)
+{
+ LayoutInfo *layoutPtr;
+ LayoutChunk *chunkPtr;
+ int maxChunks, numChars;
+ size_t s;
+
+ layoutPtr = *layoutPtrPtr;
+ maxChunks = *maxPtr;
+ if (layoutPtr->numChunks == maxChunks)
+ {
+ maxChunks *= 2;
+ s = sizeof(LayoutInfo) + ((maxChunks - 1) * sizeof(LayoutChunk));
+ layoutPtr = (LayoutInfo *) ckrealloc((char *) layoutPtr, s);
+
+ *layoutPtrPtr = layoutPtr;
+ *maxPtr = maxChunks;
+ }
+ numChars = Tcl_NumUtfChars(start, numBytes);
+ chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks];
+ chunkPtr->start = start;
+ chunkPtr->numBytes = numBytes;
+ chunkPtr->numChars = numChars;
+ chunkPtr->numDisplayChars = numChars;
+ chunkPtr->x = curX;
+ chunkPtr->y = y;
+ chunkPtr->totalWidth = newX - curX;
+ chunkPtr->displayWidth = newX - curX;
+ chunkPtr->ellipsis = FALSE;
+ layoutPtr->numChunks++;
+
+ return chunkPtr;
+}
+
+TextLayout TextLayout_Compute(
+ Tk_Font tkfont, /* Font that will be used to display text. */
+ CONST char *string, /* String whose dimensions are to be
+ ** computed. */
+ int numChars, /* Number of characters to consider from
+ ** string, or < 0 for strlen(). */
+ int wrapLength, /* Longest permissible line length, in
+ ** pixels. <= 0 means no automatic wrapping:
+ ** just let lines get as long as needed. */
+ Tk_Justify justify, /* How to justify lines. */
+ int maxLines,
+ int flags /* Flag bits OR-ed together.
+ ** TK_IGNORE_TABS means that tab characters
+ ** should not be expanded. TK_IGNORE_NEWLINES
+ ** means that newline characters should not
+ ** cause a line break. */
+ )
+{
+ CONST char *start, *end, *special;
+ int n, y, bytesThisChunk, maxChunks;
+ int baseline, height, curX, newX, maxWidth;
+ LayoutInfo *layoutPtr;
+ LayoutChunk *chunkPtr;
+ Tk_FontMetrics fm;
+ Tcl_DString lineBuffer;
+ int *lineLengths;
+ int curLine;
+ int tabWidth = 20; /* FIXME */
+
+ Tcl_DStringInit(&lineBuffer);
+
+ Tk_GetFontMetrics(tkfont, &fm);
+ height = fm.ascent + fm.descent;
+
+ if (numChars < 0)
+ numChars = Tcl_NumUtfChars(string, -1);
+ if (wrapLength == 0)
+ wrapLength = -1;
+
+ maxChunks = 1;
+
+ layoutPtr = (LayoutInfo *) ckalloc(sizeof(LayoutInfo) + (maxChunks -
+ 1) * sizeof(LayoutChunk));
+ layoutPtr->tkfont = tkfont;
+ layoutPtr->string = string;
+ layoutPtr->numChunks = 0;
+ layoutPtr->numLines = 0;
+
+ baseline = fm.ascent;
+ maxWidth = 0;
+
+ curX = 0;
+
+ end = Tcl_UtfAtIndex(string, numChars);
+ special = string;
+
+ flags &= TK_WHOLE_WORDS | TK_IGNORE_TABS | TK_IGNORE_NEWLINES;
+ flags |= TK_AT_LEAST_ONE;
+ for (start = string; start < end;)
+ {
+ if (start >= special)
+ {
+ for (special = start; special < end; special++)
+ {
+ if (!(flags & TK_IGNORE_NEWLINES))
+ {
+ if ((*special == '\n') || (*special == '\r'))
+ break;
+ }
+ if (!(flags & TK_IGNORE_TABS))
+ {
+ if (*special == '\t')
+ break;
+ }
+ }
+ }
+
+ chunkPtr = NULL;
+ if (start < special)
+ {
+ bytesThisChunk = Tk_MeasureChars(tkfont, start, special - start,
+ wrapLength - curX, flags, &newX);
+ newX += curX;
+ flags &= ~TK_AT_LEAST_ONE;
+ if (bytesThisChunk > 0)
+ {
+ chunkPtr = NewChunk(&layoutPtr, &maxChunks, start,
+ bytesThisChunk, curX, newX, baseline);
+ start += bytesThisChunk;
+ curX = newX;
+ }
+ }
+
+ if ((start == special) && (special < end))
+ {
+ chunkPtr = NULL;
+ if (*special == '\t')
+ {
+ newX = curX + tabWidth;
+ newX -= newX % tabWidth;
+ NewChunk(&layoutPtr, &maxChunks, start, 1, curX, newX,
+ baseline)->numDisplayChars = -1;
+ start++;
+ if ((start < end) && ((wrapLength <= 0) ||
+ (newX <= wrapLength)))
+ {
+ curX = newX;
+ flags &= ~TK_AT_LEAST_ONE;
+ continue;
+ }
+ }
+ else
+ {
+ NewChunk(&layoutPtr, &maxChunks, start, 1, curX, curX,
+ baseline)->numDisplayChars = -1;
+ start++;
+ goto wrapLine;
+ }
+ }
+
+ while ((start < end) && isspace(UCHAR(*start)))
+ {
+ if (!(flags & TK_IGNORE_NEWLINES))
+ {
+ if ((*start == '\n') || (*start == '\r'))
+ break;
+ }
+ if (!(flags & TK_IGNORE_TABS))
+ {
+ if (*start == '\t')
+ break;
+ }
+ start++;
+ }
+ if (chunkPtr != NULL)
+ {
+ CONST char *end;
+
+ end = chunkPtr->start + chunkPtr->numBytes;
+ bytesThisChunk = start - end;
+ if (bytesThisChunk > 0)
+ {
+ bytesThisChunk =
+ Tk_MeasureChars(tkfont, end, bytesThisChunk, -1, 0,
+ &chunkPtr->totalWidth);
+ chunkPtr->numBytes += bytesThisChunk;
+ chunkPtr->numChars += Tcl_NumUtfChars(end, bytesThisChunk);
+ chunkPtr->totalWidth += curX;
+ }
+ }
+
+wrapLine:
+ flags |= TK_AT_LEAST_ONE;
+
+ if (curX > maxWidth)
+ maxWidth = curX;
+
+ Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
+
+ curX = 0;
+ baseline += height;
+
+ if ((maxLines > 0) && (++layoutPtr->numLines >= maxLines))
+ break;
+ }
+
+ if (start >= end)
+ if ((layoutPtr->numChunks > 0) && !(flags & TK_IGNORE_NEWLINES))
+ {
+ if (layoutPtr->chunks[layoutPtr->numChunks - 1].start[0] == '\n')
+ {
+ chunkPtr =
+ NewChunk(&layoutPtr, &maxChunks, start, 0, curX, curX,
+ baseline);
+ chunkPtr->numDisplayChars = -1;
+ Tcl_DStringAppend(&lineBuffer, (char *) &curX, sizeof(curX));
+ baseline += height;
+ }
+ }
+
+#if 1
+ /* Fiddle with chunks on the last line to add ellipsis if there is some
+ * text remaining */
+ if ((start < end) && (layoutPtr->numChunks > 0))
+ {
+ chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 1];
+ if (wrapLength > 0)
+ {
+ y = chunkPtr->y;
+ for (n = layoutPtr->numChunks - 1; n >= 0; n--)
+ {
+ chunkPtr = &layoutPtr->chunks[n];
+
+ /* Only consider the last line */
+ if (chunkPtr->y != y)
+ break;
+
+ if (chunkPtr->start[0] == '\n')
+ continue;
+
+ newX = chunkPtr->totalWidth - 1;
+ bytesThisChunk = Ellipsis(tkfont, (char *) chunkPtr->start,
+ chunkPtr->numBytes, &newX, "...");
+ if (bytesThisChunk > 0)
+ {
+ chunkPtr->numBytes = bytesThisChunk;
+ chunkPtr->numChars = Tcl_NumUtfChars(chunkPtr->start, bytesThisChunk);
+ chunkPtr->numDisplayChars = chunkPtr->numChars;
+ chunkPtr->ellipsis = TRUE;
+ chunkPtr->displayWidth = newX;
+ chunkPtr->totalWidth = newX;
+ lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
+ lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + newX;
+ if (chunkPtr->x + newX > maxWidth)
+ maxWidth = chunkPtr->x + newX;
+ break;
+ }
+ }
+ }
+ else
+ {
+ char staticStr[256], *buf = staticStr;
+ char *ellipsis = "...";
+ int ellipsisLen = strlen(ellipsis);
+
+ if (chunkPtr->start[0] == '\n')
+ {
+ if (layoutPtr->numChunks == 1)
+ goto finish;
+ if (layoutPtr->chunks[layoutPtr->numChunks - 2].y != chunkPtr->y)
+ goto finish;
+ chunkPtr = &layoutPtr->chunks[layoutPtr->numChunks - 2];
+ }
+
+ if (chunkPtr->numBytes + ellipsisLen > sizeof(staticStr))
+ buf = ckalloc(chunkPtr->numBytes + ellipsisLen);
+ memcpy(buf, chunkPtr->start, chunkPtr->numBytes);
+ memcpy(buf + chunkPtr->numBytes, ellipsis, ellipsisLen);
+ Tk_MeasureChars(tkfont, buf,
+ chunkPtr->numBytes + ellipsisLen, -1, 0,
+ &chunkPtr->displayWidth);
+ chunkPtr->totalWidth = chunkPtr->displayWidth;
+ chunkPtr->ellipsis = TRUE;
+ lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
+ lineLengths[layoutPtr->numLines - 1] = chunkPtr->x + chunkPtr->displayWidth;
+ if (chunkPtr->x + chunkPtr->displayWidth > maxWidth)
+ maxWidth = chunkPtr->x + chunkPtr->displayWidth;
+ if (buf != staticStr)
+ ckfree(buf);
+ }
+ }
+finish:
+#endif
+
+ layoutPtr->width = maxWidth;
+ layoutPtr->height = baseline - fm.ascent;
+ if (layoutPtr->numChunks == 0)
+ {
+ layoutPtr->height = height;
+
+ layoutPtr->numChunks = 1;
+ layoutPtr->chunks[0].start = string;
+ layoutPtr->chunks[0].numBytes = 0;
+ layoutPtr->chunks[0].numChars = 0;
+ layoutPtr->chunks[0].numDisplayChars = -1;
+ layoutPtr->chunks[0].x = 0;
+ layoutPtr->chunks[0].y = fm.ascent;
+ layoutPtr->chunks[0].totalWidth = 0;
+ layoutPtr->chunks[0].displayWidth = 0;
+ }
+ else
+ {
+ curLine = 0;
+ chunkPtr = layoutPtr->chunks;
+ y = chunkPtr->y;
+ lineLengths = (int *) Tcl_DStringValue(&lineBuffer);
+ for (n = 0; n < layoutPtr->numChunks; n++)
+ {
+ int extra;
+
+ if (chunkPtr->y != y)
+ {
+ curLine++;
+ y = chunkPtr->y;
+ }
+ extra = maxWidth - lineLengths[curLine];
+ if (justify == TK_JUSTIFY_CENTER)
+ {
+ chunkPtr->x += extra / 2;
+ }
+ else if (justify == TK_JUSTIFY_RIGHT)
+ {
+ chunkPtr->x += extra;
+ }
+ chunkPtr++;
+ }
+ }
+
+ Tcl_DStringFree(&lineBuffer);
+
+ return (TextLayout) layoutPtr;
+}
+
+void TextLayout_Free(TextLayout textLayout)
+{
+ LayoutInfo *layoutPtr = (LayoutInfo *) textLayout;
+
+ ckfree((char *) layoutPtr);
+}
+
+void TextLayout_Size(TextLayout textLayout, int *widthPtr, int *heightPtr)
+{
+ LayoutInfo *layoutPtr = (LayoutInfo *) textLayout;
+
+ if (widthPtr != NULL)
+ (*widthPtr) = layoutPtr->width;
+ if (heightPtr != NULL)
+ (*heightPtr) = layoutPtr->height;
+}
+
+void TextLayout_Draw(
+ Display *display, /* Display on which to draw. */
+ Drawable drawable, /* Window or pixmap in which to draw. */
+ GC gc, /* Graphics context to use for drawing text. */
+ TextLayout layout, /* Layout information, from a previous call
+ * * to Tk_ComputeTextLayout(). */
+ int x, int y, /* Upper-left hand corner of rectangle in
+ * * which to draw (pixels). */
+ int firstChar, /* The index of the first character to draw
+ * * from the given text item. 0 specfies the
+ * * beginning. */
+ int lastChar /* The index just after the last character
+ * * to draw from the given text item. A number
+ * * < 0 means to draw all characters. */
+)
+{
+ LayoutInfo *layoutPtr = (LayoutInfo *) layout;
+ int i, numDisplayChars, drawX;
+ CONST char *firstByte;
+ CONST char *lastByte;
+ LayoutChunk *chunkPtr;
+
+ if (lastChar < 0)
+ lastChar = 100000000;
+ chunkPtr = layoutPtr->chunks;
+ for (i = 0; i < layoutPtr->numChunks; i++)
+ {
+ numDisplayChars = chunkPtr->numDisplayChars;
+ if ((numDisplayChars > 0) && (firstChar < numDisplayChars))
+ {
+ if (firstChar <= 0)
+ {
+ drawX = 0;
+ firstChar = 0;
+ firstByte = chunkPtr->start;
+ }
+ else
+ {
+ firstByte = Tcl_UtfAtIndex(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);
+#if 1
+ if (chunkPtr->ellipsis)
+ {
+ char staticStr[256], *buf = staticStr;
+ char *ellipsis = "...";
+ int ellipsisLen = strlen(ellipsis);
+
+ if ((lastByte - firstByte) + ellipsisLen > sizeof(staticStr))
+ buf = ckalloc((lastByte - firstByte) + ellipsisLen);
+ memcpy(buf, firstByte, (lastByte - firstByte));
+ memcpy(buf + (lastByte - firstByte), ellipsis, ellipsisLen);
+ Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
+ buf, (lastByte - firstByte) + ellipsisLen,
+ x + chunkPtr->x + drawX, y + chunkPtr->y);
+ if (buf != staticStr)
+ ckfree(buf);
+ }
+ else
+#endif
+ Tk_DrawChars(display, drawable, gc, layoutPtr->tkfont,
+ firstByte, lastByte - firstByte, x + chunkPtr->x + drawX,
+ y + chunkPtr->y);
+ }
+ firstChar -= chunkPtr->numChars;
+ lastChar -= chunkPtr->numChars;
+ if (lastChar <= 0)
+ break;
+ chunkPtr++;
+ }
+}