diff options
author | dgp <dgp@users.sourceforge.net> | 2015-02-06 18:13:48 (GMT) |
---|---|---|
committer | dgp <dgp@users.sourceforge.net> | 2015-02-06 18:13:48 (GMT) |
commit | 13d19756cf0ce7e3255a5d4c9a5efdfae1bc95df (patch) | |
tree | 36babdf2ad07ce4891bd92635298bc51692c100f /generic | |
parent | 75fec0b8d45c2b2151e5337213b8f122e0d6f025 (diff) | |
parent | 6b00db07c84b661e9971e8aff1ae241138cea97f (diff) | |
download | tk-13d19756cf0ce7e3255a5d4c9a5efdfae1bc95df.zip tk-13d19756cf0ce7e3255a5d4c9a5efdfae1bc95df.tar.gz tk-13d19756cf0ce7e3255a5d4c9a5efdfae1bc95df.tar.bz2 |
Merge bug fixes from fvogel's text-elided branch
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkText.c | 53 | ||||
-rw-r--r-- | generic/tkText.h | 3 | ||||
-rw-r--r-- | generic/tkTextBTree.c | 3 | ||||
-rw-r--r-- | generic/tkTextDisp.c | 567 | ||||
-rw-r--r-- | generic/tkTextIndex.c | 87 |
5 files changed, 514 insertions, 199 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 |