tkTextDisp.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:158k
- /*
- * tkTextDisp.c --
- *
- * This module provides facilities to display text widgets. It is
- * the only place where information is kept about the screen layout
- * of text widgets.
- *
- * Copyright (c) 1992-1994 The Regents of the University of California.
- * Copyright (c) 1994-1997 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tkTextDisp.c,v 1.14.2.5 2007/04/29 02:24:02 das Exp $
- */
- #include "tkPort.h"
- #include "tkInt.h"
- #include "tkText.h"
- #ifdef __WIN32__
- #include "tkWinInt.h"
- #endif
- #ifdef TK_NO_DOUBLE_BUFFERING
- #ifdef MAC_OSX_TK
- #include "tkMacOSXInt.h"
- #endif
- #endif /* TK_NO_DOUBLE_BUFFERING */
- /*
- * The following structure describes how to display a range of characters.
- * The information is generated by scanning all of the tags associated
- * with the characters and combining that with default information for
- * the overall widget. These structures form the hash keys for
- * dInfoPtr->styleTable.
- */
- typedef struct StyleValues {
- Tk_3DBorder border; /* Used for drawing background under text.
- * NULL means use widget background. */
- int borderWidth; /* Width of 3-D border for background. */
- int relief; /* 3-D relief for background. */
- Pixmap bgStipple; /* Stipple bitmap for background. None
- * means draw solid. */
- XColor *fgColor; /* Foreground color for text. */
- Tk_Font tkfont; /* Font for displaying text. */
- Pixmap fgStipple; /* Stipple bitmap for text and other
- * foreground stuff. None means draw
- * solid.*/
- int justify; /* Justification style for text. */
- int lMargin1; /* Left margin, in pixels, for first display
- * line of each text line. */
- int lMargin2; /* Left margin, in pixels, for second and
- * later display lines of each text line. */
- int offset; /* Offset in pixels of baseline, relative to
- * baseline of line. */
- int overstrike; /* Non-zero means draw overstrike through
- * text. */
- int rMargin; /* Right margin, in pixels. */
- int spacing1; /* Spacing above first dline in text line. */
- int spacing2; /* Spacing between lines of dline. */
- int spacing3; /* Spacing below last dline in text line. */
- TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
- * be NULL). */
- int underline; /* Non-zero means draw underline underneath
- * text. */
- int elide; /* Non-zero means draw text */
- TkWrapMode wrapMode; /* How to handle wrap-around for this tag.
- * One of TEXT_WRAPMODE_CHAR,
- * TEXT_WRAPMODE_NONE or TEXT_WRAPMODE_WORD.*/
- } StyleValues;
- /*
- * The following structure extends the StyleValues structure above with
- * graphics contexts used to actually draw the characters. The entries
- * in dInfoPtr->styleTable point to structures of this type.
- */
- typedef struct TextStyle {
- int refCount; /* Number of times this structure is
- * referenced in Chunks. */
- GC bgGC; /* Graphics context for background. None
- * means use widget background. */
- GC fgGC; /* Graphics context for foreground. */
- StyleValues *sValuePtr; /* Raw information from which GCs were
- * derived. */
- Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable. Used
- * to delete entry. */
- } TextStyle;
- /*
- * The following macro determines whether two styles have the same
- * background so that, for example, no beveled border should be drawn
- * between them.
- */
- #define SAME_BACKGROUND(s1, s2)
- (((s1)->sValuePtr->border == (s2)->sValuePtr->border)
- && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth)
- && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief)
- && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
- /*
- * The following macro is used to compare two floating-point numbers
- * to within a certain degree of scale. Direct comparison fails on
- * processors where the processor and memory representations of FP
- * numbers of a particular precision is different (e.g. Intel)
- */
- #define FP_EQUAL_SCALE(double1, double2, scaleFactor)
- (fabs((double1)-(double2))*((scaleFactor)+1.0) < 0.3)
- /*
- * The following structure describes one line of the display, which may
- * be either part or all of one line of the text.
- */
- typedef struct DLine {
- TkTextIndex index; /* Identifies first character in text
- * that is displayed on this line. */
- int byteCount; /* Number of bytes accounted for by this
- * display line, including a trailing space
- * or newline that isn't actually displayed. */
- int y; /* Y-position at which line is supposed to
- * be drawn (topmost pixel of rectangular
- * area occupied by line). */
- int oldY; /* Y-position at which line currently
- * appears on display. -1 means line isn't
- * currently visible on display and must be
- * redrawn. This is used to move lines by
- * scrolling rather than re-drawing. */
- int height; /* Height of line, in pixels. */
- int baseline; /* Offset of text baseline from y, in
- * pixels. */
- int spaceAbove; /* How much extra space was added to the
- * top of the line because of spacing
- * options. This is included in height
- * and baseline. */
- int spaceBelow; /* How much extra space was added to the
- * bottom of the line because of spacing
- * options. This is included in height. */
- int length; /* Total length of line, in pixels. */
- TkTextDispChunk *chunkPtr; /* Pointer to first chunk in list of all
- * of those that are displayed on this
- * line of the screen. */
- struct DLine *nextPtr; /* Next in list of all display lines for
- * this window. The list is sorted in
- * order from 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 have been
- * deleted because they're out of date. */
- int flags; /* Various flag bits: see below for values. */
- } DLine;
- /*
- * Flag bits for DLine structures:
- *
- * HAS_3D_BORDER - Non-zero means that at least one of the
- * chunks in this line has a 3D border, so
- * it potentially interacts with 3D borders
- * in neighboring lines (see
- * DisplayLineBackground).
- * NEW_LAYOUT - Non-zero means that the line has been
- * re-layed out since the last time the
- * display was updated.
- * TOP_LINE - Non-zero means that this was the top line
- * in the window the last time that the window
- * was laid out. This is important because
- * a line may be displayed differently if its
- * at the top or bottom than if it's in the
- * middle (e.g. beveled edges aren't displayed
- * for middle lines if the adjacent line has
- * a similar background).
- * BOTTOM_LINE - Non-zero means that this was the bottom line
- * in the window the last time that the window
- * was laid out.
- * IS_DISABLED - This Dline cannot be edited.
- */
- #define HAS_3D_BORDER 1
- #define NEW_LAYOUT 2
- #define TOP_LINE 4
- #define BOTTOM_LINE 8
- #define IS_DISABLED 16
- /*
- * Overall display information for a text widget:
- */
- typedef struct TextDInfo {
- Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
- * to TextStyles for this widget. */
- DLine *dLinePtr; /* First in list of all display lines for
- * this widget, in order from top to bottom. */
- GC copyGC; /* Graphics context for copying from off-
- * screen pixmaps onto screen. */
- GC scrollGC; /* Graphics context for copying from one place
- * in the window to another (scrolling):
- * differs from copyGC in that we need to get
- * GraphicsExpose events. */
- int x; /* First x-coordinate that may be used for
- * actually displaying line information.
- * Leaves space for border, etc. */
- int y; /* First y-coordinate that may be used for
- * actually displaying line information.
- * Leaves space for border, etc. */
- int maxX; /* First x-coordinate to right of available
- * space for displaying lines. */
- int maxY; /* First y-coordinate below available
- * space for displaying lines. */
- int topOfEof; /* Top-most pixel (lowest y-value) that has
- * been drawn in the appropriate fashion for
- * the portion of the window after the last
- * line of the text. This field is used to
- * figure out when to redraw part or all of
- * the eof field. */
- /*
- * Information used for scrolling:
- */
- int newByteOffset; /* Desired x scroll position, measured as the
- * number of average-size characters off-screen
- * to the left for a line with no left
- * margin. */
- int curPixelOffset; /* Actual x scroll position, measured as the
- * number of pixels off-screen to the left. */
- int maxLength; /* Length in pixels of longest line that's
- * visible in window (length may exceed window
- * size). If there's no wrapping, this will
- * be zero. */
- double xScrollFirst, xScrollLast;
- /* Most recent values reported to horizontal
- * scrollbar; used to eliminate unnecessary
- * reports. */
- double yScrollFirst, yScrollLast;
- /* Most recent values reported to vertical
- * scrollbar; used to eliminate unnecessary
- * reports. */
- /*
- * The following information is used to implement scanning:
- */
- int scanMarkIndex; /* Byte index of character that was at the
- * left edge of the window when the scan
- * started. */
- int scanMarkX; /* X-position of mouse at time scan started. */
- int scanTotalScroll; /* Total scrolling (in screen lines) that has
- * occurred since scanMarkY was set. */
- int scanMarkY; /* Y-position of mouse at time scan started. */
- /*
- * Miscellaneous information:
- */
- int dLinesInvalidated; /* This value is set to 1 whenever something
- * happens that invalidates information in
- * DLine structures; if a redisplay
- * is in progress, it will see this and
- * abort the redisplay. This is needed
- * because, for example, an embedded window
- * could change its size when it is first
- * displayed, invalidating the DLine that
- * is currently being displayed. If redisplay
- * continues, it will use freed memory and
- * could dump core. */
- int flags; /* Various flag values: see below for
- * definitions. */
- } TextDInfo;
- /*
- * In TkTextDispChunk structures for character segments, the clientData
- * field points to one of the following structures:
- */
- typedef struct CharInfo {
- int numBytes; /* Number of bytes to display. */
- char chars[4]; /* UTF characters to display. Actual size
- * will be numBytes, not 4. THIS MUST BE
- * THE LAST FIELD IN THE STRUCTURE. */
- } CharInfo;
- /*
- * Flag values for TextDInfo structures:
- *
- * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
- * for this window are partially or completely
- * out of date and need to be recomputed.
- * REDRAW_PENDING: Means that a when-idle handler has been
- * scheduled to update the display.
- * REDRAW_BORDERS: Means window border or pad area has
- * potentially been damaged and must be redrawn.
- * REPICK_NEEDED: 1 means that the widget has been modified
- * in a way that could change the current
- * character (a different character might be
- * under the mouse cursor now). Need to
- * recompute the current character before
- * the next redisplay.
- */
- #define DINFO_OUT_OF_DATE 1
- #define REDRAW_PENDING 2
- #define REDRAW_BORDERS 4
- #define REPICK_NEEDED 8
- /*
- * The following counters keep statistics about redisplay that can be
- * checked to see how clever this code is at reducing redisplays.
- */
- static int numRedisplays; /* Number of calls to DisplayText. */
- static int linesRedrawn; /* Number of calls to DisplayDLine. */
- static int numCopies; /* Number of calls to XCopyArea to copy part
- * of the screen. */
- /*
- * Forward declarations for procedures defined later in this file:
- */
- static void AdjustForTab _ANSI_ARGS_((TkText *textPtr,
- TkTextTabArray *tabArrayPtr, int index,
- TkTextDispChunk *chunkPtr));
- static void CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
- int index, int y, int lineHeight, int baseline,
- int *xPtr, int *yPtr, int *widthPtr,
- int *heightPtr));
- static void CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
- int x, int y, int height, int baseline,
- Display *display, Drawable dst, int screenY));
- static int CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
- int x));
- static void CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
- TkTextDispChunk *chunkPtr));
- /*
- Definitions of elided procs.
- Compiler can't inline these since we use pointers to these functions.
- ElideDisplayProc, ElideUndisplayProc special-cased for speed,
- as potentially many elided DLine chunks if large, tag toggle-filled
- elided region.
- */
- static void ElideBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
- int index, int y, int lineHeight, int baseline,
- int *xPtr, int *yPtr, int *widthPtr,
- int *heightPtr));
- static int ElideMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
- int x));
- static void DisplayDLine _ANSI_ARGS_((TkText *textPtr,
- DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
- static void DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
- DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
- static void DisplayText _ANSI_ARGS_((ClientData clientData));
- static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr,
- TkTextIndex *indexPtr));
- static void FreeDLines _ANSI_ARGS_((TkText *textPtr,
- DLine *firstPtr, DLine *lastPtr, int unlink));
- static void FreeStyle _ANSI_ARGS_((TkText *textPtr,
- TextStyle *stylePtr));
- static TextStyle * GetStyle _ANSI_ARGS_((TkText *textPtr,
- TkTextIndex *indexPtr));
- static void GetXView _ANSI_ARGS_((Tcl_Interp *interp,
- TkText *textPtr, int report));
- static void GetYView _ANSI_ARGS_((Tcl_Interp *interp,
- TkText *textPtr, int report));
- static DLine * LayoutDLine _ANSI_ARGS_((TkText *textPtr,
- TkTextIndex *indexPtr));
- static int MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
- CONST char *source, int maxBytes, int startX,
- int maxX, int tabOrigin, int *nextXPtr));
- static void MeasureUp _ANSI_ARGS_((TkText *textPtr,
- TkTextIndex *srcPtr, int distance,
- TkTextIndex *dstPtr));
- static int NextTabStop _ANSI_ARGS_((Tk_Font tkfont, int x,
- int tabOrigin));
- static void UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
- static void ScrollByLines _ANSI_ARGS_((TkText *textPtr,
- int offset));
- static int SizeOfTab _ANSI_ARGS_((TkText *textPtr,
- TkTextTabArray *tabArrayPtr, int index, int x,
- int maxX));
- static void TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
- TkRegion region));
- /*
- *----------------------------------------------------------------------
- *
- * TkTextCreateDInfo --
- *
- * This procedure is called when a new text widget is created.
- * Its job is to set up display-related information for the widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A TextDInfo data structure is allocated and initialized and attached
- * to textPtr.
- *
- *----------------------------------------------------------------------
- */
- void
- TkTextCreateDInfo(textPtr)
- TkText *textPtr; /* Overall information for text widget. */
- {
- register TextDInfo *dInfoPtr;
- XGCValues gcValues;
- dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
- Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
- dInfoPtr->dLinePtr = NULL;
- dInfoPtr->copyGC = None;
- gcValues.graphics_exposures = True;
- dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
- &gcValues);
- dInfoPtr->topOfEof = 0;
- dInfoPtr->newByteOffset = 0;
- dInfoPtr->curPixelOffset = 0;
- dInfoPtr->maxLength = 0;
- dInfoPtr->xScrollFirst = -1;
- dInfoPtr->xScrollLast = -1;
- dInfoPtr->yScrollFirst = -1;
- dInfoPtr->yScrollLast = -1;
- dInfoPtr->scanMarkIndex = 0;
- dInfoPtr->scanMarkX = 0;
- dInfoPtr->scanTotalScroll = 0;
- dInfoPtr->scanMarkY = 0;
- dInfoPtr->dLinesInvalidated = 0;
- dInfoPtr->flags = DINFO_OUT_OF_DATE;
- textPtr->dInfoPtr = dInfoPtr;
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkTextFreeDInfo --
- *
- * This procedure is called to free up all of the private display
- * information kept by this file for a text widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Lots of resources get freed.
- *
- *----------------------------------------------------------------------
- */
- void
- TkTextFreeDInfo(textPtr)
- TkText *textPtr; /* Overall information for text widget. */
- {
- register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- /*
- * Be careful to free up styleTable *after* freeing up all the
- * DLines, so that the hash table is still intact to free up the
- * style-related information from the lines. Once the lines are
- * all free then styleTable will be empty.
- */
- FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
- Tcl_DeleteHashTable(&dInfoPtr->styleTable);
- if (dInfoPtr->copyGC != None) {
- Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
- }
- Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
- if (dInfoPtr->flags & REDRAW_PENDING) {
- Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
- }
- ckfree((char *) dInfoPtr);
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetStyle --
- *
- * This procedure creates all the information needed to display
- * text at a particular location.
- *
- * Results:
- * The return value is a pointer to a TextStyle structure that
- * corresponds to *sValuePtr.
- *
- * Side effects:
- * A new entry may be created in the style table for the widget.
- *
- *----------------------------------------------------------------------
- */
- static TextStyle *
- GetStyle(textPtr, indexPtr)
- TkText *textPtr; /* Overall information about text widget. */
- TkTextIndex *indexPtr; /* The character in the text for which
- * display information is wanted. */
- {
- TkTextTag **tagPtrs;
- register TkTextTag *tagPtr;
- StyleValues styleValues;
- TextStyle *stylePtr;
- Tcl_HashEntry *hPtr;
- int numTags, new, i;
- XGCValues gcValues;
- unsigned long mask;
- /*
- * The variables below keep track of the highest-priority specification
- * that has occurred for each of the various fields of the StyleValues.
- */
- int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
- int fgPrio, fontPrio, fgStipplePrio;
- int underlinePrio, elidePrio, justifyPrio, offsetPrio;
- int lMargin1Prio, lMargin2Prio, rMarginPrio;
- int spacing1Prio, spacing2Prio, spacing3Prio;
- int overstrikePrio, tabPrio, wrapPrio;
- /*
- * Find out what tags are present for the character, then compute
- * a StyleValues structure corresponding to those tags (scan
- * through all of the tags, saving information for the highest-
- * priority tag).
- */
- tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
- borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
- fgPrio = fontPrio = fgStipplePrio = -1;
- underlinePrio = elidePrio = justifyPrio = offsetPrio = -1;
- lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
- spacing1Prio = spacing2Prio = spacing3Prio = -1;
- overstrikePrio = tabPrio = wrapPrio = -1;
- memset((VOID *) &styleValues, 0, sizeof(StyleValues));
- styleValues.relief = TK_RELIEF_FLAT;
- styleValues.fgColor = textPtr->fgColor;
- styleValues.tkfont = textPtr->tkfont;
- styleValues.justify = TK_JUSTIFY_LEFT;
- styleValues.spacing1 = textPtr->spacing1;
- styleValues.spacing2 = textPtr->spacing2;
- styleValues.spacing3 = textPtr->spacing3;
- styleValues.tabArrayPtr = textPtr->tabArrayPtr;
- styleValues.wrapMode = textPtr->wrapMode;
- styleValues.elide = 0;
- for (i = 0 ; i < numTags; i++) {
- tagPtr = tagPtrs[i];
- /*
- * Skip the selection tag if we don't have focus,
- * unless we always want to show the selection.
- */
- if (
- #ifndef MAC_OSX_TK
- !TkpAlwaysShowSelection(textPtr->tkwin)
- #else
- /* Don't show inactive selection in disabled widgets. */
- textPtr->state == TK_STATE_DISABLED
- #endif
- && (tagPtr == textPtr->selTagPtr)
- && !(textPtr->flags & GOT_FOCUS)) {
- continue;
- }
- if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
- styleValues.border = tagPtr->border;
- borderPrio = tagPtr->priority;
- }
- if ((tagPtr->bdString != NULL)
- && (tagPtr->priority > borderWidthPrio)) {
- styleValues.borderWidth = tagPtr->borderWidth;
- borderWidthPrio = tagPtr->priority;
- }
- if ((tagPtr->reliefString != NULL)
- && (tagPtr->priority > reliefPrio)) {
- if (styleValues.border == NULL) {
- styleValues.border = textPtr->border;
- }
- styleValues.relief = tagPtr->relief;
- reliefPrio = tagPtr->priority;
- }
- if ((tagPtr->bgStipple != None)
- && (tagPtr->priority > bgStipplePrio)) {
- styleValues.bgStipple = tagPtr->bgStipple;
- bgStipplePrio = tagPtr->priority;
- }
- if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
- styleValues.fgColor = tagPtr->fgColor;
- fgPrio = tagPtr->priority;
- }
- if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
- styleValues.tkfont = tagPtr->tkfont;
- fontPrio = tagPtr->priority;
- }
- if ((tagPtr->fgStipple != None)
- && (tagPtr->priority > fgStipplePrio)) {
- styleValues.fgStipple = tagPtr->fgStipple;
- fgStipplePrio = tagPtr->priority;
- }
- if ((tagPtr->justifyString != NULL)
- && (tagPtr->priority > justifyPrio)) {
- styleValues.justify = tagPtr->justify;
- justifyPrio = tagPtr->priority;
- }
- if ((tagPtr->lMargin1String != NULL)
- && (tagPtr->priority > lMargin1Prio)) {
- styleValues.lMargin1 = tagPtr->lMargin1;
- lMargin1Prio = tagPtr->priority;
- }
- if ((tagPtr->lMargin2String != NULL)
- && (tagPtr->priority > lMargin2Prio)) {
- styleValues.lMargin2 = tagPtr->lMargin2;
- lMargin2Prio = tagPtr->priority;
- }
- if ((tagPtr->offsetString != NULL)
- && (tagPtr->priority > offsetPrio)) {
- styleValues.offset = tagPtr->offset;
- offsetPrio = tagPtr->priority;
- }
- if ((tagPtr->overstrikeString != NULL)
- && (tagPtr->priority > overstrikePrio)) {
- styleValues.overstrike = tagPtr->overstrike;
- overstrikePrio = tagPtr->priority;
- }
- if ((tagPtr->rMarginString != NULL)
- && (tagPtr->priority > rMarginPrio)) {
- styleValues.rMargin = tagPtr->rMargin;
- rMarginPrio = tagPtr->priority;
- }
- if ((tagPtr->spacing1String != NULL)
- && (tagPtr->priority > spacing1Prio)) {
- styleValues.spacing1 = tagPtr->spacing1;
- spacing1Prio = tagPtr->priority;
- }
- if ((tagPtr->spacing2String != NULL)
- && (tagPtr->priority > spacing2Prio)) {
- styleValues.spacing2 = tagPtr->spacing2;
- spacing2Prio = tagPtr->priority;
- }
- if ((tagPtr->spacing3String != NULL)
- && (tagPtr->priority > spacing3Prio)) {
- styleValues.spacing3 = tagPtr->spacing3;
- spacing3Prio = tagPtr->priority;
- }
- if ((tagPtr->tabString != NULL)
- && (tagPtr->priority > tabPrio)) {
- styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
- tabPrio = tagPtr->priority;
- }
- if ((tagPtr->underlineString != NULL)
- && (tagPtr->priority > underlinePrio)) {
- styleValues.underline = tagPtr->underline;
- underlinePrio = tagPtr->priority;
- }
- if ((tagPtr->elideString != NULL)
- && (tagPtr->priority > elidePrio)) {
- styleValues.elide = tagPtr->elide;
- elidePrio = tagPtr->priority;
- }
- if ((tagPtr->wrapMode != TEXT_WRAPMODE_NULL)
- && (tagPtr->priority > wrapPrio)) {
- styleValues.wrapMode = tagPtr->wrapMode;
- wrapPrio = tagPtr->priority;
- }
- }
- if (tagPtrs != NULL) {
- ckfree((char *) tagPtrs);
- }
- /*
- * Use an existing style if there's one around that matches.
- */
- hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
- (char *) &styleValues, &new);
- if (!new) {
- stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
- stylePtr->refCount++;
- return stylePtr;
- }
- /*
- * No existing style matched. Make a new one.
- */
- stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
- stylePtr->refCount = 1;
- if (styleValues.border != NULL) {
- gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
- mask = GCForeground;
- if (styleValues.bgStipple != None) {
- gcValues.stipple = styleValues.bgStipple;
- gcValues.fill_style = FillStippled;
- mask |= GCStipple|GCFillStyle;
- }
- stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
- } else {
- stylePtr->bgGC = None;
- }
- mask = GCFont;
- gcValues.font = Tk_FontId(styleValues.tkfont);
- mask |= GCForeground;
- gcValues.foreground = styleValues.fgColor->pixel;
- if (styleValues.fgStipple != None) {
- gcValues.stipple = styleValues.fgStipple;
- gcValues.fill_style = FillStippled;
- mask |= GCStipple|GCFillStyle;
- }
- stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
- stylePtr->sValuePtr = (StyleValues *)
- Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
- stylePtr->hPtr = hPtr;
- Tcl_SetHashValue(hPtr, stylePtr);
- return stylePtr;
- }
- /*
- *----------------------------------------------------------------------
- *
- * FreeStyle --
- *
- * This procedure is called when a TextStyle structure is no longer
- * needed. It decrements the reference count and frees up the
- * space for the style structure if the reference count is 0.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The storage and other resources associated with the style
- * are freed up if no-one's still using it.
- *
- *----------------------------------------------------------------------
- */
- static void
- FreeStyle(textPtr, stylePtr)
- TkText *textPtr; /* Information about overall widget. */
- register TextStyle *stylePtr; /* Information about style to free. */
- {
- stylePtr->refCount--;
- if (stylePtr->refCount == 0) {
- if (stylePtr->bgGC != None) {
- Tk_FreeGC(textPtr->display, stylePtr->bgGC);
- }
- if (stylePtr->fgGC != None) {
- Tk_FreeGC(textPtr->display, stylePtr->fgGC);
- }
- Tcl_DeleteHashEntry(stylePtr->hPtr);
- ckfree((char *) stylePtr);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * LayoutDLine --
- *
- * This procedure generates a single DLine structure for a display
- * line whose leftmost character is given by indexPtr.
- *
- * Results:
- * The return value is a pointer to a DLine structure desribing the
- * display line. All fields are filled in and correct except for
- * y and nextPtr.
- *
- * Side effects:
- * Storage is allocated for the new DLine.
- *
- *----------------------------------------------------------------------
- */
- static DLine *
- LayoutDLine(textPtr, indexPtr)
- TkText *textPtr; /* Overall information about text widget. */
- TkTextIndex *indexPtr; /* Beginning of display line. May not
- * necessarily point to a character segment. */
- {
- register DLine *dlPtr; /* New display line. */
- TkTextSegment *segPtr; /* Current segment in text. */
- TkTextDispChunk *lastChunkPtr; /* Last chunk allocated so far
- * for line. */
- TkTextDispChunk *chunkPtr; /* Current chunk. */
- TkTextIndex curIndex;
- TkTextDispChunk *breakChunkPtr; /* Chunk containing best word break
- * point, if any. */
- TkTextIndex breakIndex; /* Index of first character in
- * breakChunkPtr. */
- int breakByteOffset; /* Byte offset of character within
- * breakChunkPtr just to right of best
- * break point. */
- int noCharsYet; /* Non-zero means that no characters
- * have been placed on the line yet. */
- int justify; /* How to justify line: taken from
- * style for the first character in
- * line. */
- int jIndent; /* Additional indentation (beyond
- * margins) due to justification. */
- int rMargin; /* Right margin width for line. */
- TkWrapMode wrapMode; /* Wrap mode to use for this line. */
- int x = 0, maxX = 0; /* Initializations needed only to
- * stop compiler warnings. */
- int wholeLine; /* Non-zero means this display line
- * runs to the end of the text line. */
- int tabIndex; /* Index of the current tab stop. */
- int gotTab; /* Non-zero means the current chunk
- * contains a tab. */
- TkTextDispChunk *tabChunkPtr; /* Pointer to the chunk containing
- * the previous tab stop. */
- int maxBytes; /* Maximum number of bytes to
- * include in this chunk. */
- TkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
- * style for the first character on
- * line. */
- int tabSize; /* Number of pixels consumed by current
- * tab stop. */
- TkTextDispChunk *lastCharChunkPtr; /* Pointer to last chunk in display
- * lines with numBytes > 0. Used to
- * drop 0-sized chunks from the end
- * of the line. */
- int byteOffset, ascent, descent, code, elide, elidesize;
- StyleValues *sValuePtr;
- /*
- * Create and initialize a new DLine structure.
- */
- dlPtr = (DLine *) ckalloc(sizeof(DLine));
- dlPtr->index = *indexPtr;
- dlPtr->byteCount = 0;
- dlPtr->y = 0;
- dlPtr->oldY = -1;
- dlPtr->height = 0;
- dlPtr->baseline = 0;
- dlPtr->chunkPtr = NULL;
- dlPtr->nextPtr = NULL;
- dlPtr->flags = NEW_LAYOUT;
- /*
- * Special case entirely elide line as there may be 1000s or more
- */
- elide = TkTextIsElided(textPtr, indexPtr); /* save a malloc */
- if (elide && indexPtr->byteIndex==0) {
- maxBytes = 0;
- for (segPtr = indexPtr->linePtr->segPtr;
- elide && (segPtr != NULL);
- segPtr = segPtr->nextPtr) {
- if ((elidesize = segPtr->size) > 0) {
- maxBytes += elidesize;
- /*
- * If have we have a tag toggle, there is a chance
- * that invisibility state changed, so bail out
- */
- } else if ((segPtr->typePtr == &tkTextToggleOffType)
- || (segPtr->typePtr == &tkTextToggleOnType)) {
- if (segPtr->body.toggle.tagPtr->elideString != NULL) {
- elide = (segPtr->typePtr == &tkTextToggleOffType)
- ^ segPtr->body.toggle.tagPtr->elide;
- }
- }
- }
- if (elide) {
- dlPtr->byteCount = maxBytes;
- dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0;
- return dlPtr;
- }
- }
- /*
- * Each iteration of the loop below creates one TkTextDispChunk for
- * the new display line. The line will always have at least one
- * chunk (for the newline character at the end, if there's nothing
- * else available).
- */
- curIndex = *indexPtr;
- lastChunkPtr = NULL;
- chunkPtr = NULL;
- noCharsYet = 1;
- elide = 0;
- breakChunkPtr = NULL;
- breakByteOffset = 0;
- justify = TK_JUSTIFY_LEFT;
- tabIndex = -1;
- tabChunkPtr = NULL;
- tabArrayPtr = NULL;
- rMargin = 0;
- wrapMode = TEXT_WRAPMODE_CHAR;
- tabSize = 0;
- 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 (byteOffset = curIndex.byteIndex, segPtr = curIndex.linePtr->segPtr;
- (byteOffset > 0) && (byteOffset >= segPtr->size);
- byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
- /* Empty loop body. */
- }
- while (segPtr != NULL) {
- /*
- * Every line still gets at least one chunk due to expectations
- * in the rest of the code, but we are able to skip elided portions
- * of the line quickly.
- * If current chunk is elided and last chunk was too, coalese
- */
- if (elide && (lastChunkPtr != NULL)
- && (lastChunkPtr->displayProc == NULL /*ElideDisplayProc*/)) {
- if ((elidesize = segPtr->size - byteOffset) > 0) {
- curIndex.byteIndex += elidesize;
- lastChunkPtr->numBytes += elidesize;
- breakByteOffset = lastChunkPtr->breakIndex = lastChunkPtr->numBytes;
- /*
- * If have we have a tag toggle, there is a chance
- * that invisibility state changed, so bail out
- */
- } else if ((segPtr->typePtr == &tkTextToggleOffType)
- || (segPtr->typePtr == &tkTextToggleOnType)) {
- if (segPtr->body.toggle.tagPtr->elideString != NULL) {
- elide = (segPtr->typePtr == &tkTextToggleOffType)
- ^ segPtr->body.toggle.tagPtr->elide;
- }
- }
- byteOffset = 0;
- segPtr = segPtr->nextPtr;
- if (segPtr == NULL && chunkPtr != NULL) {
- ckfree((char *) chunkPtr);
- }
- continue;
- }
- if (segPtr->typePtr->layoutProc == NULL) {
- segPtr = segPtr->nextPtr;
- byteOffset = 0;
- continue;
- }
- if (chunkPtr == NULL) {
- chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
- chunkPtr->nextPtr = NULL;
- }
- chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
- elide = chunkPtr->stylePtr->sValuePtr->elide;
- /*
- * 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.byteIndex == 0)
- ? chunkPtr->stylePtr->sValuePtr->lMargin1
- : chunkPtr->stylePtr->sValuePtr->lMargin2);
- if (wrapMode == TEXT_WRAPMODE_NONE) {
- maxX = -1;
- } 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;
- maxBytes = segPtr->size - byteOffset;
- if (!elide && justify == TK_JUSTIFY_LEFT) {
- if (segPtr->typePtr == &tkTextCharType) {
- char *p;
- for (p = segPtr->body.chars + byteOffset; *p != 0; p++) {
- if (*p == 't') {
- maxBytes = (p + 1 - segPtr->body.chars) - byteOffset;
- gotTab = 1;
- break;
- }
- }
- }
- }
- chunkPtr->x = x;
- if (elide && maxBytes) {
- /* don't free style here, as other code expects to be able to do that */
- /*breakByteOffset =*/ chunkPtr->breakIndex = chunkPtr->numBytes = maxBytes;
- chunkPtr->width = 0;
- chunkPtr->minAscent = chunkPtr->minDescent = chunkPtr->minHeight = 0;
- /* would just like to point to canonical empty chunk */
- chunkPtr->displayProc = (Tk_ChunkDisplayProc *) NULL;
- chunkPtr->undisplayProc = (Tk_ChunkUndisplayProc *) NULL;
- chunkPtr->measureProc = ElideMeasureProc;
- chunkPtr->bboxProc = ElideBboxProc;
- code = 1;
- } else
- code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
- byteOffset, maxX-tabSize, maxBytes, 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;
- byteOffset = 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->numBytes > 0) {
- noCharsYet = 0;
- lastCharChunkPtr = chunkPtr;
- }
- if (lastChunkPtr == NULL) {
- dlPtr->chunkPtr = chunkPtr;
- } else {
- lastChunkPtr->nextPtr = chunkPtr;
- }
- lastChunkPtr = chunkPtr;
- x += chunkPtr->width;
- if (chunkPtr->breakIndex > 0) {
- breakByteOffset = chunkPtr->breakIndex;
- breakIndex = curIndex;
- breakChunkPtr = chunkPtr;
- }
- if (chunkPtr->numBytes != maxBytes) {
- 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 ((maxX >= 0) && (tabSize >= maxX - x)) {
- break;
- }
- }
- curIndex.byteIndex += chunkPtr->numBytes;
- byteOffset += chunkPtr->numBytes;
- if (byteOffset >= segPtr->size) {
- byteOffset = 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;
- breakByteOffset = breakChunkPtr->numBytes;
- }
- if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
- || (breakByteOffset != lastChunkPtr->numBytes))) {
- 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 (breakByteOffset != breakChunkPtr->numBytes) {
- (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
- segPtr = TkTextIndexToSeg(&breakIndex, &byteOffset);
- (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
- segPtr, byteOffset, maxX, breakByteOffset, 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 bytes. 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 == TEXT_WRAPMODE_NONE) {
- 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->byteCount += chunkPtr->numBytes;
- 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.byteIndex == 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 void
- UpdateDisplayInfo(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.
- */
- if (index.byteIndex == dlPtr->index.byteIndex) {
- /*
- * Case (a) -- can use existing display line as-is.
- */
- if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
- && (prevPtr->flags & (NEW_LAYOUT))) {
- dlPtr->oldY = -1;
- }
- goto lineOK;
- }
- if (index.byteIndex < dlPtr->index.byteIndex) {
- goto makeNewDLine;
- }
- /*
- * Case (c) -- dlPtr is useless. Discard it and start
- * again with the next display line.
- */
- newPtr = dlPtr->nextPtr;
- FreeDLines(textPtr, dlPtr, newPtr, 0);
- dlPtr = newPtr;
- if (prevPtr != NULL) {
- prevPtr->nextPtr = newPtr;
- } else {
- dInfoPtr->dLinePtr = newPtr;
- }
- continue;
- }
- /*
- * Advance to the start of the next line.
- */
- lineOK:
- dlPtr->y = y;
- y += dlPtr->height;
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
- prevPtr = dlPtr;
- dlPtr = dlPtr->nextPtr;
- /*
- * If we switched text lines, delete any DLines left for the
- * old text line.
- */
- if (index.linePtr != prevPtr->index.linePtr) {
- register DLine *nextPtr;
- nextPtr = dlPtr;
- while ((nextPtr != NULL)
- && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
- nextPtr = nextPtr->nextPtr;
- }
- if (nextPtr != dlPtr) {
- FreeDLines(textPtr, dlPtr, nextPtr, 0);
- prevPtr->nextPtr = nextPtr;
- dlPtr = nextPtr;
- }
- }
- /*
- * It's important to have the following check here rather than in
- * the while statement for the loop, so that there's always at least
- * one DLine generated, regardless of how small the window is. This
- * keeps a lot of other code from breaking.
- */
- if (y >= maxY) {
- break;
- }
- }
- /*
- * Delete any DLine structures that don't fit on the screen.
- */
- FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
- /*
- *--------------------------------------------------------------
- * If there is extra space at the bottom of the window (because
- * we've hit the end of the text), then bring in more lines at
- * the top of the window, if there are any, to fill in the view.
- *--------------------------------------------------------------
- */
- if (y < maxY) {
- int lineNum, spaceLeft, bytesToCount;
- DLine *lowestPtr;
- /*
- * Layout an entire text line (potentially > 1 display line),
- * then link in as many display lines as fit without moving
- * the bottom line out of the window. Repeat this until
- * all the extra space has been used up or we've reached the
- * beginning of the text.
- */
- spaceLeft = maxY - y;
- lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
- bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
- if (bytesToCount == 0) {
- bytesToCount = INT_MAX;
- lineNum--;
- }
- for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
- index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
- index.byteIndex = 0;
- lowestPtr = NULL;
- do {
- dlPtr = LayoutDLine(textPtr, &index);
- dlPtr->nextPtr = lowestPtr;
- lowestPtr = dlPtr;
- if (dlPtr->length == 0 && dlPtr->height == 0) { bytesToCount--; break; } /* elide */
- TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
- bytesToCount -= dlPtr->byteCount;
- } while ((bytesToCount > 0)
- && (index.linePtr == lowestPtr->index.linePtr));
- /*
- * Scan through the display lines from the bottom one up to
- * the top one.
- */
- while (lowestPtr != NULL) {
- dlPtr = lowestPtr;
- spaceLeft -= dlPtr->height;
- if (spaceLeft < 0) {
- break;
- }
- lowestPtr = dlPtr->nextPtr;
- dlPtr->nextPtr = dInfoPtr->dLinePtr;
- dInfoPtr->dLinePtr = dlPtr;
- if (tkTextDebug) {
- char string[TK_POS_CHARS];
- TkTextPrintIndex(&dlPtr->index, string);
- Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
- (char *) NULL, string,
- TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
- }
- }
- FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
- bytesToCount = INT_MAX;
- }
- /*
- * Now we're all done except that the y-coordinates in all the
- * DLines are wrong and the top index for the text is wrong.
- * Update them.
- */
- textPtr->topIndex = dInfoPtr->dLinePtr->index;
- y = dInfoPtr->y;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- if (y > dInfoPtr->maxY) {
- panic("Added too many new lines in UpdateDisplayInfo");
- }
- dlPtr->y = y;
- y += dlPtr->height;
- }
- }
- /*
- *--------------------------------------------------------------
- * If the old top or bottom line has scrolled elsewhere on the
- * screen, we may not be able to re-use its old contents by
- * copying bits (e.g., a beveled edge that was drawn when it was
- * at the top or bottom won't be drawn when the line is in the
- * middle and its neighbor has a matching background). Similarly,
- * if the new top or bottom line came from somewhere else on the
- * screen, we may not be able to copy the old bits.
- *--------------------------------------------------------------
- */
- dlPtr = dInfoPtr->dLinePtr;
- if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
- dlPtr->oldY = -1;
- }
- while (1) {
- if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
- && (dlPtr->flags & HAS_3D_BORDER)) {
- dlPtr->oldY = -1;
- }
- if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
- && (dlPtr->flags & HAS_3D_BORDER)) {
- dlPtr->oldY = -1;
- }
- if (dlPtr->nextPtr == NULL) {
- if ((dlPtr->flags & HAS_3D_BORDER)
- && !(dlPtr->flags & BOTTOM_LINE)) {
- dlPtr->oldY = -1;
- }
- dlPtr->flags &= ~TOP_LINE;
- dlPtr->flags |= BOTTOM_LINE;
- break;
- }
- dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
- dlPtr = dlPtr->nextPtr;
- }
- dInfoPtr->dLinePtr->flags |= TOP_LINE;
- /*
- * Arrange for scrollbars to be updated.
- */
- textPtr->flags |= UPDATE_SCROLLBARS;
- /*
- *--------------------------------------------------------------
- * Deal with horizontal scrolling:
- * 1. If there's empty space to the right of the longest line,
- * shift the screen to the right to fill in the empty space.
- * 2. If the desired horizontal scroll position has changed,
- * force a full redisplay of all the lines in the widget.
- * 3. If the wrap mode isn't "none" then re-scroll to the base
- * position.
- *--------------------------------------------------------------
- */
- dInfoPtr->maxLength = 0;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- if (dlPtr->length > dInfoPtr->maxLength) {
- dInfoPtr->maxLength = dlPtr->length;
- }
- }
- maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
- + textPtr->charWidth - 1)/textPtr->charWidth;
- if (dInfoPtr->newByteOffset > maxOffset) {
- dInfoPtr->newByteOffset = maxOffset;
- }
- if (dInfoPtr->newByteOffset < 0) {
- dInfoPtr->newByteOffset = 0;
- }
- pixelOffset = dInfoPtr->newByteOffset * textPtr->charWidth;
- if (pixelOffset != dInfoPtr->curPixelOffset) {
- dInfoPtr->curPixelOffset = pixelOffset;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- dlPtr->oldY = -1;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * FreeDLines --
- *
- * This procedure is called to free up all of the resources
- * associated with one or more DLine structures.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory gets freed and various other resources are released.
- *
- *----------------------------------------------------------------------
- */
- static void
- FreeDLines(textPtr, firstPtr, lastPtr, unlink)
- TkText *textPtr; /* Information about overall text
- * widget. */
- register DLine *firstPtr; /* Pointer to first DLine to free up. */
- DLine *lastPtr; /* Pointer to DLine just after last
- * one to free (NULL means everything
- * starting with firstPtr). */
- int unlink; /* 1 means DLines are currently linked
- * into the list rooted at
- * textPtr->dInfoPtr->dLinePtr and
- * they have to be unlinked. 0 means
- * just free without unlinking. */
- {
- register TkTextDispChunk *chunkPtr, *nextChunkPtr;
- register DLine *nextDLinePtr;
- if (unlink) {
- if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
- textPtr->dInfoPtr->dLinePtr = lastPtr;
- } else {
- register DLine *prevPtr;
- for (prevPtr = textPtr->dInfoPtr->dLinePtr;
- prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
- /* Empty loop body. */
- }
- prevPtr->nextPtr = lastPtr;
- }
- }
- while (firstPtr != lastPtr) {
- nextDLinePtr = firstPtr->nextPtr;
- for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
- chunkPtr = nextChunkPtr) {
- if (chunkPtr->undisplayProc != NULL) {
- (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
- }
- FreeStyle(textPtr, chunkPtr->stylePtr);
- nextChunkPtr = chunkPtr->nextPtr;
- ckfree((char *) chunkPtr);
- }
- ckfree((char *) firstPtr);
- firstPtr = nextDLinePtr;
- }
- textPtr->dInfoPtr->dLinesInvalidated = 1;
- }
- /*
- *----------------------------------------------------------------------
- *
- * DisplayDLine --
- *
- * This procedure is invoked to draw a single line on the
- * screen.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The line given by dlPtr is drawn at its correct position in
- * textPtr's window. Note that this is one *display* line, not
- * one *text* line.
- *
- *----------------------------------------------------------------------
- */
- static void
- DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
- TkText *textPtr; /* Text widget in which to draw line. */
- register DLine *dlPtr; /* Information about line to draw. */
- DLine *prevPtr; /* Line just before one to draw, or NULL
- * if dlPtr is the top line. */
- Pixmap pixmap; /* Pixmap to use for double-buffering.
- * Caller must make sure it's large enough
- * to hold line. */
- {
- register TkTextDispChunk *chunkPtr;
- TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- Display *display;
- int height, x;
- #ifndef TK_NO_DOUBLE_BUFFERING
- const int y = 0;
- #else
- const int y = dlPtr->y;
- #endif /* TK_NO_DOUBLE_BUFFERING */
- if (dlPtr->chunkPtr == NULL) return;
- display = Tk_Display(textPtr->tkwin);
- height = dlPtr->height;
- if ((height + dlPtr->y) > dInfoPtr->maxY) {
- height = dInfoPtr->maxY - dlPtr->y;
- }
- #ifdef TK_NO_DOUBLE_BUFFERING
- TkpClipDrawableToRect(display, pixmap, dInfoPtr->x, y,
- dInfoPtr->maxX - dInfoPtr->x, height);
- #endif /* TK_NO_DOUBLE_BUFFERING */
- /*
- * First, clear the area of the line to the background color for the
- * text widget.
- */
- Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, y,
- Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
- /*
- * Next, draw background information for the whole line.
- */
- DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
- /*
- * Make another pass through all of the chunks to redraw the
- * insertion cursor, if it is visible on this line. Must do
- * it here rather than in the foreground pass below because
- * otherwise a wide insertion cursor will obscure the character
- * to its left.
- */
- if (textPtr->state == TK_STATE_NORMAL) {
- for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
- chunkPtr = chunkPtr->nextPtr) {
- x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
- if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
- (*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
- dlPtr->y + dlPtr->spaceAbove);
- }
- }
- }
- /*
- * Make yet another pass through all of the chunks to redraw all of
- * foreground information. Note: we have to call the displayProc
- * even for chunks that are off-screen. This is needed, for
- * example, so that embedded windows can be unmapped in this case.
- * Conve
- */
- for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
- chunkPtr = chunkPtr->nextPtr) {
- if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
- /*
- * Already displayed the insertion cursor above. Don't
- * do it again here.
- */
- continue;
- }
- x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
- if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
- /*
- * Note: we have to call the displayProc even for chunks
- * that are off-screen. This is needed, for example, so
- * that embedded windows can be unmapped in this case.
- * Display the chunk at a coordinate that can be clearly
- * identified by the displayProc as being off-screen to
- * the left (the displayProc may not be able to tell if
- * something is off to the right).
- */
- if (chunkPtr->displayProc != NULL)
- (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
- y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
- dlPtr->y + dlPtr->spaceAbove);
- } else {
- /* don't call if elide. This tax ok since not very many visible DLine's in
- an area, but potentially many elide ones */
- if (chunkPtr->displayProc != NULL)
- (*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
- dlPtr->y + dlPtr->spaceAbove);
- }
- if (dInfoPtr->dLinesInvalidated) {
- return;
- }
- }
- #ifndef TK_NO_DOUBLE_BUFFERING
- /*
- * Copy the pixmap onto the screen. If this is the last line on
- * the screen then copy a piece of the line, so that it doesn't
- * overflow into the border area. Another special trick: copy the
- * padding area to the left of the line; this is because the
- * insertion cursor sometimes overflows onto that area and we want
- * to get as much of the cursor as possible.
- */
- XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
- dInfoPtr->x, y, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
- (unsigned) height, dInfoPtr->x, dlPtr->y);
- #else
- TkpClipDrawableToRect(display, pixmap, 0, 0, -1, -1);
- #endif /* TK_NO_DOUBLE_BUFFERING */
- linesRedrawn++;
- }
- /*
- *--------------------------------------------------------------
- *
- * DisplayLineBackground --
- *
- * This procedure is called to fill in the background for
- * a display line. It draws 3D borders cleverly so that
- * adjacent chunks with the same style (whether on the same
- * line or different lines) have a single 3D border around
- * the whole region.
- *
- * Results:
- * There is no return value. Pixmap is filled in with background
- * information for dlPtr.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static void
- DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
- TkText *textPtr; /* Text widget containing line. */
- register DLine *dlPtr; /* Information about line to draw. */
- DLine *prevPtr; /* Line just above dlPtr, or NULL if dlPtr
- * is the top-most line in the window. */
- Pixmap pixmap; /* Pixmap to use for double-buffering.
- * Caller must make sure it's large enough
- * to hold line. Caller must also have
- * filled it with the background color for
- * the widget. */
- {
- TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- TkTextDispChunk *chunkPtr; /* Pointer to chunk in the current line. */
- TkTextDispChunk *chunkPtr2; /* Pointer to chunk in the line above or
- * below the current one. NULL if we're to
- * the left of or to the right of the chunks
- * in the line. */
- TkTextDispChunk *nextPtr2; /* Next chunk after chunkPtr2 (it's not the
- * same as chunkPtr2->nextPtr in the case
- * where chunkPtr2 is NULL because the line
- * is indented). */
- int leftX; /* The left edge of the region we're
- * currently working on. */
- int leftXIn; /* 1 means beveled edge at leftX slopes right
- * as it goes down, 0 means it slopes left
- * as it goes down. */
- int rightX; /* Right edge of chunkPtr. */
- int rightX2; /* Right edge of chunkPtr2. */
- int matchLeft; /* Does the style of this line match that
- * of its neighbor just to the left of
- * the current x coordinate? */
- int matchRight; /* Does line's style match its neighbor
- * just to the right of the current x-coord? */
- int minX, maxX, xOffset;
- StyleValues *sValuePtr;
- Display *display;
- #ifndef TK_NO_DOUBLE_BUFFERING
- const int y = 0;
- #else
- const int y = dlPtr->y;
- #endif /* TK_NO_DOUBLE_BUFFERING */
- /*
- * Pass 1: scan through dlPtr from left to right. For each range of
- * chunks with the same style, draw the main background for the style
- * plus the vertical parts of the 3D borders (the left and right
- * edges).
- */
- display = Tk_Display(textPtr->tkwin);
- minX = dInfoPtr->curPixelOffset;
- xOffset = dInfoPtr->x - minX;
- maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
- chunkPtr = dlPtr->chunkPtr;
- /*
- * Note A: in the following statement, and a few others later in
- * this file marked with "See Note A above", the right side of the
- * assignment was replaced with 0 on 6/18/97. This has the effect
- * of highlighting the empty space to the left of a line whenever
- * the leftmost character of the line is highlighted. This way,
- * multi-line highlights always line up along their left edges.
- * However, this may look funny in the case where a single word is
- * highlighted. To undo the change, replace "leftX = 0" with "leftX
- * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
- * here and at all the marked points below. This restores the old
- * behavior where empty space to the left of a line is not
- * highlighted, leaving a ragged left edge for multi-line
- * highlights.
- */
- leftX = 0;
- for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
- if ((chunkPtr->nextPtr != NULL)
- && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
- chunkPtr->stylePtr)) {
- continue;
- }
- sValuePtr = chunkPtr->stylePtr->sValuePtr;
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- if (chunkPtr->stylePtr->bgGC != None) {
- /* Not visible - bail out now */
- if (rightX + xOffset <= 0) {
- leftX = rightX;
- continue;
- }
- /*
- * Trim the start position for drawing to be no further away than
- * -borderWidth. The reason is that on many X servers drawing from
- * -32768 (or less) to +something simply does not display
- * correctly. [Patch #541999]
- */
- if ((leftX + xOffset) < -(sValuePtr->borderWidth)) {
- leftX = -sValuePtr->borderWidth - xOffset;
- }
- if ((rightX - leftX) > 32767) {
- rightX = leftX + 32767;
- }
- XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
- leftX + xOffset, y, (unsigned int) (rightX - leftX),
- (unsigned int) dlPtr->height);
- if (sValuePtr->relief != TK_RELIEF_FLAT) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, y, sValuePtr->borderWidth,
- dlPtr->height, 1, sValuePtr->relief);
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX - sValuePtr->borderWidth + xOffset,
- y, sValuePtr->borderWidth, dlPtr->height, 0,
- sValuePtr->relief);
- }
- }
- leftX = rightX;
- }
- /*
- * Pass 2: draw the horizontal bevels along the top of the line. To
- * do this, scan through dlPtr from left to right while simultaneously
- * scanning through the line just above dlPtr. ChunkPtr2 and nextPtr2
- * refer to two adjacent chunks in the line above.
- */
- chunkPtr = dlPtr->chunkPtr;
- leftX = 0; /* See Note A above. */
- leftXIn = 1;
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- chunkPtr2 = NULL;
- if (prevPtr != NULL && prevPtr->chunkPtr != NULL) {
- /*
- * Find the chunk in the previous line that covers leftX.
- */
- nextPtr2 = prevPtr->chunkPtr;
- rightX2 = 0; /* See Note A above. */
- while (rightX2 <= leftX) {
- chunkPtr2 = nextPtr2;
- if (chunkPtr2 == NULL) {
- break;
- }
- nextPtr2 = chunkPtr2->nextPtr;
- rightX2 = chunkPtr2->x + chunkPtr2->width;
- if (nextPtr2 == NULL) {
- rightX2 = INT_MAX;
- }
- }
- } else {
- nextPtr2 = NULL;
- rightX2 = INT_MAX;
- }
- while (leftX < maxX) {
- matchLeft = (chunkPtr2 != NULL)
- && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
- sValuePtr = chunkPtr->stylePtr->sValuePtr;
- if (rightX <= rightX2) {
- /*
- * The chunk in our line is about to end. If its style
- * changes then draw the bevel for the current style.
- */
- if ((chunkPtr->nextPtr == NULL)
- || !SAME_BACKGROUND(chunkPtr->stylePtr,
- chunkPtr->nextPtr->stylePtr)) {
- if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
- sValuePtr->border, leftX + xOffset, y,
- rightX - leftX, sValuePtr->borderWidth, leftXIn,
- 1, 1, sValuePtr->relief);
- }
- leftX = rightX;
- leftXIn = 1;
- /*
- * If the chunk in the line above is also ending at
- * the same point then advance to the next chunk in
- * that line.
- */
- if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
- goto nextChunk2;
- }
- }
- chunkPtr = chunkPtr->nextPtr;
- if (chunkPtr == NULL) {
- break;
- }
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- continue;
- }
- /*
- * The chunk in the line above is ending at an x-position where
- * there is no change in the style of the current line. If the
- * style above matches the current line on one side of the change
- * but not on the other, we have to draw an L-shaped piece of
- * bevel.
- */
- matchRight = (nextPtr2 != NULL)
- && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
- if (matchLeft && !matchRight) {
- if (sValuePtr->relief != TK_RELIEF_FLAT) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX2 - sValuePtr->borderWidth + xOffset, y,
- sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
- sValuePtr->relief);
- }
- leftX = rightX2 - sValuePtr->borderWidth;
- leftXIn = 0;
- } else if (!matchLeft && matchRight
- && (sValuePtr->relief != TK_RELIEF_FLAT)) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX2 + xOffset, y, sValuePtr->borderWidth,
- sValuePtr->borderWidth, 1, sValuePtr->relief);
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, y, rightX2 + sValuePtr->borderWidth -
- leftX, sValuePtr->borderWidth, leftXIn, 0, 1,
- sValuePtr->relief);
- }
- nextChunk2:
- chunkPtr2 = nextPtr2;
- if (chunkPtr2 == NULL) {
- rightX2 = INT_MAX;
- } else {
- nextPtr2 = chunkPtr2->nextPtr;
- rightX2 = chunkPtr2->x + chunkPtr2->width;
- if (nextPtr2 == NULL) {
- rightX2 = INT_MAX;
- }
- }
- }
- /*
- * Pass 3: draw the horizontal bevels along the bottom of the line.
- * This uses the same approach as pass 2.
- */
- chunkPtr = dlPtr->chunkPtr;
- leftX = 0; /* See Note A above. */
- leftXIn = 0;
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- chunkPtr2 = NULL;
- if (dlPtr->nextPtr != NULL && dlPtr->nextPtr->chunkPtr != NULL) {
- /*
- * Find the chunk in the previous line that covers leftX.
- */
- nextPtr2 = dlPtr->nextPtr->chunkPtr;
- rightX2 = 0; /* See Note A above. */
- while (rightX2 <= leftX) {
- chunkPtr2 = nextPtr2;
- if (chunkPtr2 == NULL) {
- break;
- }
- nextPtr2 = chunkPtr2->nextPtr;
- rightX2 = chunkPtr2->x + chunkPtr2->width;
- if (nextPtr2 == NULL) {
- rightX2 = INT_MAX;
- }
- }
- } else {
- nextPtr2 = NULL;
- rightX2 = INT_MAX;
- }
- while (leftX < maxX) {
- matchLeft = (chunkPtr2 != NULL)
- && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
- sValuePtr = chunkPtr->stylePtr->sValuePtr;
- if (rightX <= rightX2) {
- if ((chunkPtr->nextPtr == NULL)
- || !SAME_BACKGROUND(chunkPtr->stylePtr,
- chunkPtr->nextPtr->stylePtr)) {
- if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
- sValuePtr->border, leftX + xOffset,
- y + dlPtr->height - sValuePtr->borderWidth,
- rightX - leftX, sValuePtr->borderWidth, leftXIn,
- 0, 0, sValuePtr->relief);
- }
- leftX = rightX;
- leftXIn = 0;
- if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
- goto nextChunk2b;
- }
- }
- chunkPtr = chunkPtr->nextPtr;
- if (chunkPtr == NULL) {
- break;
- }
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- continue;
- }
- matchRight = (nextPtr2 != NULL)
- && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
- if (matchLeft && !matchRight) {
- if (sValuePtr->relief != TK_RELIEF_FLAT) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX2 - sValuePtr->borderWidth + xOffset,
- y + dlPtr->height - sValuePtr->borderWidth,
- sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
- sValuePtr->relief);
- }
- leftX = rightX2 - sValuePtr->borderWidth;
- leftXIn = 1;
- } else if (!matchLeft && matchRight
- && (sValuePtr->relief != TK_RELIEF_FLAT)) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX2 + xOffset, y + dlPtr->height -
- sValuePtr->borderWidth, sValuePtr->borderWidth,
- sValuePtr->borderWidth, 1, sValuePtr->relief);
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, y + dlPtr->height -
- sValuePtr->borderWidth, rightX2 + sValuePtr->borderWidth -
- leftX, sValuePtr->borderWidth, leftXIn, 1, 0,
- sValuePtr->relief);
- }
- nextChunk2b:
- chunkPtr2 = nextPtr2;
- if (chunkPtr2 == NULL) {
- rightX2 = INT_MAX;
- } else {
- nextPtr2 = chunkPtr2->nextPtr;
- rightX2 = chunkPtr2->x + chunkPtr2->width;
- if (nextPtr2 == NULL) {
- rightX2 = INT_MAX;
- }
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * DisplayText --
- *
- * This procedure is invoked as a when-idle handler to update the
- * display. It only redisplays the parts of the text widget that
- * are out of date.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information is redrawn on the screen.
- *
- *----------------------------------------------------------------------
- */
- static void
- DisplayText(clientData)
- ClientData clientData; /* Information about widget. */
- {
- register TkText *textPtr = (TkText *) clientData;
- TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- Tk_Window tkwin;
- register DLine *dlPtr;
- DLine *prevPtr;
- Pixmap pixmap;
- int maxHeight, borders;
- int bottomY = 0; /* Initialization needed only to stop
- * compiler warnings. */
- Tcl_Interp *interp;
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- return;
- }
- interp = textPtr->interp;
- Tcl_Preserve((ClientData) interp);
- if (tkTextDebug) {
- Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
- TCL_GLOBAL_ONLY);
- }
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- goto end;
- }
- if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
- || (dInfoPtr->maxY <= dInfoPtr->y)) {
- UpdateDisplayInfo(textPtr);
- dInfoPtr->flags &= ~REDRAW_PENDING;
- goto doScrollbars;
- }
- numRedisplays++;
- if (tkTextDebug) {
- Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
- TCL_GLOBAL_ONLY);
- }
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- goto end;
- }
- /*
- * Choose a new current item if that is needed (this could cause
- * event handlers to be invoked, hence the preserve/release calls
- * and the loop, since the handlers could conceivably necessitate
- * yet another current item calculation). The tkwin check is because
- * the whole window could go away in the Tcl_Release call.
- */
- while (dInfoPtr->flags & REPICK_NEEDED) {
- Tcl_Preserve((ClientData) textPtr);
- dInfoPtr->flags &= ~REPICK_NEEDED;
- TkTextPickCurrent(textPtr, &textPtr->pickEvent);
- tkwin = textPtr->tkwin;
- Tcl_Release((ClientData) textPtr);
- if (tkwin == NULL) {
- goto end;
- }
- }
- /*
- * First recompute what's supposed to be displayed.
- */
- UpdateDisplayInfo(textPtr);
- dInfoPtr->dLinesInvalidated = 0;
- /*
- * See if it's possible to bring some parts of the screen up-to-date
- * by scrolling (copying from other parts of the screen).
- */
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
- register DLine *dlPtr2;
- int offset, height, y, oldY;
- TkRegion damageRgn;
- if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
- || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
- continue;
- }
- /*
- * This line is already drawn somewhere in the window so it only
- * needs to be copied to its new location. See if there's a group
- * of lines that can all be copied together.
- */
- offset = dlPtr->y - dlPtr->oldY;
- height = dlPtr->height;
- y = dlPtr->y;
- for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
- dlPtr2 = dlPtr2->nextPtr) {
- if ((dlPtr2->oldY == -1)
- || ((dlPtr2->oldY + offset) != dlPtr2->y)
- || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
- break;
- }
- height += dlPtr2->height;
- }
- /*
- * Reduce the height of the area being copied if necessary to
- * avoid overwriting the border area.
- */
- if ((y + height) > dInfoPtr->maxY) {
- height = dInfoPtr->maxY -y;
- }
- oldY = dlPtr->oldY;
- /*
- * Update the lines we are going to scroll to show that they
- * have been copied.
- */
- while (1) {
- dlPtr->oldY = dlPtr->y;
- if (dlPtr->nextPtr == dlPtr2) {
- break;
- }
- dlPtr = dlPtr->nextPtr;
- }
- /*
- * Scan through the lines following the copied ones to see if
- * we are going to overwrite them with the copy operation.
- * If so, mark them for redisplay.
- */
- for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
- if ((dlPtr2->oldY != -1)
- && ((dlPtr2->oldY + dlPtr2->height) > y)
- && (dlPtr2->oldY < (y + height))) {
- dlPtr2->oldY = -1;
- }
- }
- /*
- * Now scroll the lines. This may generate damage which we
- * handle by calling TextInvalidateRegion to mark the display
- * blocks as stale.
- */
- damageRgn = TkCreateRegion();
- if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
- dInfoPtr->x, oldY,
- (dInfoPtr->maxX - dInfoPtr->x), height,
- 0, y - oldY, damageRgn)) {
- TextInvalidateRegion(textPtr, damageRgn);
- }
- numCopies++;
- TkDestroyRegion(damageRgn);
- }
- /*
- * Clear the REDRAW_PENDING flag here. This is actually pretty
- * tricky. We want to wait until *after* doing the scrolling,
- * since that could generate more areas to redraw and don't
- * want to reschedule a redisplay for them. On the other hand,
- * we can't wait until after all the redisplaying, because the
- * act of redisplaying could actually generate more redisplays
- * (e.g. in the case of a nested window with event bindings triggered
- * by redisplay).
- */
- dInfoPtr->flags &= ~REDRAW_PENDING;
- /*
- * Redraw the borders if that's needed.
- */
- if (dInfoPtr->flags & REDRAW_BORDERS) {
- if (tkTextDebug) {
- Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
- TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
- }
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- goto end;
- }
- Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border, textPtr->highlightWidth,
- textPtr->highlightWidth,
- Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
- Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
- textPtr->borderWidth, textPtr->relief);
- if (textPtr->highlightWidth != 0) {
- GC fgGC, bgGC;
-
- bgGC = Tk_GCForColor(textPtr->highlightBgColorPtr,
- Tk_WindowId(textPtr->tkwin));
- if (textPtr->flags & GOT_FOCUS) {
- fgGC = Tk_GCForColor(textPtr->highlightColorPtr,
- Tk_WindowId(textPtr->tkwin));
- TkpDrawHighlightBorder(textPtr->tkwin, fgGC, bgGC,
- textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
- } else {
- TkpDrawHighlightBorder(textPtr->tkwin, bgGC, bgGC,
- textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
- }
- }
- borders = textPtr->borderWidth + textPtr->highlightWidth;
- if (textPtr->padY > 0) {
- Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border, borders, borders,
- Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
- 0, TK_RELIEF_FLAT);
- Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border, borders,
- Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
- Tk_Width(textPtr->tkwin) - 2*borders,
- textPtr->padY, 0, TK_RELIEF_FLAT);
- }
- if (textPtr->padX > 0) {
- Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border, borders, borders + textPtr->padY,
- textPtr->padX,
- Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
- 0, TK_RELIEF_FLAT);
- Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border,
- Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
- borders + textPtr->padY, textPtr->padX,
- Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
- 0, TK_RELIEF_FLAT);
- }
- dInfoPtr->flags &= ~REDRAW_BORDERS;
- }
- /*
- * Now we have to redraw the lines that couldn't be updated by
- * scrolling. First, compute the height of the largest line and
- * allocate an off-screen pixmap to use for double-buffered
- * displays.
- */
- maxHeight = -1;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
- maxHeight = dlPtr->height;
- }
- bottomY = dlPtr->y + dlPtr->height;
- }
- if (maxHeight > dInfoPtr->maxY) {
- maxHeight = dInfoPtr->maxY;
- }
- if (maxHeight > 0) {
- #ifndef TK_NO_DOUBLE_BUFFERING
- pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
- Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
- maxHeight, Tk_Depth(textPtr->tkwin));
- #else
- pixmap = Tk_WindowId(textPtr->tkwin);
- #endif /* TK_NO_DOUBLE_BUFFERING */
- for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
- (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
- prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
- if (dlPtr->chunkPtr == NULL) continue;
- if (dlPtr->oldY != dlPtr->y) {
- if (tkTextDebug) {
- char string[TK_POS_CHARS];
- TkTextPrintIndex(&dlPtr->index, string);
- Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
- (char *) NULL, string,
- TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
- }
- DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
- if (dInfoPtr->dLinesInvalidated) {
- #ifndef TK_NO_DOUBLE_BUFFERING
- Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
- #endif /* TK_NO_DOUBLE_BUFFERING */
- return;
- }
- dlPtr->oldY = dlPtr->y;
- dlPtr->flags &= ~NEW_LAYOUT;
- }
- /*prevPtr = dlPtr;*/
- }
- #ifndef TK_NO_DOUBLE_BUFFERING
- Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
- #endif /* TK_NO_DOUBLE_BUFFERING */
- }
- /*
- * See if we need to refresh the part of the window below the
- * last line of text (if there is any such area). Refresh the
- * padding area on the left too, since the insertion cursor might
- * have been displayed there previously).
- */
- if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
- dInfoPtr->topOfEof = dInfoPtr->maxY;
- }
- if (bottomY < dInfoPtr->topOfEof) {
- if (tkTextDebug) {
- Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
- (char *) NULL, "eof",
- TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
- }
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- goto end;
- }
- Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
- textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
- dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
- dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
- }
- dInfoPtr->topOfEof = bottomY;
- doScrollbars:
- /*
- * Update the vertical scrollbar, if there is one. Note: it's
- * important to clear REDRAW_PENDING here, just in case the
- * scroll procedure does something that requires redisplay.
- */
-
- if (textPtr->flags & UPDATE_SCROLLBARS) {
- textPtr->flags &= ~UPDATE_SCROLLBARS;
- if (textPtr->yScrollCmd != NULL) {
- GetYView(textPtr->interp, textPtr, 1);
- }
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
- goto end;
- }
- /*
- * Update the horizontal scrollbar, if any.
- */
- if (textPtr->xScrollCmd != NULL) {
- GetXView(textPtr->interp, textPtr, 1);
- }
- }
- end:
- Tcl_Release((ClientData) interp);
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkTextEventuallyRepick --
- *
- * This procedure is invoked whenever something happens that
- * could change the current character or the tags associated
- * with it.
- *
- * Results:
- * None.
- *
- * Side effects:
- * A repick is scheduled as an idle handler.
- *
- *----------------------------------------------------------------------
- */
- /* ARGSUSED */
- void
- TkTextEventuallyRepick(textPtr)
- TkText *textPtr; /* Widget record for text widget. */
- {
- TextDInfo *dInfoPtr = textPtr->dInfoPtr;
- dInfoPtr->flags |= REPICK_NEEDED;
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * TkTextRedrawRegion --
- *
- * This procedure is invoked to schedule a redisplay for a given
- * region of a text widget. The redisplay itself may not occur
- * immediately: it's scheduled as a when-idle handler.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information will eventually be redrawn on the screen.
- *
- *----------------------------------------------------------------------
- */
- /* ARGSUSED */
- void
- TkTextRedrawRegion(textPtr, x, y, width, height)
- TkText *textPtr; /* Widget record for text widget. */
- int x, y; /* Coordinates of upper-left corner of area
- * to be redrawn, in pixels relative to