summaryrefslogtreecommitdiffstats
path: root/generic/tkTextDisp.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tkTextDisp.c')
-rw-r--r--generic/tkTextDisp.c586
1 files changed, 407 insertions, 179 deletions
diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c
index 548e47e..3edd5dc 100644
--- a/generic/tkTextDisp.c
+++ b/generic/tkTextDisp.c
@@ -243,7 +243,8 @@ typedef struct DLine {
* top to bottom. Note: the next DLine doesn't
* always correspond to the next line of text:
* (a) can have multiple DLines for one text
- * line, and (b) can have gaps where DLine's
+ * line (wrapping), (b) can have elided newlines,
+ * and (c) can have gaps where DLine's
* have been deleted because they're out of
* date. */
int flags; /* Various flag bits: see below for values. */
@@ -542,7 +543,8 @@ static void DisplayDLine(TkText *textPtr, DLine *dlPtr,
static void DisplayLineBackground(TkText *textPtr, DLine *dlPtr,
DLine *prevPtr, Pixmap pixmap);
static void DisplayText(ClientData clientData);
-static DLine * FindDLine(DLine *dlPtr, CONST TkTextIndex *indexPtr);
+static DLine * FindDLine(TkText *textPtr, DLine *dlPtr,
+ CONST TkTextIndex *indexPtr);
static void FreeDLines(TkText *textPtr, DLine *firstPtr,
DLine *lastPtr, int action);
static void FreeStyle(TkText *textPtr, TextStyle *stylePtr);
@@ -589,6 +591,8 @@ static int TextGetScrollInfoObj(Tcl_Interp *interp,
int *intPtr);
static void AsyncUpdateLineMetrics(ClientData clientData);
static void AsyncUpdateYScrollbar(ClientData clientData);
+static int IsStartOfNotMergedLine(TkText *textPtr,
+ CONST TkTextIndex *indexPtr);
/*
* Result values returned by TextGetScrollInfoObj:
@@ -1758,7 +1762,7 @@ UpdateDisplayInfo(
*/
index = textPtr->topIndex;
- dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
+ dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &index);
if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, DLINE_UNLINK);
}
@@ -3381,7 +3385,7 @@ TkTextFindDisplayLineEnd(
* of the original index within its display
* line. */
{
- if (!end && indexPtr->byteIndex == 0) {
+ if (!end && IsStartOfNotMergedLine(textPtr, indexPtr)) {
/*
* Nothing to do.
*/
@@ -3460,8 +3464,9 @@ TkTextFindDisplayLineEnd(
* this now.
*/
- *xOffset = DlineXOfIndex(textPtr, dlPtr,
- indexPtr->byteIndex - dlPtr->index.byteIndex);
+ *xOffset = DlineXOfIndex(textPtr, dlPtr,
+ TkTextIndexCountBytes(textPtr, &dlPtr->index,
+ indexPtr));
}
if (end) {
/*
@@ -3531,6 +3536,27 @@ CalculateDisplayLineHeight(
DLine *dlPtr;
int pixelHeight;
+ if (tkTextDebug) {
+ int oldtkTextDebug = tkTextDebug;
+ /*
+ * Check that the indexPtr we are given really is at the start of a
+ * display line. The gymnastics with tkTextDebug is to prevent
+ * failure of a test suite test, that checks that lines are rendered
+ * exactly once. TkTextFindDisplayLineEnd is used here for checking
+ * indexPtr but it calls LayoutDLine/FreeDLine which makes the
+ * counting wrong. The debug mode shall therefore be switched off
+ * when calling TkTextFindDisplayLineEnd.
+ */
+
+ TkTextIndex indexPtr2 = *indexPtr;
+ tkTextDebug = 0;
+ TkTextFindDisplayLineEnd(textPtr, &indexPtr2, 0, NULL);
+ tkTextDebug = oldtkTextDebug;
+ if (TkTextIndexCmp(&indexPtr2,indexPtr) != 0) {
+ Tcl_Panic("CalculateDisplayLineHeight called with bad indexPtr");
+ }
+ }
+
/*
* Special case for artificial last line. May be better to move this
* inside LayoutDLine.
@@ -3597,26 +3623,44 @@ TkTextIndexYPixels(
{
int pixelHeight;
TkTextIndex index;
+ int alreadyStartOfLine = 1;
- pixelHeight = TkBTreePixelsTo(textPtr, indexPtr->linePtr);
+ /*
+ * Find the index denoting the closest position being at the same time
+ * the start of a logical line above indexPtr and the start of a display
+ * line.
+ */
+
+ index = *indexPtr;
+ while (1) {
+ TkTextFindDisplayLineEnd(textPtr, &index, 0, NULL);
+ if (index.byteIndex == 0) {
+ break;
+ }
+ TkTextIndexBackBytes(textPtr, &index, 1, &index);
+ alreadyStartOfLine = 0;
+ }
+
+ pixelHeight = TkBTreePixelsTo(textPtr, index.linePtr);
/*
- * Iterate through all display-lines corresponding to the single logical
- * line belonging to indexPtr, adding up the pixel height of each such
- * display line as we go along, until we go past 'indexPtr'.
+ * Shortcut to avoid layout of a superfluous display line. We know there
+ * is nothing more to add up to the height if the index we were given was
+ * already on the first display line of a logical line.
*/
- if (indexPtr->byteIndex == 0) {
- return pixelHeight;
+ if (alreadyStartOfLine) {
+ return pixelHeight;
}
- index.tree = textPtr->sharedTextPtr->tree;
- index.linePtr = indexPtr->linePtr;
- index.byteIndex = 0;
- index.textPtr = NULL;
+ /*
+ * Iterate through display lines, starting at the logical line belonging
+ * to index, adding up the pixel height of each such display line as we
+ * go along, until we go past 'indexPtr'.
+ */
while (1) {
- int bytes, height;
+ int bytes, height, compare;
/*
* Currently this call doesn't have many side-effects. However, if in
@@ -3628,9 +3672,10 @@ TkTextIndexYPixels(
height = CalculateDisplayLineHeight(textPtr, &index, &bytes, NULL);
- index.byteIndex += bytes;
+ TkTextIndexForwBytes(textPtr, &index, bytes, &index);
- if (index.byteIndex > indexPtr->byteIndex) {
+ compare = TkTextIndexCmp(&index,indexPtr);
+ if (compare > 0) {
return pixelHeight;
}
@@ -3638,7 +3683,7 @@ TkTextIndexYPixels(
pixelHeight += height;
}
- if (index.byteIndex == indexPtr->byteIndex) {
+ if (compare == 0) {
return pixelHeight;
}
}
@@ -3702,10 +3747,26 @@ TkTextUpdateOneLine(
}
/*
+ * CalculateDisplayLineHeight _must_ be called (below) with an index at
+ * the beginning of a display line. Force this to happen. This is needed
+ * when TkTextUpdateOneLine is called with a line that is merged with its
+ * previous line: the number of merged logical lines in a display line is
+ * calculated correctly only when CalculateDisplayLineHeight receives
+ * an index at the beginning of a display line. In turn this causes the
+ * merged lines to receive their correct zero pixel height in
+ * TkBTreeAdjustPixelHeight.
+ */
+
+ TkTextFindDisplayLineEnd(textPtr, indexPtr, 0, NULL);
+ linePtr = indexPtr->linePtr;
+
+ /*
* Iterate through all display-lines corresponding to the single logical
- * line 'linePtr', adding up the pixel height of each such display line as
- * we go along. The final total is, therefore, the height of the logical
- * line.
+ * line 'linePtr' (and lines merged into this line due to eol elision),
+ * adding up the pixel height of each such display line as we go along.
+ * The final total is, therefore, the total height of all display lines
+ * made up by the logical line 'linePtr' and subsequent logical lines
+ * merged into this line.
*/
displayLines = 0;
@@ -3722,7 +3783,7 @@ TkTextUpdateOneLine(
* test below this while loop.
*/
- height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes,
+ height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes,
&logicalLines);
if (height > 0) {
@@ -3736,44 +3797,31 @@ TkTextUpdateOneLine(
break;
}
- if (logicalLines == 0) {
- if (indexPtr->linePtr != linePtr) {
- /*
- * If we reached the end of the logical line, then either way
- * we don't have a partial calculation.
- */
+ if (mergedLines == 0) {
+ if (indexPtr->linePtr != linePtr) {
+ /*
+ * If we reached the end of the logical line, then either way
+ * we don't have a partial calculation.
+ */
- partialCalc = 0;
- break;
- }
- } else if (indexPtr->byteIndex != 0) {
- /*
- * We must still be on the same wrapped line.
- */
- } else {
- /*
- * Must check if indexPtr is really a new logical line which is
- * not merged with the previous line. The only code that would
- * really know this is LayoutDLine, which doesn't pass the
- * information on, so we have to check manually here.
- */
-
- TkTextIndex idx;
-
- TkTextIndexBackChars(textPtr, indexPtr, 1, &idx, COUNT_INDICES);
- if (!TkTextIsElided(textPtr, &idx, NULL)) {
- /*
- * We've ended a logical line.
- */
-
- partialCalc = 0;
- break;
- }
+ partialCalc = 0;
+ break;
+ }
+ } else {
+ if (IsStartOfNotMergedLine(textPtr, indexPtr)) {
+ /*
+ * We've ended a logical line.
+ */
+
+ partialCalc = 0;
+ break;
+ }
- /*
- * We must still be on the same wrapped line.
- */
- }
+ /*
+ * We must still be on the same wrapped line, on a new logical
+ * line merged with the logical line 'linePtr'.
+ */
+ }
if (partialCalc && displayLines > 50 && mergedLines == 0) {
/*
* Only calculate 50 display lines at a time, to avoid huge
@@ -3890,8 +3938,17 @@ RedisplayText(
{
register TkText *textPtr = (TkText *) clientData;
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- TkRegion damageRegion = TkCreateRegion();
- XRectangle rectangle = {0, 0, dInfoPtr->maxX, dInfoPtr->maxY};
+ TkRegion damageRegion;
+ XRectangle rectangle;
+
+ if (dInfoPtr == NULL) {
+ return;
+ }
+ damageRegion = TkCreateRegion();
+ rectangle.x = 0;
+ rectangle.y = 0;
+ rectangle.width = dInfoPtr->maxX;
+ rectangle.height = dInfoPtr->maxY;
TkUnionRectWithRegion(&rectangle, damageRegion, damageRegion);
TextInvalidateRegion(textPtr, damageRegion);
@@ -4571,6 +4628,8 @@ TextChanged(
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
DLine *firstPtr, *lastPtr;
TkTextIndex rounded;
+ TkTextLine *linePtr;
+ int notBegin;
/*
* Schedule both a redisplay and a recomputation of display information.
@@ -4596,23 +4655,78 @@ TextChanged(
/*
* Find the DLines corresponding to index1Ptr and index2Ptr. There is one
* tricky thing here, which is that we have to relayout in units of whole
- * text lines: round index1Ptr back to the beginning of its text line, and
- * include all the display lines after index2, up to the end of its text
- * line. This is necessary because the indices stored in the display lines
- * will no longer be valid. It's also needed because any edit could change
- * the way lines wrap.
+ * text lines: This is necessary because the indices stored in the display
+ * lines will no longer be valid. It's also needed because any edit could
+ * change the way lines wrap.
+ * To relayout in units of whole text (logical) lines, round index1Ptr
+ * back to the beginning of its text line (or, if this line start is
+ * elided, to the beginning of the text line that starts the display line
+ * it is included in), and include all the display lines after index2Ptr,
+ * up to the end of its text line (or, if this line end is elided, up to
+ * the end of the first non elided text line after this line end).
*/
rounded = *index1Ptr;
rounded.byteIndex = 0;
- firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
+ notBegin = 0;
+ while (!IsStartOfNotMergedLine(textPtr, &rounded) && notBegin) {
+ notBegin = !TkTextIndexBackBytes(textPtr, &rounded, 1, &rounded);
+ rounded.byteIndex = 0;
+ }
+
+ /*
+ * 'rounded' now points to the start of a display line as well as the
+ * real (non elided) start of a logical line, and this index is the
+ * closest before index1Ptr.
+ */
+
+ firstPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &rounded);
+
if (firstPtr == NULL) {
+ /*
+ * index1Ptr pertains to no display line, i.e this index is after
+ * the last display line. Since index2Ptr is after index1Ptr, there
+ * is no display line to free/redisplay and we can return early.
+ */
+
return;
}
- lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
- while ((lastPtr != NULL)
- && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
- lastPtr = lastPtr->nextPtr;
+
+ rounded = *index2Ptr;
+ linePtr = index2Ptr->linePtr;
+ do {
+ linePtr = TkBTreeNextLine(textPtr, linePtr);
+ if (linePtr == NULL) {
+ break;
+ }
+ rounded.linePtr = linePtr;
+ rounded.byteIndex = 0;
+ } while (!IsStartOfNotMergedLine(textPtr, &rounded));
+
+ if (linePtr == NULL) {
+ lastPtr = NULL;
+ } else {
+ /*
+ * 'rounded' now points to the start of a display line as well as the
+ * start of a logical line not merged with its previous line, and
+ * this index is the closest after index2Ptr.
+ */
+
+ lastPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &rounded);
+
+ /*
+ * At least one display line is supposed to change. This makes the
+ * redisplay OK in case the display line we expect to get here was
+ * unlinked by a previous call to TkTextChanged and the text widget
+ * did not update before reaching this point. This happens for
+ * instance when moving the cursor up one line.
+ * Note that lastPtr != NULL here, otherwise we would have returned
+ * earlier when we tested for firstPtr being NULL.
+ */
+
+ if (lastPtr == firstPtr) {
+ lastPtr = lastPtr->nextPtr;
+ }
}
/*
@@ -4792,14 +4906,13 @@ TextRedrawTag(
* the line containing the previous character.
*/
- if (curIndexPtr->byteIndex == 0) {
- dlPtr = FindDLine(dlPtr, curIndexPtr);
+ if (IsStartOfNotMergedLine(textPtr, curIndexPtr)) {
+ dlPtr = FindDLine(textPtr, dlPtr, curIndexPtr);
} else {
- TkTextIndex tmp;
+ TkTextIndex tmp = *curIndexPtr;
- tmp = *curIndexPtr;
- tmp.byteIndex -= 1;
- dlPtr = FindDLine(dlPtr, &tmp);
+ TkTextIndexBackBytes(textPtr, &tmp, 1, &tmp);
+ dlPtr = FindDLine(textPtr, dlPtr, &tmp);
}
if (dlPtr == NULL) {
break;
@@ -4815,9 +4928,9 @@ TextRedrawTag(
curIndexPtr = &search.curIndex;
endIndexPtr = curIndexPtr;
}
- endPtr = FindDLine(dlPtr, endIndexPtr);
- if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
- && (endPtr->index.byteIndex < endIndexPtr->byteIndex)) {
+ endPtr = FindDLine(textPtr, dlPtr, endIndexPtr);
+ if ((endPtr != NULL)
+ && (TkTextIndexCmp(&endPtr->index,endIndexPtr) < 0)) {
endPtr = endPtr->nextPtr;
}
@@ -4935,7 +5048,7 @@ TkTextRelayoutWindow(
* could change the way lines wrap.
*/
- if (textPtr->topIndex.byteIndex != 0) {
+ if (!IsStartOfNotMergedLine(textPtr, &textPtr->topIndex)) {
TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
}
@@ -5048,9 +5161,9 @@ TkTextSetYView(
*/
textPtr->topIndex = *indexPtr;
- if (indexPtr->byteIndex != 0) {
- TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
- }
+ if (!IsStartOfNotMergedLine(textPtr, indexPtr)) {
+ TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
+ }
dInfoPtr->newTopPixelOffset = pickPlace;
goto scheduleUpdate;
}
@@ -5064,7 +5177,7 @@ TkTextSetYView(
if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
UpdateDisplayInfo(textPtr);
}
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr);
if (dlPtr != NULL) {
if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
/*
@@ -5073,19 +5186,20 @@ TkTextSetYView(
*/
dlPtr = NULL;
- } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
- && (dlPtr->index.byteIndex <= indexPtr->byteIndex)) {
- if (dInfoPtr->dLinePtr == dlPtr && dInfoPtr->topPixelOffset != 0) {
- /*
- * It is on the top line, but that line is hanging off the top
- * of the screen. Change the top overlap to zero and update.
- */
-
- dInfoPtr->newTopPixelOffset = 0;
- goto scheduleUpdate;
- }
- return;
- }
+ } else {
+ if (TkTextIndexCmp(&dlPtr->index, indexPtr) <= 0) {
+ if (dInfoPtr->dLinePtr == dlPtr && dInfoPtr->topPixelOffset != 0) {
+ /*
+ * It is on the top line, but that line is hanging off the top
+ * of the screen. Change the top overlap to zero and update.
+ */
+
+ dInfoPtr->newTopPixelOffset = 0;
+ goto scheduleUpdate;
+ }
+ return;
+ }
+ }
}
/*
@@ -5096,7 +5210,9 @@ TkTextSetYView(
* If the line is not close, place it in the center of the window.
*/
- lineHeight = CalculateDisplayLineHeight(textPtr, indexPtr, NULL, NULL);
+ tmpIndex = *indexPtr;
+ TkTextFindDisplayLineEnd(textPtr, &tmpIndex, 0, NULL);
+ lineHeight = CalculateDisplayLineHeight(textPtr, &tmpIndex, NULL, NULL);
/*
* It would be better if 'bottomY' were calculated using the actual height
@@ -5137,7 +5253,7 @@ TkTextSetYView(
MeasureUp(textPtr, indexPtr, close + lineHeight
- textPtr->charHeight/2, &tmpIndex, &overlap);
- if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
+ if (FindDLine(textPtr, dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
bottomY = dInfoPtr->maxY - dInfoPtr->y;
}
}
@@ -5275,6 +5391,8 @@ MeasureUp(
index.linePtr = TkBTreeFindLine(srcPtr->tree, textPtr, lineNum);
index.byteIndex = 0;
+ TkTextFindDisplayLineEnd(textPtr, &index, 0, NULL);
+ lineNum = TkBTreeLinesTo(textPtr, index.linePtr);
lowestPtr = NULL;
do {
dlPtr = LayoutDLine(textPtr, &index);
@@ -5295,8 +5413,21 @@ MeasureUp(
for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
distance -= dlPtr->height;
if (distance <= 0) {
- *dstPtr = dlPtr->index;
- if (overlap != NULL) {
+ *dstPtr = dlPtr->index;
+
+ /*
+ * dstPtr is the start of a display line that is or is not
+ * the start of a logical line. If it is the start of a
+ * logical line, we must check whether this line is merged
+ * with the previous logical line, and if so we must adjust
+ * dstPtr to the start of the display line since a display
+ * line start needs to be returned.
+ */
+ if (!IsStartOfNotMergedLine(textPtr, dstPtr)) {
+ TkTextFindDisplayLineEnd(textPtr, dstPtr, 0, NULL);
+ }
+
+ if (overlap != NULL) {
*overlap = -distance;
}
break;
@@ -5396,16 +5527,24 @@ TkTextSeeCmd(
}
/*
- * Find the chunk that contains the desired index. dlPtr may be NULL if
- * the widget is not mapped. [Bug #641778]
+ * Find the display line containing the desired index. dlPtr may be NULL
+ * if the widget is not mapped. [Bug #641778]
*/
- dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
+ dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, &index);
if (dlPtr == NULL) {
return TCL_OK;
}
- byteCount = index.byteIndex - dlPtr->index.byteIndex;
+ /*
+ * Find the chunk within the display line that contains the desired
+ * index. The chunks making the display line are skipped up to but not
+ * including the one crossing index. Skipping is done based on a
+ * byteCount offset possibly spanning several logical lines in case
+ * they are elided.
+ */
+
+ byteCount = TkTextIndexCountBytes(textPtr, &dlPtr->index, &index);
for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL ;
chunkPtr = chunkPtr->nextPtr) {
if (byteCount < chunkPtr->numBytes) {
@@ -5421,31 +5560,31 @@ TkTextSeeCmd(
*/
if (chunkPtr != NULL) {
- (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteCount,
- dlPtr->y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
- &height);
- delta = x - dInfoPtr->curXPixelOffset;
- oneThird = lineWidth/3;
- if (delta < 0) {
- if (delta < -oneThird) {
- dInfoPtr->newXPixelOffset = (x - lineWidth/2);
- } else {
- dInfoPtr->newXPixelOffset -= ((-delta) );
- }
- } else {
- delta -= (lineWidth - width);
- if (delta > 0) {
- if (delta > oneThird) {
- dInfoPtr->newXPixelOffset = (x - lineWidth/2);
- } else {
- dInfoPtr->newXPixelOffset += (delta );
- }
- } else {
- return TCL_OK;
- }
- }
+ (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteCount,
+ dlPtr->y + dlPtr->spaceAbove,
+ dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
+ dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
+ &height);
+ delta = x - dInfoPtr->curXPixelOffset;
+ oneThird = lineWidth/3;
+ if (delta < 0) {
+ if (delta < -oneThird) {
+ dInfoPtr->newXPixelOffset = (x - lineWidth/2);
+ } else {
+ dInfoPtr->newXPixelOffset -= ((-delta) );
+ }
+ } else {
+ delta -= (lineWidth - width);
+ if (delta > 0) {
+ if (delta > oneThird) {
+ dInfoPtr->newXPixelOffset = (x - lineWidth/2);
+ } else {
+ dInfoPtr->newXPixelOffset += (delta );
+ }
+ } else {
+ return TCL_OK;
+ }
+ }
}
dInfoPtr->flags |= DINFO_OUT_OF_DATE;
if (!(dInfoPtr->flags & REDRAW_PENDING)) {
@@ -5682,7 +5821,25 @@ YScrollByLines(
offset++;
if (offset == 0) {
textPtr->topIndex = dlPtr->index;
- break;
+
+ /*
+ * topIndex is the start of a logical line. However, if
+ * the eol of the previous logical line is elided, then
+ * topIndex may be elsewhere than the first character of
+ * a display line, which is unwanted. Adjust to the start
+ * of the display line, if needed.
+ * topIndex is the start of a display line that is or is
+ * not the start of a logical line. If it is the start of
+ * a logical line, we must check whether this line is
+ * merged with the previous logical line, and if so we
+ * must adjust topIndex to the start of the display line.
+ */
+ if (!IsStartOfNotMergedLine(textPtr, &textPtr->topIndex)) {
+ TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex,
+ 0, NULL);
+ }
+
+ break;
}
}
@@ -6160,13 +6317,10 @@ GetYPixelCount(
/*
* For the common case where this dlPtr is also the start of the 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).
+ * line, we can return right away.
*/
- if (dlPtr->index.byteIndex == 0) {
+ if (IsStartOfNotMergedLine(textPtr, &dlPtr->index)) {
return count;
}
@@ -6448,11 +6602,12 @@ AsyncUpdateYScrollbar(
static DLine *
FindDLine(
+ TkText *textPtr, /* Widget record for text widget. */
register DLine *dlPtr, /* Pointer to first in list of DLines to
* search. */
CONST TkTextIndex *indexPtr)/* Index of desired character. */
{
- TkTextLine *linePtr;
+ DLine *dlPtrPrev;
if (dlPtr == NULL) {
return NULL;
@@ -6467,43 +6622,89 @@ FindDLine(
}
/*
- * Find the first display line that covers the desired text line.
+ * The display line containing the desired index is such that the index
+ * of the first character of this display line is at or before the
+ * desired index, and the index of the first character of the next
+ * display line is after the desired index.
*/
- linePtr = dlPtr->index.linePtr;
- while (linePtr != indexPtr->linePtr) {
- while (dlPtr->index.linePtr == linePtr) {
- dlPtr = dlPtr->nextPtr;
- if (dlPtr == NULL) {
- return NULL;
- }
- }
+ while (TkTextIndexCmp(&dlPtr->index,indexPtr) < 0) {
+ dlPtrPrev = dlPtr;
+ dlPtr = dlPtr->nextPtr;
+ if (dlPtr == NULL) {
+ TkTextIndex indexPtr2;
+ /*
+ * We're past the last display line, either because the desired
+ * index lies past the visible text, or because the desired index
+ * is on the last display line showing the last logical line.
+ */
+ indexPtr2 = dlPtrPrev->index;
+ TkTextIndexForwBytes(textPtr, &indexPtr2, dlPtrPrev->byteCount,
+ &indexPtr2);
+ if (TkTextIndexCmp(&indexPtr2,indexPtr) > 0) {
+ dlPtr = dlPtrPrev;
+ break;
+ } else {
+ return NULL;
+ }
+ }
+ if (TkTextIndexCmp(&dlPtr->index,indexPtr) > 0) {
+ dlPtr = dlPtrPrev;
+ break;
+ }
+ }
- /*
- * VMD: some concern here as to whether this logic, or the caller's
- * logic will work well with partial peer widgets.
- */
+ return dlPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IsStartOfNotMergedLine --
+ *
+ * This function checks whether the given index is the start of a
+ * logical line that is not merged with the previous logical line
+ * (due to elision of the eol of the previous line).
+ *
+ * Results:
+ * Returns whether the given index denotes the first index of a
+* logical line not merged with its previous line.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
- linePtr = TkBTreeNextLine(NULL, linePtr);
- if (linePtr == NULL) {
- Tcl_Panic("FindDLine reached end of text");
- }
- }
- if (indexPtr->linePtr != dlPtr->index.linePtr) {
- return dlPtr;
+static int
+IsStartOfNotMergedLine(
+ TkText *textPtr, /* Widget record for text widget. */
+ CONST TkTextIndex *indexPtr) /* Index to check. */
+{
+ TkTextIndex indexPtr2;
+
+ if (indexPtr->byteIndex != 0) {
+ /*
+ * Not the start of a logical line.
+ */
+ return 0;
}
- /*
- * Now get to the right position within the text line.
- */
+ if (TkTextIndexBackBytes(textPtr, indexPtr, 1, &indexPtr2)) {
+ /*
+ * indexPtr is the first index of the text widget.
+ */
+ return 1;
+ }
- while (indexPtr->byteIndex >= (dlPtr->index.byteIndex+dlPtr->byteCount)) {
- dlPtr = dlPtr->nextPtr;
- if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
- break;
- }
+ if (!TkTextIsElided(textPtr, &indexPtr2, NULL)) {
+ /*
+ * The eol of the line just before indexPtr is elided.
+ */
+ return 1;
}
- return dlPtr;
+
+ return 0;
}
/*
@@ -6673,10 +6874,15 @@ DlineIndexOfX(
* We've reached the end of the text.
*/
+ TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES);
return;
}
if (chunkPtr->nextPtr == NULL) {
- TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES);
+ /*
+ * We've reached the end of the display line.
+ */
+
+ TkTextIndexBackChars(NULL, indexPtr, 1, indexPtr, COUNT_INDICES);
return;
}
chunkPtr = chunkPtr->nextPtr;
@@ -6762,7 +6968,7 @@ DlineXOfIndex(
* coordinate. */
{
register TkTextDispChunk *chunkPtr = dlPtr->chunkPtr;
- int x;
+ int x = 0;
if (byteIndex == 0 || chunkPtr == NULL) {
return 0;
@@ -6833,7 +7039,7 @@ TkTextIndexBbox(
TextDInfo *dInfoPtr = textPtr->dInfoPtr;
DLine *dlPtr;
register TkTextDispChunk *chunkPtr;
- int byteIndex;
+ int byteCount;
/*
* Make sure that all of the screen layout information is up to date.
@@ -6847,24 +7053,37 @@ TkTextIndexBbox(
* Find the display line containing the desired index.
*/
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr);
+
+ /*
+ * Two cases shall be trapped here because the logic later really
+ * needs dlPtr to be the display line containing indexPtr:
+ * 1. if no display line contains the desired index (NULL dlPtr)
+ * 2. if indexPtr is before the first display line, in which case
+ * dlPtr currently points to the first display line
+ */
+
if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
return -1;
}
/*
- * Find the chunk within the line that contains the desired index.
+ * Find the chunk within the display line that contains the desired
+ * index. The chunks making the display line are skipped up to but not
+ * including the one crossing indexPtr. Skipping is done based on
+ * a byteCount offset possibly spanning several logical lines in case
+ * they are elided.
*/
- byteIndex = indexPtr->byteIndex - dlPtr->index.byteIndex;
+ byteCount = TkTextIndexCountBytes(textPtr, &dlPtr->index, indexPtr);
for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
if (chunkPtr == NULL) {
return -1;
}
- if (byteIndex < chunkPtr->numBytes) {
+ if (byteCount < chunkPtr->numBytes) {
break;
}
- byteIndex -= chunkPtr->numBytes;
+ byteCount -= chunkPtr->numBytes;
}
/*
@@ -6874,13 +7093,13 @@ TkTextIndexBbox(
* coordinate on the screen. Translate it to reflect horizontal scrolling.
*/
- (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteIndex,
+ (*chunkPtr->bboxProc)(textPtr, chunkPtr, byteCount,
dlPtr->y + dlPtr->spaceAbove,
dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
heightPtr);
*xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curXPixelOffset;
- if ((byteIndex == chunkPtr->numBytes-1) && (chunkPtr->nextPtr == NULL)) {
+ if ((byteCount == chunkPtr->numBytes-1) && (chunkPtr->nextPtr == NULL)) {
/*
* Last character in display line. Give it all the space up to the
* line.
@@ -6978,7 +7197,16 @@ TkTextDLineInfo(
* Find the display line containing the desired index.
*/
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
+ dlPtr = FindDLine(textPtr, dInfoPtr->dLinePtr, indexPtr);
+
+ /*
+ * Two cases shall be trapped here because the logic later really
+ * needs dlPtr to be the display line containing indexPtr:
+ * 1. if no display line contains the desired index (NULL dlPtr)
+ * 2. if indexPtr is before the first display line, in which case
+ * dlPtr currently points to the first display line
+ */
+
if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
return -1;
}