From 6b97ba2c3cc4da8e29a95fc694baea3b57cf8018 Mon Sep 17 00:00:00 2001 From: vincentdarley Date: Mon, 15 Dec 2003 11:51:00 +0000 Subject: text widget more extensive documentation, and two small code improvements --- ChangeLog | 12 ++++++ generic/tkText.h | 4 +- generic/tkTextBTree.c | 45 +++++++++++++++++---- generic/tkTextDisp.c | 106 ++++++++++++++++++++++++++++++++++++++++++-------- generic/tkTextIndex.c | 5 ++- 5 files changed, 147 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index 37396be..a562964 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2003-12-15 Vince Darley + + * generic/tkText.h: + * generic/tkText.h: + * generic/tkTextBTree.c: + * generic/tkTextDisp.c: + * generic/tkTextIndex.c: improved documentation in comments to + explain how pixel heights are kept track of. Also ensured + correct clean-up of elide-state calculation, even with very + large numbers of tags. Also provided slightly better updating + of cache for totally elided display lines. + 2003-12-12 David Gravereaux * win/tkWinEmbed.c (TkWinEmbeddedEventProc) : for loop dereferences diff --git a/generic/tkText.h b/generic/tkText.h index 051c874..57560e8 100644 --- a/generic/tkText.h +++ b/generic/tkText.h @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkText.h,v 1.23 2003/12/05 17:19:06 vincentdarley Exp $ + * RCS: @(#) $Id: tkText.h,v 1.24 2003/12/15 11:51:05 vincentdarley Exp $ */ #ifndef _TKTEXT @@ -1012,6 +1012,8 @@ EXTERN TkTextIndex * TkTextMakeCharIndex _ANSI_ARGS_((TkTextBTree tree, TkTextIndex *indexPtr)); EXTERN int TkTextMeasureDown _ANSI_ARGS_((TkText *textPtr, TkTextIndex *srcPtr, int distance)); +EXTERN void TkTextFreeElideInfo _ANSI_ARGS_(( + TkTextElideInfo *infoPtr)); EXTERN int TkTextIsElided _ANSI_ARGS_((CONST TkText *textPtr, CONST TkTextIndex *indexPtr, TkTextElideInfo *infoPtr)); diff --git a/generic/tkTextBTree.c b/generic/tkTextBTree.c index 22d91c7..b2db5aa 100644 --- a/generic/tkTextBTree.c +++ b/generic/tkTextBTree.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkTextBTree.c,v 1.10 2003/11/08 17:22:46 vincentdarley Exp $ + * RCS: @(#) $Id: tkTextBTree.c,v 1.11 2003/12/15 11:51:06 vincentdarley Exp $ */ #include "tkInt.h" @@ -55,7 +55,9 @@ typedef struct Node { int numChildren; /* Number of children of this node. */ int numLines; /* Total number of lines (leaves) in * the subtree rooted here. */ - int numPixels; + int numPixels; /* Total number of vertical + * display pixels in the + * subtree rooted here. */ } Node; /* @@ -2837,13 +2839,14 @@ TkTextIsElided(textPtr, indexPtr, elideInfo) } } - if (LOTSA_TAGS < infoPtr->numTags) { - ckfree((char *) infoPtr->tagCnts); - ckfree((char *) infoPtr->tagPtrs); - } elide = infoPtr->elide; - + if (elideInfo == NULL) { + if (LOTSA_TAGS < infoPtr->numTags) { + ckfree((char *) infoPtr->tagCnts); + ckfree((char *) infoPtr->tagPtrs); + } + ckfree((char*) infoPtr); } @@ -2853,6 +2856,34 @@ TkTextIsElided(textPtr, indexPtr, elideInfo) /* *---------------------------------------------------------------------- * + * TkTextFreeElideInfo -- + * + * This is a utility procedure used to free up any memory + * allocated by the TkTextIsElided function above. + * + * Results: + * None. + * + * Side effects: + * Memory may be freed. + * + *---------------------------------------------------------------------- + */ + +void +TkTextFreeElideInfo(elideInfo) + TkTextElideInfo *elideInfo; /* Free any allocated memory in this + * structure. */ +{ + if (LOTSA_TAGS < elideInfo->numTags) { + ckfree((char*)elideInfo->tagCnts); + ckfree((char*)elideInfo->tagPtrs); + } +} + +/* + *---------------------------------------------------------------------- + * * IncCount -- * * This is a utility procedure used by TkBTreeGetTags. It diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c index 6968c82..1fbf4cf 100644 --- a/generic/tkTextDisp.c +++ b/generic/tkTextDisp.c @@ -13,7 +13,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkTextDisp.c,v 1.37 2003/12/05 17:19:06 vincentdarley Exp $ + * RCS: @(#) $Id: tkTextDisp.c,v 1.38 2003/12/15 11:51:06 vincentdarley Exp $ */ #include "tkPort.h" @@ -25,6 +25,53 @@ #endif /* + * "Calculations of line pixel heights and the size of the vertical + * scrollbar." + * + * Given that tag, font and elide changes can happen to large numbers of + * diverse chunks in a text widget containing megabytes of text, it is + * not possible to recalculate all affected height information + * immediately any such change takes place and maintain a responsive + * user-experience. Yet, for an accurate vertical scrollbar to be + * drawn, we must know the total number of vertical pixels shown on + * display versus the number available to be displayed. + * + * The way the text widget solves this problem is by maintaining cached + * line pixel heights (in the BTree for each logical line), and having + * asynchronous timer callbacks (i) to iterate through the logical + * lines recalculating their heights, and (ii) to recalculate the + * vertical scrollbar's position and size. + * + * Typically this works well but there are some situations where the + * overall functional design of this file causes some problems. These + * problems can only arise because the calculations used to display + * lines on screen are not connected to those in the iterating-line- + * recalculation-process. + * + * The reason for this disconnect is that the display calculations + * operate in display lines, and the iteration and cache operates in + * logical lines. Given that the display calculations both need not + * contain complete logical lines (at top or bottom of display), and + * that they do not actually keep track of logical lines (for simplicity + * of code and historical design), this means a line may be known and + * drawn with a different pixel height to that which is cached in the + * BTree, and this might cause some temporary undesirable mismatch + * between display and the vertical scrollbar. + * + * All such mismatches should be temporary, however, since the + * asynchronous height calculations will always catch up eventually. + * + * For further details see the comments before and within the following + * functions below: LayoutDLine, AsyncUpdateLineMetrics, GetYView, + * GetYPixelCount, TkTextUpdateOneLine, TkTextUpdateLineMetrics. + * + * For details of the way in which the BTree keeps track of pixel + * heights, see tkTextBTree.c. Basically the BTree maintains two + * pieces of information: the logical line indices and the pixel + * height cache. + */ + +/* * The following structure describes how to display a range of characters. * The information is generated by scanning all of the tags associated * with the characters and combining that with default information for @@ -415,7 +462,7 @@ static void GetXView _ANSI_ARGS_((Tcl_Interp *interp, TkText *textPtr, int report)); static void GetYView _ANSI_ARGS_((Tcl_Interp *interp, TkText *textPtr, int report)); -static int GetPixelCount _ANSI_ARGS_((TkText *textPtr, +static int GetYPixelCount _ANSI_ARGS_((TkText *textPtr, DLine *dlPtr)); static DLine * LayoutDLine _ANSI_ARGS_((TkText *textPtr, CONST TkTextIndex *indexPtr)); @@ -1030,10 +1077,26 @@ LayoutDLine(textPtr, indexPtr) if (elide) { dlPtr->byteCount = maxBytes; dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0; + if (dlPtr->index.byteIndex == 0) { + /* + * Elided state goes from beginning to end of + * an entire logical line. This means we can + * update the line's pixel height, and bring + * its pixel calculation up to date. + */ + dlPtr->index.linePtr->pixelCalculationEpoch + = textPtr->dInfoPtr->lineMetricUpdateEpoch; + + if (dlPtr->index.linePtr->pixelHeight != 0) { + TkBTreeAdjustPixelHeight(dlPtr->index.linePtr, 0); + } + } + TkTextFreeElideInfo(&info); return dlPtr; } } - + TkTextFreeElideInfo(&info); + /* * Each iteration of the loop below creates one TkTextDispChunk for * the new display line. The line will always have at least one @@ -5381,17 +5444,22 @@ GetXView(interp, textPtr, report) /* *---------------------------------------------------------------------- * - * GetPixelCount -- + * GetYPixelCount -- * * How many pixels are there between the absolute top of the * widget and the top of the given DLine. * - * This is only ever called when dlPtr is the first display - * line in the widget (by 'GetYView'). We have to be careful - * if dlPtr's logical line wraps enough times to fill the - * text widget's current view -- in this case we won't have - * enough dlPtrs in the linked list to be able to subtract off - * what we want. + * While this function will work for any valid DLine, it is + * only ever called when dlPtr is the first display + * line in the widget (by 'GetYView'). This means that + * usually this function is a very quick calculation, since + * it can use the pre-calculated linked-list of DLines for + * height information. + * + * The only situation where this breaks down is if dlPtr's logical + * line wraps enough times to fill the text widget's current view + * -- in this case we won't have enough dlPtrs in the linked list + * to be able to subtract off what we want. * * Results: * The number of pixels. @@ -5403,20 +5471,26 @@ GetXView(interp, textPtr, report) */ static int -GetPixelCount(textPtr, dlPtr) +GetYPixelCount(textPtr, dlPtr) TkText *textPtr; /* Information about text widget. */ DLine *dlPtr; /* Information about the layout * of a given index */ { TkTextLine *linePtr = dlPtr->index.linePtr; /* - * Get the pixel count to the top of dlPtr's logical line. + * Get the pixel count to the top of dlPtr's logical line. The + * rest of the function is then concerned with updating 'count' + * for any difference between the top of the logical line and + * the display line. */ int count = TkBTreePixels(linePtr); /* * For the common case where this dlPtr is also the start of the - * logical line, we can return right away. + * logical line, we can return right away. Note the implicit + * assumption here that the start of a logical line is always the + * start of a display line (if the 'elide won't elide first newline' + * bug is fixed, this will no longer necessarily be true). */ if (dlPtr->index.byteIndex == 0) { return count; @@ -5424,8 +5498,8 @@ GetPixelCount(textPtr, dlPtr) /* * Add on the logical line's height to reach one pixel beyond the - * bottom of the line. And then subtract off the heights of all the - * display lines from dlPtr to the end of its logical line. + * bottom of the logical line. And then subtract off the heights of + * all the display lines from dlPtr to the end of its logical line. * * A different approach would be to lay things out from the start of * the logical line until we reach dlPtr, but since none of those are @@ -5543,7 +5617,7 @@ GetYView(interp, textPtr, report) * partially visible, then we use 'topPixelOffset' to get the * difference. */ - count = GetPixelCount(textPtr, dlPtr); + count = GetYPixelCount(textPtr, dlPtr); first = (count + dInfoPtr->topPixelOffset) / (double) totalPixels; /* diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c index c270320..667cb64 100644 --- a/generic/tkTextIndex.c +++ b/generic/tkTextIndex.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tkTextIndex.c,v 1.12 2003/11/12 17:19:18 vincentdarley Exp $ + * RCS: @(#) $Id: tkTextIndex.c,v 1.13 2003/12/15 11:51:06 vincentdarley Exp $ */ #include "default.h" @@ -1473,6 +1473,7 @@ TkTextIndexForwChars(textPtr, srcPtr, charCount, dstPtr, type) } forwardCharDone: if (infoPtr != NULL) { + TkTextFreeElideInfo(infoPtr); ckfree((char*) infoPtr); } } @@ -1662,6 +1663,7 @@ TkTextIndexCount(textPtr, indexPtr1, indexPtr2, type) } countDone: if (infoPtr != NULL) { + TkTextFreeElideInfo(infoPtr); ckfree((char*) infoPtr); } return count; @@ -1920,6 +1922,7 @@ TkTextIndexBackChars(textPtr, srcPtr, charCount, dstPtr, type) } backwadCharDone: if (infoPtr != NULL) { + TkTextFreeElideInfo(infoPtr); ckfree((char*) infoPtr); } } -- cgit v0.12