summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tkText.c53
-rw-r--r--generic/tkText.h3
-rw-r--r--generic/tkTextBTree.c3
-rw-r--r--generic/tkTextDisp.c567
-rw-r--r--generic/tkTextIndex.c87
-rw-r--r--tests/text.test60
-rw-r--r--tests/textDisp.test322
7 files changed, 882 insertions, 213 deletions
diff --git a/generic/tkText.c b/generic/tkText.c
index 386dac5..0d06233 100644
--- a/generic/tkText.c
+++ b/generic/tkText.c
@@ -881,7 +881,7 @@ TextWidgetObjCmd(
} else if (c == 'd' && (length > 8)
&& !strncmp("-displaylines", option, (unsigned) length)) {
TkTextLine *fromPtr, *lastPtr;
- TkTextIndex index;
+ TkTextIndex index, index2;
int compare = TkTextIndexCmp(indexFromPtr, indexToPtr);
value = 0;
@@ -916,35 +916,44 @@ TextWidgetObjCmd(
/*
* We're going to count up all display lines in the logical
* line of 'indexFromPtr' up to, but not including the logical
- * line of 'indexToPtr', and then subtract off what we didn't
- * want from 'from' and add on what we didn't count from 'to.
+ * line of 'indexToPtr' (except if this line is elided), and
+ * then subtract off what came in too much from elided lines,
+ * also subtract off what we didn't want from 'from' and add
+ * on what we didn't count from 'to'.
*/
- while (index.linePtr != indexToPtr->linePtr) {
- value += TkTextUpdateOneLine(textPtr, fromPtr,0,&index,0);
-
- /*
- * We might have skipped past indexToPtr, if we have
- * multiple logical lines in a single display line.
- */
- if (TkTextIndexCmp(&index,indexToPtr) > 0) {
- break;
- }
+ while (TkTextIndexCmp(&index,indexToPtr) < 0) {
+ value += TkTextUpdateOneLine(textPtr, index.linePtr,
+ 0, &index, 0);
}
- /*
- * Now we need to adjust the count to add on the number of
- * display lines in the last logical line, and subtract off
- * the number of display lines overcounted in the first
- * logical line. This logic is still ok if both indices are in
- * the same logical line.
- */
+ index2 = index;
+
+ /*
+ * Now we need to adjust the count to:
+ * - subtract off the number of display lines between
+ * indexToPtr and index2, since we might have skipped past
+ * indexToPtr, if we have several logical lines in a
+ * single display line
+ * - subtract off the number of display lines overcounted
+ * in the first logical line
+ * - add on the number of display lines in the last logical
+ * line
+ * This logic is still ok if both indexFromPtr and indexToPtr
+ * are in the same logical line.
+ */
+ index = *indexToPtr;
+ index.byteIndex = 0;
+ while (TkTextIndexCmp(&index,&index2) < 0) {
+ value -= TkTextUpdateOneLine(textPtr, index.linePtr,
+ 0, &index, 0);
+ }
index.linePtr = indexFromPtr->linePtr;
index.byteIndex = 0;
while (1) {
TkTextFindDisplayLineEnd(textPtr, &index, 1, NULL);
- if (index.byteIndex >= indexFromPtr->byteIndex) {
+ if (TkTextIndexCmp(&index,indexFromPtr) >= 0) {
break;
}
TkTextIndexForwBytes(textPtr, &index, 1, &index);
@@ -956,7 +965,7 @@ TextWidgetObjCmd(
index.byteIndex = 0;
while (1) {
TkTextFindDisplayLineEnd(textPtr, &index, 1, NULL);
- if (index.byteIndex >= indexToPtr->byteIndex) {
+ if (TkTextIndexCmp(&index,indexToPtr) >= 0) {
break;
}
TkTextIndexForwBytes(textPtr, &index, 1, &index);
diff --git a/generic/tkText.h b/generic/tkText.h
index fb9eab2..99a4888 100644
--- a/generic/tkText.h
+++ b/generic/tkText.h
@@ -1066,6 +1066,9 @@ MODULE_SCOPE void TkTextIndexBackChars(const TkText *textPtr,
TkTextIndex *dstPtr, TkTextCountType type);
MODULE_SCOPE int TkTextIndexCmp(const TkTextIndex *index1Ptr,
const TkTextIndex *index2Ptr);
+MODULE_SCOPE int TkTextIndexCountBytes(const TkText *textPtr,
+ const TkTextIndex *index1Ptr,
+ const TkTextIndex *index2Ptr);
MODULE_SCOPE int TkTextIndexCount(const TkText *textPtr,
const TkTextIndex *index1Ptr,
const TkTextIndex *index2Ptr,
diff --git a/generic/tkTextBTree.c b/generic/tkTextBTree.c
index a06d7e9..eaab652 100644
--- a/generic/tkTextBTree.c
+++ b/generic/tkTextBTree.c
@@ -1879,8 +1879,7 @@ TkBTreePreviousLine(
* number of pixels in the widget.
*
* Results:
- * The result is the index of linePtr within the tree, where 0
- * corresponds to the first line in the tree.
+ * The result is the pixel height of the top of the given line.
*
* Side effects:
* None.
diff --git a/generic/tkTextDisp.c b/generic/tkTextDisp.c
index b75a152..3ab9fde 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. */
@@ -543,7 +544,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);
@@ -590,6 +592,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:
@@ -1759,7 +1763,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);
}
@@ -3385,7 +3389,7 @@ TkTextFindDisplayLineEnd(
{
TkTextIndex index;
- if (!end && indexPtr->byteIndex == 0) {
+ if (!end && IsStartOfNotMergedLine(textPtr, indexPtr)) {
/*
* Nothing to do.
*/
@@ -3463,7 +3467,8 @@ TkTextFindDisplayLineEnd(
*/
*xOffset = DlineXOfIndex(textPtr, dlPtr,
- indexPtr->byteIndex - dlPtr->index.byteIndex);
+ TkTextIndexCountBytes(textPtr, &dlPtr->index,
+ indexPtr));
}
if (end) {
/*
@@ -3533,6 +3538,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.
@@ -3599,26 +3625,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
@@ -3630,9 +3674,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;
}
@@ -3640,7 +3685,7 @@ TkTextIndexYPixels(
pixelHeight += height;
}
- if (index.byteIndex == indexPtr->byteIndex) {
+ if (compare == 0) {
return pixelHeight;
}
}
@@ -3704,10 +3749,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;
@@ -3724,7 +3785,7 @@ TkTextUpdateOneLine(
* test below this while loop.
*/
- height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes,
+ height = CalculateDisplayLineHeight(textPtr, indexPtr, &bytes,
&logicalLines);
if (height > 0) {
@@ -3738,44 +3799,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.
- */
-
- 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;
+ 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.
+ */
- 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
@@ -4582,6 +4630,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.
@@ -4607,23 +4657,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;
+ }
}
/*
@@ -4803,14 +4908,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;
@@ -4826,9 +4930,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;
}
@@ -4946,7 +5050,7 @@ TkTextRelayoutWindow(
* could change the way lines wrap.
*/
- if (textPtr->topIndex.byteIndex != 0) {
+ if (!IsStartOfNotMergedLine(textPtr, &textPtr->topIndex)) {
TkTextFindDisplayLineEnd(textPtr, &textPtr->topIndex, 0, NULL);
}
@@ -5059,9 +5163,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;
}
@@ -5075,7 +5179,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) {
/*
@@ -5084,19 +5188,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;
+ }
+ }
}
/*
@@ -5107,7 +5212,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
@@ -5148,7 +5255,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;
}
}
@@ -5286,6 +5393,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);
@@ -5306,8 +5415,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;
@@ -5407,16 +5529,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) {
@@ -5427,34 +5557,32 @@ TkTextSeeCmd(
/*
* Call a chunk-specific function to find the horizontal range of the
- * character within the chunk. chunkPtr is NULL if trying to see in elided
- * region.
+ * character within the chunk.
*/
- 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) {
- return TCL_OK;
- } else if (delta > oneThird) {
- dInfoPtr->newXPixelOffset = x - lineWidth/2;
- } else {
- dInfoPtr->newXPixelOffset += delta;
- }
+ 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) {
+ return TCL_OK;
}
+ if (delta > oneThird) {
+ dInfoPtr->newXPixelOffset = x - lineWidth/2;
+ } else {
+ dInfoPtr->newXPixelOffset += delta;
+ }
}
dInfoPtr->flags |= DINFO_OUT_OF_DATE;
if (!(dInfoPtr->flags & REDRAW_PENDING)) {
@@ -5691,7 +5819,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;
}
}
@@ -6178,13 +6324,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;
}
@@ -6471,11 +6614,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;
@@ -6490,43 +6634,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;
}
/*
@@ -6697,10 +6887,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;
@@ -6856,7 +7051,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.
@@ -6870,24 +7065,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;
}
/*
@@ -6897,13 +7105,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.
@@ -7001,7 +7209,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;
}
diff --git a/generic/tkTextIndex.c b/generic/tkTextIndex.c
index 9914ea6..70f74fb 100644
--- a/generic/tkTextIndex.c
+++ b/generic/tkTextIndex.c
@@ -40,6 +40,9 @@ static const char * StartEnd(TkText *textPtr, const char *string,
static int GetIndex(Tcl_Interp *interp, TkSharedText *sharedPtr,
TkText *textPtr, const char *string,
TkTextIndex *indexPtr, int *canCachePtr);
+static int IndexCountBytesOrdered(CONST TkText *textPtr,
+ CONST TkTextIndex *indexPtr1,
+ CONST TkTextIndex *indexPtr2);
/*
* The "textindex" Tcl_Obj definition:
@@ -1616,6 +1619,90 @@ TkTextIndexForwChars(
/*
*---------------------------------------------------------------------------
*
+ * TkTextIndexCountBytes --
+ *
+ * Given a pair of indices in a text widget, this function counts how
+ * many bytes are between the two indices. The two indices do not need
+ * to be ordered.
+ *
+ * Results:
+ * The number of bytes in the given range.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+int
+TkTextIndexCountBytes(
+ CONST TkText *textPtr,
+ CONST TkTextIndex *indexPtr1, /* Index describing one location. */
+ CONST TkTextIndex *indexPtr2) /* Index describing second location. */
+{
+ int compare = TkTextIndexCmp(indexPtr1, indexPtr2);
+
+ if (compare == 0) {
+ return 0;
+ } else if (compare > 0) {
+ return IndexCountBytesOrdered(textPtr, indexPtr2, indexPtr1);
+ } else {
+ return IndexCountBytesOrdered(textPtr, indexPtr1, indexPtr2);
+ }
+}
+
+static int
+IndexCountBytesOrdered(
+ CONST TkText *textPtr,
+ CONST TkTextIndex *indexPtr1,
+ /* Index describing location of character from
+ * which to count. */
+ CONST TkTextIndex *indexPtr2)
+ /* Index describing location of last character
+ * at which to stop the count. */
+{
+ int byteCount, offset;
+ TkTextSegment *segPtr, *segPtr1;
+ TkTextLine *linePtr;
+
+ if (indexPtr1->linePtr == indexPtr2->linePtr) {
+ return indexPtr2->byteIndex - indexPtr1->byteIndex;
+ }
+
+ /*
+ * indexPtr2 is on a line strictly after the line containing indexPtr1.
+ * Add up:
+ * bytes between indexPtr1 and end of its line
+ * bytes in lines strictly between indexPtr1 and indexPtr2
+ * bytes between start of the indexPtr2 line and indexPtr2
+ */
+
+ segPtr1 = TkTextIndexToSeg(indexPtr1, &offset);
+ byteCount = -offset;
+ for (segPtr = segPtr1; segPtr != NULL; segPtr = segPtr->nextPtr) {
+ byteCount += segPtr->size;
+ }
+
+ linePtr = indexPtr1->linePtr->nextPtr;
+ while (linePtr != indexPtr2->linePtr) {
+ for (segPtr = linePtr->segPtr; segPtr != NULL;
+ segPtr = segPtr->nextPtr) {
+ byteCount += segPtr->size;
+ }
+ linePtr = TkBTreeNextLine(textPtr, linePtr);
+ if (linePtr == NULL) {
+ Tcl_Panic("TextIndexCountBytesOrdered ran out of lines");
+ }
+ }
+
+ byteCount += indexPtr2->byteIndex;
+
+ return byteCount;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
* TkTextIndexCount --
*
* Given an ordered pair of indices in a text widget, this function
diff --git a/tests/text.test b/tests/text.test
index 5089bb1..d8d01a0 100644
--- a/tests/text.test
+++ b/tests/text.test
@@ -2632,7 +2632,65 @@ test text-10.39 {TextWidgetCmd procedure, "count" option} -setup {
lappend res [.t count -displaylines 1.19 3.24] [.t count -displaylines 1.0 end]
} -cleanup {
destroy .t
-} -result {2 6 2 5}
+} -result {2 6 1 5}
+test text-9.2.45 {TextWidgetCmd procedure, "count" option} -setup {
+ text .t
+ pack .t
+ update
+ set res {}
+} -body {
+ for {set i 1} {$i < 5} {incr i} {
+ .t insert end "Line $i+++Line $i---Line $i///Line $i - This is Line [format %c [expr 64+$i]]\n"
+ }
+ .t tag configure hidden -elide true
+ .t tag add hidden 2.15 3.10
+ .t configure -wrap none
+ set res [.t count -displaylines 2.0 3.0]
+} -cleanup {
+ destroy .t
+} -result {0}
+test text-9.2.46 {TextWidgetCmd procedure, "count" option} -setup {
+ toplevel .mytop
+ pack [text .mytop.t]
+ wm geometry .mytop 100x300+0+0
+ .mytop.t delete 1.0 end
+ update
+ set res {}
+} -body {
+ for {set i 1} {$i < 5} {incr i} {
+ # 0 1 2 3 4
+ # 012345 678901234 567890123 456789012 34567890123456789
+ .mytop.t insert end "Line $i+++Line $i---Line $i///Line $i - This is Line [format %c [expr 64+$i]]\n"
+ }
+ .mytop.t tag configure hidden -elide true
+ .mytop.t tag add hidden 2.15 3.10
+ .mytop.t configure -wrap char
+ lappend res [.mytop.t count -displaylines 2.0 3.0]
+ lappend res [.mytop.t count -displaylines 2.0 3.40]
+} -cleanup {
+ destroy .mytop
+} -result {1 3}
+test text-9.2.47 {TextWidgetCmd procedure, "count" option} -setup {
+ text .t
+ pack .t
+ update
+ set res {}
+} -body {
+ for {set i 1} {$i < 25} {incr i} {
+ .t insert end "Line $i\n"
+ }
+ .t tag configure hidden -elide true
+ .t tag add hidden 5.7 11.0
+ update
+ set y1 [lindex [.t yview] 1]
+ .t count -displaylines 5.0 11.0
+ set y2 [lindex [.t yview] 1]
+ .t count -displaylines 5.0 12.0
+ set y3 [lindex [.t yview] 1]
+ list [expr {$y1 == $y2}] [expr {$y1 == $y3}]
+} -cleanup {
+ destroy .t
+} -result {1 1}
test text-11.1 {counting with tag priority eliding} -setup {
diff --git a/tests/textDisp.test b/tests/textDisp.test
index 70c7208..12a20c6 100644
--- a/tests/textDisp.test
+++ b/tests/textDisp.test
@@ -537,20 +537,24 @@ test textDisp-4.1 {UpdateDisplayInfo, basic} {textfonts} {
.t insert end "Line 1\nLine 2\nLine 3\n"
update
.t delete 2.0 2.end
+ update
+ set res $tk_textRelayout
.t insert 2.0 "New Line 2"
update
- list [.t bbox 1.0] [.t bbox 2.0] [.t bbox 3.0] $tk_textRelayout
-} [list [list 5 5 7 $fixedHeight] [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 5 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] 2.0]
+ lappend res [.t bbox 1.0] [.t bbox 2.0] [.t bbox 3.0] $tk_textRelayout
+} [list 2.0 [list 5 5 7 $fixedHeight] [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 5 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] 2.0]
test textDisp-4.2 {UpdateDisplayInfo, re-use tail of text line} {textfonts} {
.t delete 1.0 end
.t insert end "Line 1\nLine 2 is so long that it wraps around\nLine 3"
update
.t mark set x 2.21
.t delete 2.2
+ update
+ set res $tk_textRelayout
.t insert 2.0 X
update
- list [.t bbox 2.0] [.t bbox x] [.t bbox 3.0] $tk_textRelayout
-} [list [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 12 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] [list 5 [expr {3*$fixedDiff + 44}] 7 $fixedHeight] {2.0 2.20}]
+ lappend res [.t bbox 2.0] [.t bbox x] [.t bbox 3.0] $tk_textRelayout
+} [list 2.0 2.20 [list 5 [expr {$fixedDiff + 18}] 7 $fixedHeight] [list 12 [expr {2*$fixedDiff + 31}] 7 $fixedHeight] [list 5 [expr {3*$fixedDiff + 44}] 7 $fixedHeight] {2.0 2.20}]
test textDisp-4.3 {UpdateDisplayInfo, tail of text line shifts} {textfonts} {
.t delete 1.0 end
.t insert end "Line 1\nLine 2 is so long that it wraps around\nLine 3"
@@ -1148,6 +1152,41 @@ test textDisp-8.11 {TkTextChanged, scrollbar notification when changes are off-s
.t configure -yscrollcommand ""
set scrollInfo
} {0.0 0.625}
+test textDisp-8.12 {TkTextChanged, moving the insert cursor redraws only past and new lines} {
+ .t delete 1.0 end
+ .t configure -wrap none
+ for {set i 1} {$i < 25} {incr i} {
+ .t insert end "Line $i Line $i\n"
+ }
+ .t tag add hidden 5.0 8.0
+ .t tag configure hidden -elide true
+ .t mark set insert 9.0
+ update
+ .t mark set insert 8.0 ; # up one line
+ update
+ set res [list $tk_textRedraw]
+ .t mark set insert 12.2 ; # in the visible text
+ update
+ lappend res $tk_textRedraw
+ .t mark set insert 6.5 ; # in the hidden text
+ update
+ lappend res $tk_textRedraw
+ .t mark set insert 3.5 ; # in the visible text again
+ update
+ lappend res $tk_textRedraw
+ .t mark set insert 3.8 ; # within the same line
+ update
+ lappend res $tk_textRedraw
+ # This last one is tricky: correct result really is {2.0 3.0} when
+ # calling .t mark set insert, two calls to TkTextChanged are done:
+ # (a) to redraw the line of the past position of the cursor
+ # (b) to redraw the line of the new position of the cursor
+ # During (a) the display line showing the cursor gets unlinked,
+ # which leads TkTextChanged in (b) to schedule a redraw starting
+ # one line _before_ the line containing the insert cursor. This is
+ # because during (b) findDLine cannot return the display line the
+ # cursor is in since this display line was just unlinked in (a).
+} {{8.0 9.0} {8.0 12.0} {8.0 12.0} {3.0 8.0} {2.0 3.0}}
test textDisp-9.1 {TkTextRedrawTag} {
.t configure -wrap char
@@ -1173,40 +1212,44 @@ test textDisp-9.3 {TkTextRedrawTag} {
.t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4"
update
.t tag add big 2.2 2.4
+ update
.t tag remove big 1.0 end
update
list $tk_textRelayout $tk_textRedraw
-} {2.0 2.0}
+} {{2.0 2.20} {2.0 2.20 eof}}
test textDisp-9.4 {TkTextRedrawTag} {
.t configure -wrap char
.t delete 1.0 end
.t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4"
update
.t tag add big 2.2 2.20
+ update
.t tag remove big 1.0 end
update
list $tk_textRelayout $tk_textRedraw
-} {2.0 2.0}
+} {{2.0 2.20} {2.0 2.20 eof}}
test textDisp-9.5 {TkTextRedrawTag} {
.t configure -wrap char
.t delete 1.0 end
.t insert 1.0 "Line 1\nLine 2 is long enough to wrap around\nLine 3\nLine 4"
update
.t tag add big 2.2 2.end
+ update
.t tag remove big 1.0 end
update
list $tk_textRelayout $tk_textRedraw
-} {{2.0 2.20} {2.0 2.20}}
+} {{2.0 2.20} {2.0 2.20 eof}}
test textDisp-9.6 {TkTextRedrawTag} {
.t configure -wrap char
.t delete 1.0 end
- .t insert 1.0 "Line 1\nLine 2 is long enough to wrap\nLine 3 is also long enough to wrap\nLine 4"
+ .t insert 1.0 "Line 1\nLine 2 is long enough to wrap\nLine 3 is also long enough to wrap"
update
.t tag add big 2.2 3.5
+ update
.t tag remove big 1.0 end
update
list $tk_textRelayout $tk_textRedraw
-} {{2.0 2.20 3.0} {2.0 2.20 3.0}}
+} {{2.0 2.20 3.0 3.20} {2.0 2.20 3.0 3.20 eof}}
test textDisp-9.7 {TkTextRedrawTag} {
.t configure -wrap char
.t delete 1.0 end
@@ -1258,6 +1301,34 @@ test textDisp-9.11 {TkTextRedrawTag} {
update
set tk_textRedraw
} {}
+test textDisp-9.12 {TkTextRedrawTag} {
+ .t configure -wrap char
+ .t delete 1.0 end
+ for {set i 1} {$i < 5} {incr i} {
+ .t insert end "Line $i+++Line $i\n"
+ }
+ .t tag configure hidden -elide true
+ .t tag add hidden 2.6 3.6
+ update
+ .t tag add hidden 3.11 4.6
+ update
+ list $tk_textRelayout $tk_textRedraw
+} {2.0 {2.0 eof}}
+test textDisp-9.13 {TkTextRedrawTag} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ for {set i 1} {$i < 10} {incr i} {
+ .t insert end "Line $i - This is Line [format %c [expr 64+$i]]\n"
+ }
+ .t tag add hidden 2.8 2.17
+ .t tag add hidden 6.8 7.17
+ .t tag configure hidden -background red
+ .t tag configure hidden -elide true
+ update
+ .t tag configure hidden -elide false
+ update
+ list $tk_textRelayout $tk_textRedraw
+} {{2.0 6.0 7.0} {2.0 6.0 7.0}}
test textDisp-10.1 {TkTextRelayoutWindow} {
.t configure -wrap char
@@ -1426,7 +1497,7 @@ test textDisp-11.15 {TkTextSetYView, only a few lines visible} {
update
.top.t see 11.0
.top.t index @0,0
- # Thie index 9.0 should be just visible by a couple of pixels
+ # The index 9.0 should be just visible by a couple of pixels
} {9.0}
test textDisp-11.16 {TkTextSetYView, only a few lines visible} {
.top.t yview 8.0
@@ -1439,9 +1510,54 @@ test textDisp-11.17 {TkTextSetYView, only a few lines visible} {
update
.top.t see 4.0
.top.t index @0,0
- # Thie index 2.0 should be just visible by a couple of pixels
+ # The index 2.0 should be just visible by a couple of pixels
} {2.0}
-destroy .top
+test textDisp-11.18 {TkTextSetYView, see in elided lines} {
+ .top.t delete 1.0 end
+ for {set i 1} {$i < 20} {incr i} {
+ .top.t insert end [string repeat "Line $i" 10]
+ .top.t insert end "\n"
+ }
+ .top.t yview 4.0
+ .top.t tag add hidden 4.10 "4.10 lineend"
+ .top.t tag add hidden 5.15 10.3
+ .top.t tag configure hidden -elide true
+ update
+ .top.t see "8.0 lineend"
+ # The index "8.0 lineend" is on screen despite elided -> no scroll
+ .top.t index @0,0
+} {4.0}
+test textDisp-11.19 {TkTextSetYView, see in elided lines} {
+ .top.t delete 1.0 end
+ for {set i 1} {$i < 50} {incr i} {
+ .top.t insert end "Line $i\n"
+ }
+ # button just for having a line with a larger height
+ button .top.t.b -text "Test" -bd 2 -highlightthickness 2
+ .top.t window create 21.0 -window .top.t.b
+ .top.t tag add hidden 15.36 21.0
+ .top.t tag configure hidden -elide true
+ .top.t configure -height 15
+ wm geometry .top 300x200+0+0
+ # Indices 21.0, 17.0 and 15.0 are all on the same display line
+ # therefore index @0,0 shall be the same for all of them
+ .top.t see end
+ update
+ .top.t see 21.0
+ update
+ set ind1 [.top.t index @0,0]
+ .top.t see end
+ update
+ .top.t see 17.0
+ update
+ set ind2 [.top.t index @0,0]
+ .top.t see end
+ update
+ .top.t see 15.0
+ update
+ set ind3 [.top.t index @0,0]
+ list [expr {$ind1 == $ind2}] [expr {$ind1 == $ind3}]
+} {1 1}
.t configure -wrap word
.t delete 50.0 51.0
@@ -1583,6 +1699,29 @@ test textDisp-13.10 {TkTextSeeCmd procedure} {} {
destroy $w
set res
} {}
+test textDisp-13.11 {TkTextSeeCmd procedure} {} {
+ # insertion of a character at end of a line containing multi-byte
+ # characters and calling see at the line end shall actually show
+ # this character
+ toplevel .top2
+ pack [text .top2.t2 -wrap none]
+ for {set i 1} {$i < 5} {incr i} {
+ .top2.t2 insert end [string repeat "Line $i: éèàçù" 5]\n
+
+ }
+ wm geometry .top2 300x200+0+0
+ update
+ .top2.t2 see "1.0 lineend"
+ update
+ set ref [.top2.t2 index @0,0]
+ .top2.t2 insert "1.0 lineend" ç
+ .top2.t2 see "1.0 lineend"
+ update
+ set new [.top2.t2 index @0,0]
+ set res [.top2.t2 compare $ref == $new]
+ destroy .top2
+ set res
+} {0}
wm geom . {}
.t configure -wrap none
@@ -2019,6 +2158,70 @@ test textDisp-16.40 {text count -xpixels} {
[.t count -xpixels 1.0 "1.0 displaylineend"] \
[.t count -xpixels 1.0 end]
} {35 -35 0 42 42 42 0}
+test textDisp-16.41 {text count -xpixels with indices in elided lines} {
+ set res {}
+ .t delete 1.0 end
+ for {set i 1} {$i < 40} {incr i} {
+ .t insert end [string repeat "Line $i" 20]
+ .t insert end "\n"
+ }
+ .t configure -wrap none
+ .t tag add hidden 5.15 20.15
+ .t tag configure hidden -elide true
+ lappend res [.t count -xpixels 5.15 6.0] \
+ [.t count -xpixels 5.15 6.1] \
+ [.t count -xpixels 6.0 6.1] \
+ [.t count -xpixels 6.1 6.2] \
+ [.t count -xpixels 6.1 6.0] \
+ [.t count -xpixels 6.0 7.0] \
+ [.t count -xpixels 6.1 7.1] \
+ [.t count -xpixels 15.0 20.15] \
+ [.t count -xpixels 20.15 20.16] \
+ [.t count -xpixels 20.16 20.15]
+ .t tag remove hidden 20.0 20.15
+ lappend res [expr {[.t count -xpixels 5.0 20.0] != 0}]
+} [list 0 0 0 0 0 0 0 0 $fixedWidth -$fixedWidth 1]
+test textDisp-16.42 {TkTextYviewCmd procedure with indices in elided lines} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ for {set i 1} {$i < 100} {incr i} {
+ .t insert end [string repeat "Line $i" 20]
+ .t insert end "\n"
+ }
+ .t tag add hidden 5.15 20.15
+ .t tag configure hidden -elide true
+ .t yview 35.0
+ .t yview scroll [expr {- 15 * $fixedHeight}] pixels
+ update
+ .t index @0,0
+} {5.0}
+test textDisp-16.43 {TkTextYviewCmd procedure with indices in elided lines} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ for {set i 1} {$i < 100} {incr i} {
+ .t insert end [string repeat "Line $i" 20]
+ .t insert end "\n"
+ }
+ .t tag add hidden 5.15 20.15
+ .t tag configure hidden -elide true
+ .t yview 35.0
+ .t yview scroll -15 units
+ update
+ .t index @0,0
+} {5.0}
+test textDisp-16.44 {TkTextYviewCmd procedure, scroll down, with elided lines} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ foreach x [list 0 1 2 3 4 5 6 7 8 9 0] {
+ .t insert end "$x aaa1\n$x bbb2\n$x ccc3\n$x ddd4\n$x eee5\n$x fff6"
+ .t insert end "$x 1111\n$x 2222\n$x 3333\n$x 4444\n$x 5555\n$x 6666" hidden
+ }
+ .t tag configure hidden -elide true ; # 5 hidden lines
+ update
+ .t see [expr {5 + [winfo height .t] / $fixedHeight} + 1].0
+ update
+ .t index @0,0
+} {2.0}
.t delete 1.0 end
foreach i {a b c d e f g h i j k l m n o p q r s t u v w x y z} {
@@ -2444,7 +2647,7 @@ test textDisp-19.11.23 {TextWidgetCmd procedure, "index +displaylines"} {
[.t index "12.0 +2d lines"] [.t index "11.0 +2d lines"] \
[.t index "13.0 +2d lines"] [.t index "13.0 +3d lines"] \
[.t index "13.0 +4d lines"]
-} {16.17 16.33 16.28 16.46 16.28 16.49 16.65 17.0}
+} {16.17 16.33 16.28 16.46 16.28 16.49 16.65 16.72}
.t tag remove elide 1.0 end
test textDisp-19.11.24 {TextWidgetCmd procedure, "index +/-displaylines"} {
list [.t index "11.5 + -1 display lines"] \
@@ -2553,6 +2756,63 @@ test textDisp-19.16 {count -ypixels} {
[.t count -ypixels 16.0 "16.0 displaylineend +1c"] \
[.t count -ypixels "16.0 +1 displaylines" "16.0 +4 displaylines +3c"]
} [list [expr {260 + 20 * $fixedDiff}] [expr {260 + 20 * $fixedDiff}] $fixedHeight [expr {2*$fixedHeight}] $fixedHeight [expr {3*$fixedHeight}]]
+test textDisp-19.17 {count -ypixels with indices in elided lines} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ for {set i 1} {$i < 100} {incr i} {
+ .t insert end [string repeat "Line $i" 20]
+ .t insert end "\n"
+ }
+ .t tag add hidden 5.15 20.15
+ .t tag configure hidden -elide true
+ set res {}
+ update
+ lappend res \
+ [.t count -ypixels 1.0 6.0] \
+ [.t count -ypixels 2.0 7.5] \
+ [.t count -ypixels 5.0 8.5] \
+ [.t count -ypixels 6.1 6.2] \
+ [.t count -ypixels 6.1 18.8] \
+ [.t count -ypixels 18.0 20.50] \
+ [.t count -ypixels 5.2 20.60] \
+ [.t count -ypixels 20.60 20.70] \
+ [.t count -ypixels 5.0 25.0] \
+ [.t count -ypixels 25.0 5.0] \
+ [.t count -ypixels 25.4 27.50] \
+ [.t count -ypixels 35.0 38.0]
+ .t yview 35.0
+ lappend res [.t count -ypixels 5.0 25.0]
+} [list [expr {4 * $fixedHeight}] [expr {3 * $fixedHeight}] 0 0 0 0 0 0 [expr {5 * $fixedHeight}] [expr {- 5 * $fixedHeight}] [expr {2 * $fixedHeight}] [expr {3 * $fixedHeight}] [expr {5 * $fixedHeight}]]
+test textDisp-19.18 {count -ypixels with indices in elided lines} {
+ .t configure -wrap none
+ .t delete 1.0 end
+ for {set i 1} {$i < 100} {incr i} {
+ .t insert end [string repeat "Line $i" 20]
+ .t insert end "\n"
+ }
+ .t tag add hidden 5.15 20.15
+ .t tag configure hidden -elide true
+ .t yview 35.0
+ set res {}
+ update
+ lappend res [.t count -ypixels 5.0 25.0]
+ .t yview scroll [expr {- 15 * $fixedHeight}] pixels
+ update
+ lappend res [.t count -ypixels 5.0 25.0]
+} [list [expr {5 * $fixedHeight}] [expr {5 * $fixedHeight}]]
+test textDisp-19.19 {count -ypixels with indices in elided lines} {
+ .t configure -wrap char
+ .t delete 1.0 end
+ for {set i 1} {$i < 25} {incr i} {
+ .t insert end [string repeat "Line $i -" 6]
+ .t insert end "\n"
+ }
+ .t tag add hidden 5.27 11.0
+ .t tag configure hidden -elide true
+ .t yview 5.0
+ update
+ set res [list [.t count -ypixels 5.0 11.0] [.t count -ypixels 5.0 11.20]]
+} [list [expr {1 * $fixedHeight}] [expr {2 * $fixedHeight}]]
.t delete 1.0 end
.t insert end "Line 1"
for {set i 2} {$i <= 200} {incr i} {
@@ -2722,6 +2982,42 @@ test textDisp-22.9 {TkTextCharBbox, handling of spacing} {textfonts} {
[.t bbox 1.1] [.t bbox 2.9]
} [list [list 24 11 10 4] [list 55 [expr {$fixedDiff/2 + 15}] 10 4] [list 10 [expr {2*$fixedDiff + 43}] 10 4] [list 76 [expr {2*$fixedDiff + 40}] 10 4] [list 10 11 7 $fixedHeight] [list 69 [expr {$fixedDiff + 34}] 7 $fixedHeight]]
.t tag delete spacing
+test textDisp-22.10 {TkTextCharBbox, handling of elided lines} {textfonts} {
+ .t configure -wrap char
+ .t delete 1.0 end
+ for {set i 1} {$i < 10} {incr i} {
+ .t insert end "Line $i - Line [format %c [expr 64+$i]]\n"
+ }
+ .t tag add hidden 2.8 2.13
+ .t tag add hidden 6.8 7.13
+ .t tag configure hidden -elide true
+ update
+ list \
+ [expr {[lindex [.t bbox 2.9] 0] - [lindex [.t bbox 2.8] 0]}] \
+ [expr {[lindex [.t bbox 2.10] 0] - [lindex [.t bbox 2.8] 0]}] \
+ [expr {[lindex [.t bbox 2.13] 0] - [lindex [.t bbox 2.8] 0]}] \
+ [expr {[lindex [.t bbox 6.9] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 6.10] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 6.13] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 6.14] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 6.15] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 7.0] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 7.1] 0] - [lindex [.t bbox 6.8] 0]}] \
+ [expr {[lindex [.t bbox 7.12] 0] - [lindex [.t bbox 6.8] 0]}]
+} [list 0 0 0 0 0 0 0 0 0 0 0]
+test textDisp-22.11 {TkTextCharBbox, handling of wrapped elided lines} {textfonts} {
+ .t configure -wrap char
+ .t delete 1.0 end
+ for {set i 1} {$i < 10} {incr i} {
+ .t insert end "Line $i - Line _$i - Lines .$i - Line [format %c [expr 64+$i]]\n"
+ }
+ .t tag add hidden 1.30 2.5
+ .t tag configure hidden -elide true
+ update
+ list \
+ [expr {[lindex [.t bbox 1.30] 0] - [lindex [.t bbox 2.4] 0]}] \
+ [expr {[lindex [.t bbox 1.30] 0] - [lindex [.t bbox 2.5] 0]}]
+} [list 0 0]
.t delete 1.0 end
.t insert end "Line 1"