📄 tktextdisp.c
字号:
lastCharChunkPtr = NULL; /* * Find the first segment to consider for the line. Can't call * TkTextIndexToSeg for this because it won't return a segment * with zero size (such as the insertion cursor's mark). */ for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr; (offset > 0) && (offset >= segPtr->size); offset -= segPtr->size, segPtr = segPtr->nextPtr) { /* Empty loop body. */ } while (segPtr != NULL) { if (segPtr->typePtr->layoutProc == NULL) { segPtr = segPtr->nextPtr; offset = 0; continue; } if (chunkPtr == NULL) { chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk)); chunkPtr->nextPtr = NULL; } chunkPtr->stylePtr = GetStyle(textPtr, &curIndex); /* * Save style information such as justification and indentation, * up until the first character is encountered, then retain that * information for the rest of the line. */ if (noCharsYet) { tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr; justify = chunkPtr->stylePtr->sValuePtr->justify; rMargin = chunkPtr->stylePtr->sValuePtr->rMargin; wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode; x = ((curIndex.charIndex == 0) ? chunkPtr->stylePtr->sValuePtr->lMargin1 : chunkPtr->stylePtr->sValuePtr->lMargin2); if (wrapMode == tkTextNoneUid) { maxX = INT_MAX; } else { maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin; if (maxX < x) { maxX = x; } } } /* * See if there is a tab in the current chunk; if so, only * layout characters up to (and including) the tab. */ gotTab = 0; maxChars = segPtr->size - offset; if (justify == TK_JUSTIFY_LEFT) { if (segPtr->typePtr == &tkTextCharType) { char *p; for (p = segPtr->body.chars + offset; *p != 0; p++) { if (*p == '\t') { maxChars = (p + 1 - segPtr->body.chars) - offset; gotTab = 1; break; } } } } chunkPtr->x = x; code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr, offset, maxX-tabSize, maxChars, noCharsYet, wrapMode, chunkPtr); if (code <= 0) { FreeStyle(textPtr, chunkPtr->stylePtr); if (code < 0) { /* * This segment doesn't wish to display itself (e.g. most * marks). */ segPtr = segPtr->nextPtr; offset = 0; continue; } /* * No characters from this segment fit in the window: this * means we're at the end of the display line. */ if (chunkPtr != NULL) { ckfree((char *) chunkPtr); } break; } if (chunkPtr->numChars > 0) { noCharsYet = 0; lastCharChunkPtr = chunkPtr; } if (lastChunkPtr == NULL) { dlPtr->chunkPtr = chunkPtr; } else { lastChunkPtr->nextPtr = chunkPtr; } lastChunkPtr = chunkPtr; x += chunkPtr->width; if (chunkPtr->breakIndex > 0) { breakCharOffset = chunkPtr->breakIndex; breakIndex = curIndex; breakChunkPtr = chunkPtr; } if (chunkPtr->numChars != maxChars) { break; } /* * If we're at a new tab, adjust the layout for all the chunks * pertaining to the previous tab. Also adjust the amount of * space left in the line to account for space that will be eaten * up by the tab. */ if (gotTab) { if (tabIndex >= 0) { AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr); x = chunkPtr->x + chunkPtr->width; } tabIndex++; tabChunkPtr = chunkPtr; tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX); if (tabSize >= (maxX - x)) { break; } } curIndex.charIndex += chunkPtr->numChars; offset += chunkPtr->numChars; if (offset >= segPtr->size) { offset = 0; segPtr = segPtr->nextPtr; } chunkPtr = NULL; } if (noCharsYet) { panic("LayoutDLine couldn't place any characters on a line"); } wholeLine = (segPtr == NULL); /* * We're at the end of the display line. Throw away everything * after the most recent word break, if there is one; this may * potentially require the last chunk to be layed out again. */ if (breakChunkPtr == NULL) { /* * This code makes sure that we don't accidentally display * chunks with no characters at the end of the line (such as * the insertion cursor). These chunks belong on the next * line. So, throw away everything after the last chunk that * has characters in it. */ breakChunkPtr = lastCharChunkPtr; breakCharOffset = breakChunkPtr->numChars; } if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr) || (breakCharOffset != lastChunkPtr->numChars))) { while (1) { chunkPtr = breakChunkPtr->nextPtr; if (chunkPtr == NULL) { break; } FreeStyle(textPtr, chunkPtr->stylePtr); breakChunkPtr->nextPtr = chunkPtr->nextPtr; (*chunkPtr->undisplayProc)(textPtr, chunkPtr); ckfree((char *) chunkPtr); } if (breakCharOffset != breakChunkPtr->numChars) { (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr); segPtr = TkTextIndexToSeg(&breakIndex, &offset); (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex, segPtr, offset, maxX, breakCharOffset, 0, wrapMode, breakChunkPtr); } lastChunkPtr = breakChunkPtr; wholeLine = 0; } /* * Make tab adjustments for the last tab stop, if there is one. */ if ((tabIndex >= 0) && (tabChunkPtr != NULL)) { AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr); } /* * Make one more pass over the line to recompute various things * like its height, length, and total number of characters. Also * modify the x-locations of chunks to reflect justification. * If we're not wrapping, I'm not sure what is the best way to * handle left and center justification: should the total length, * for purposes of justification, be (a) the window width, (b) * the length of the longest line in the window, or (c) the length * of the longest line in the text? (c) isn't available, (b) seems * weird, since it can change with vertical scrolling, so (a) is * what is implemented below. */ if (wrapMode == tkTextNoneUid) { maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin; } dlPtr->length = lastChunkPtr->x + lastChunkPtr->width; if (justify == TK_JUSTIFY_LEFT) { jIndent = 0; } else if (justify == TK_JUSTIFY_RIGHT) { jIndent = maxX - dlPtr->length; } else { jIndent = (maxX - dlPtr->length)/2; } ascent = descent = 0; for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL; chunkPtr = chunkPtr->nextPtr) { chunkPtr->x += jIndent; dlPtr->count += chunkPtr->numChars; if (chunkPtr->minAscent > ascent) { ascent = chunkPtr->minAscent; } if (chunkPtr->minDescent > descent) { descent = chunkPtr->minDescent; } if (chunkPtr->minHeight > dlPtr->height) { dlPtr->height = chunkPtr->minHeight; } sValuePtr = chunkPtr->stylePtr->sValuePtr; if ((sValuePtr->borderWidth > 0) && (sValuePtr->relief != TK_RELIEF_FLAT)) { dlPtr->flags |= HAS_3D_BORDER; } } if (dlPtr->height < (ascent + descent)) { dlPtr->height = ascent + descent; dlPtr->baseline = ascent; } else { dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2; } sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr; if (dlPtr->index.charIndex == 0) { dlPtr->spaceAbove = sValuePtr->spacing1; } else { dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2; } if (wholeLine) { dlPtr->spaceBelow = sValuePtr->spacing3; } else { dlPtr->spaceBelow = sValuePtr->spacing2/2; } dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow; dlPtr->baseline += dlPtr->spaceAbove; /* * Recompute line length: may have changed because of justification. */ dlPtr->length = lastChunkPtr->x + lastChunkPtr->width; return dlPtr;}/* *---------------------------------------------------------------------- * * UpdateDisplayInfo -- * * This procedure is invoked to recompute some or all of the * DLine structures for a text widget. At the time it is called * the DLine structures still left in the widget are guaranteed * to be correct except that (a) the y-coordinates aren't * necessarily correct, (b) there may be missing structures * (the DLine structures get removed as soon as they are potentially * out-of-date), and (c) DLine structures that don't start at the * beginning of a line may be incorrect if previous information in * the same line changed size in a way that moved a line boundary * (DLines for any info that changed will have been deleted, but * not DLines for unchanged info in the same text line). * * Results: * None. * * Side effects: * Upon return, the DLine information for textPtr correctly reflects * the positions where characters will be displayed. However, this * procedure doesn't actually bring the display up-to-date. * *---------------------------------------------------------------------- */static voidUpdateDisplayInfo(textPtr) TkText *textPtr; /* Text widget to update. */{ register TextDInfo *dInfoPtr = textPtr->dInfoPtr; register DLine *dlPtr, *prevPtr; TkTextIndex index; TkTextLine *lastLinePtr; int y, maxY, pixelOffset, maxOffset; if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) { return; } dInfoPtr->flags &= ~DINFO_OUT_OF_DATE; /* * Delete any DLines that are now above the top of the window. */ index = textPtr->topIndex; dlPtr = FindDLine(dInfoPtr->dLinePtr, &index); if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) { FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1); } /* *-------------------------------------------------------------- * Scan through the contents of the window from top to bottom, * recomputing information for lines that are missing. *-------------------------------------------------------------- */ lastLinePtr = TkBTreeFindLine(textPtr->tree, TkBTreeNumLines(textPtr->tree)); dlPtr = dInfoPtr->dLinePtr; prevPtr = NULL; y = dInfoPtr->y; maxY = dInfoPtr->maxY; while (1) { register DLine *newPtr; if (index.linePtr == lastLinePtr) { break; } /* * There are three possibilities right now: * (a) the next DLine (dlPtr) corresponds exactly to the next * information we want to display: just use it as-is. * (b) the next DLine corresponds to a different line, or to * a segment that will be coming later in the same line: * leave this DLine alone in the hopes that we'll be able * to use it later, then create a new DLine in front of * it. * (c) the next DLine corresponds to a segment in the line we * want, but it's a segment that has already been processed * or will never be processed. Delete the DLine and try * again. * * One other twist on all this. It's possible for 3D borders * to interact between lines (see DisplayLineBackground) so if * a line is relayed out and has styles with 3D borders, its * neighbors have to be redrawn if they have 3D borders too, * since the interactions could have changed (the neighbors * don't have to be relayed out, just redrawn). */ if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) { /* * Case (b) -- must make new DLine. */ makeNewDLine: if (tkTextDebug) { char string[TK_POS_CHARS]; /* * Debugging is enabled, so keep a log of all the lines * that were re-layed out. The test suite uses this * information. */ TkTextPrintIndex(&index, string); Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL, string, TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT); } newPtr = LayoutDLine(textPtr, &index); if (prevPtr == NULL) { dInfoPtr->dLinePtr = newPtr; } else { prevPtr->nextPtr = newPtr; if (prevPtr->flags & HAS_3D_BORDER) { prevPtr->oldY = -1; } } newPtr->nextPtr = dlPtr; dlPtr = newPtr; } else { /* * DlPtr refers to the line we want. Next check the * index within the line. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -