home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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-1995 Sun Microsystems, Inc.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- */
-
- static char sccsid[] = "@(#) tkTextDisp.c 1.91 95/06/28 15:06:50";
-
- #include "tkPort.h"
- #include "tkInt.h"
- #include "tkText.h"
-
- /*
- * 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. */
- XFontStruct *fontPtr; /* 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. */
- Tk_Uid wrapMode; /* How to handle wrap-around for this tag.
- * One of tkTextCharUid, tkTextNoneUid,
- * or tkTextWordUid. */
- } 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 Style {
- 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. */
- } Style;
-
- /*
- * 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 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 count; /* Number of characters 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.
- */
-
- #define HAS_3D_BORDER 1
- #define NEW_LAYOUT 2
- #define TOP_LINE 4
- #define BOTTOM_LINE 8
-
- /*
- * Overall display information for a text widget:
- */
-
- typedef struct DInfo {
- Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
- * to Styles 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 newCharOffset; /* 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 scanMarkChar; /* 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. */
- } DInfo;
-
- /*
- * In TkTextDispChunk structures for character segments, the clientData
- * field points to one of the following structures:
- */
-
- typedef struct CharInfo {
- int numChars; /* Number of characters to display. */
- char chars[4]; /* Characters to display. Actual size
- * will be numChars, not 4. THIS MUST BE
- * THE LAST FIELD IN THE STRUCTURE. */
- } CharInfo;
-
- /*
- * Flag values for DInfo 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. */
- static int damagedCopies; /* Number of times that XCopyAreas didn't
- * completely work because some of the source
- * information was damaged. */
-
- /*
- * 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));
- 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,
- Style *stylePtr));
- static Style * 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 void MeasureUp _ANSI_ARGS_((TkText *textPtr,
- TkTextIndex *srcPtr, int distance,
- TkTextIndex *dstPtr));
- 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));
-
- /*
- *----------------------------------------------------------------------
- *
- * 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 DInfo data structure is allocated and initialized and attached
- * to textPtr.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextCreateDInfo(textPtr)
- TkText *textPtr; /* Overall information for text widget. */
- {
- register DInfo *dInfoPtr;
- XGCValues gcValues;
-
- dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
- 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->newCharOffset = 0;
- dInfoPtr->curPixelOffset = 0;
- dInfoPtr->maxLength = 0;
- dInfoPtr->xScrollFirst = -1;
- dInfoPtr->xScrollLast = -1;
- dInfoPtr->yScrollFirst = -1;
- dInfoPtr->yScrollLast = -1;
- dInfoPtr->scanMarkChar = 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 DInfo *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) {
- Tk_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 Style structure that
- * corresponds to *sValuePtr.
- *
- * Side effects:
- * A new entry may be created in the style table for the widget.
- *
- *----------------------------------------------------------------------
- */
-
- static Style *
- 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;
- Style *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, 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 = 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.fontPtr = textPtr->fontPtr;
- 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;
- for (i = 0 ; i < numTags; i++) {
- tagPtr = tagPtrs[i];
- 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->fontPtr != None) && (tagPtr->priority > fontPrio)) {
- styleValues.fontPtr = tagPtr->fontPtr;
- 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->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 = (Style *) Tcl_GetHashValue(hPtr);
- stylePtr->refCount++;
- return stylePtr;
- }
-
- /*
- * No existing style matched. Make a new one.
- */
-
- stylePtr = (Style *) ckalloc(sizeof(Style));
- 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 = GCForeground|GCFont;
- gcValues.foreground = styleValues.fgColor->pixel;
- gcValues.font = styleValues.fontPtr->fid;
- 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 Style 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 Style *stylePtr; /* Information about style to be freed. */
-
- {
- stylePtr->refCount--;
- if (stylePtr->refCount == 0) {
- if (stylePtr->bgGC != None) {
- Tk_FreeGC(textPtr->display, stylePtr->bgGC);
- }
- 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 breakCharOffset; /* 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 first character in line. */
- int jIndent; /* Additional indentation (beyond
- * margins) due to justification. */
- int rMargin; /* Right margin width for line. */
- Tk_Uid 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 maxChars; /* Maximum number of characters to
- * include in this chunk. */
- TkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
- * style for first character on line. */
- int tabSize; /* Number of pixels consumed by current
- * tab stop. */
- int offset, ascent, descent, code;
- StyleValues *sValuePtr;
-
- /*
- * Create and initialize a new DLine structure.
- */
-
- dlPtr = (DLine *) ckalloc(sizeof(DLine));
- dlPtr->index = *indexPtr;
- dlPtr->count = 0;
- dlPtr->y = 0;
- dlPtr->oldY = -1;
- dlPtr->height = 0;
- dlPtr->baseline = 0;
- dlPtr->chunkPtr = NULL;
- dlPtr->nextPtr = NULL;
- dlPtr->flags = NEW_LAYOUT;
-
- /*
- * 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;
- breakChunkPtr = NULL;
- breakCharOffset = 0;
- justify = TK_JUSTIFY_LEFT;
- tabIndex = -1;
- tabChunkPtr = NULL;
- tabArrayPtr = NULL;
- rMargin = 0;
- wrapMode = tkTextCharUid;
- tabSize = 0;
-
- /*
- * 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;
- }
- 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) && ((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 void
- UpdateDisplayInfo(textPtr)
- TkText *textPtr; /* Text widget to update. */
- {
- register DInfo *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.charIndex == dlPtr->index.charIndex) {
- /*
- * 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.charIndex < dlPtr->index.charIndex) {
- 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;
- continue;
- }
-
- /*
- * Advance to the start of the next line.
- */
-
- lineOK:
- dlPtr->y = y;
- y += dlPtr->height;
- TkTextIndexForwChars(&index, dlPtr->count, &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, charsToCount;
- 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);
- charsToCount = dInfoPtr->dLinePtr->index.charIndex;
- if (charsToCount == 0) {
- charsToCount = INT_MAX;
- lineNum--;
- }
- for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
- index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
- index.charIndex = 0;
- lowestPtr = NULL;
- do {
- dlPtr = LayoutDLine(textPtr, &index);
- dlPtr->nextPtr = lowestPtr;
- lowestPtr = dlPtr;
- TkTextIndexForwChars(&index, dlPtr->count, &index);
- charsToCount -= dlPtr->count;
- } while ((charsToCount > 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);
- charsToCount = 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->newCharOffset > maxOffset) {
- dInfoPtr->newCharOffset = maxOffset;
- }
- if (dInfoPtr->newCharOffset < 0) {
- dInfoPtr->newCharOffset = 0;
- }
- pixelOffset = dInfoPtr->newCharOffset * 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;
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- Display *display;
- int height, x;
-
- /*
- * First, clear the area of the line to the background color for the
- * text widget.
- */
-
- display = Tk_Display(textPtr->tkwin);
- Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
- 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 == tkNormalUid) {
- for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
- chunkPtr = chunkPtr->nextPtr) {
- x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
- if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
- (*chunkPtr->displayProc)(chunkPtr, x, 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).
- */
-
- (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
- dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
- dlPtr->y + dlPtr->spaceAbove);
- } else {
- (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
- dlPtr->y + dlPtr->spaceAbove);
- }
- if (dInfoPtr->dLinesInvalidated) {
- return;
- }
- }
-
- /*
- * 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.
- */
-
- height = dlPtr->height;
- if ((height + dlPtr->y) > dInfoPtr->maxY) {
- height = dInfoPtr->maxY - dlPtr->y;
- }
- XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
- dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
- (unsigned) height, dInfoPtr->x, dlPtr->y);
- 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. */
- {
- DInfo *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;
-
- /*
- * 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;
- leftX = chunkPtr->x;
- 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) {
- XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
- leftX + xOffset, 0, (unsigned int) (rightX - leftX),
- (unsigned int) dlPtr->height);
- if (sValuePtr->relief != TK_RELIEF_FLAT) {
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, 0, sValuePtr->borderWidth,
- dlPtr->height, 1, sValuePtr->relief);
- Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- rightX - sValuePtr->borderWidth + xOffset,
- 0, 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 = chunkPtr->x;
- leftXIn = 1;
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- chunkPtr2 = NULL;
- if (prevPtr != NULL) {
- /*
- * Find the chunk in the previous line that covers leftX.
- */
-
- nextPtr2 = prevPtr->chunkPtr;
- rightX2 = nextPtr2->x;
- 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, 0,
- 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, 0,
- 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, 0, sValuePtr->borderWidth,
- sValuePtr->borderWidth, 1, sValuePtr->relief);
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, 0, 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 = chunkPtr->x;
- leftXIn = 0;
- rightX = chunkPtr->x + chunkPtr->width;
- if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
- rightX = maxX;
- }
- chunkPtr2 = NULL;
- if (dlPtr->nextPtr != NULL) {
- /*
- * Find the chunk in the previous line that covers leftX.
- */
-
- nextPtr2 = dlPtr->nextPtr->chunkPtr;
- rightX2 = nextPtr2->x;
- 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,
- 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,
- 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, dlPtr->height - sValuePtr->borderWidth,
- sValuePtr->borderWidth, sValuePtr->borderWidth,
- 1, sValuePtr->relief);
- Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
- leftX + xOffset, 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;
- DInfo *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. */
-
- if (textPtr->tkwin == NULL) {
- /*
- * The widget has been deleted. Don't do anything.
- */
-
- return;
- }
-
- if (tkTextDebug) {
- Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
- "", TCL_GLOBAL_ONLY);
- }
-
- 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(textPtr->interp, "tk_textRedraw", (char *) NULL,
- "", TCL_GLOBAL_ONLY);
- }
-
- /*
- * 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 Tk_Release call.
- */
-
- while (dInfoPtr->flags & REPICK_NEEDED) {
- Tk_Preserve((ClientData) textPtr);
- dInfoPtr->flags &= ~REPICK_NEEDED;
- TkTextPickCurrent(textPtr, &textPtr->pickEvent);
- tkwin = textPtr->tkwin;
- Tk_Release((ClientData) textPtr);
- if (tkwin == NULL) {
- return;
- }
- }
-
- /*
- * 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;
-
- 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;
- }
-
- /*
- * Copy the information and update the lines to show that they've
- * been copied. 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;
- }
- XCopyArea(Tk_Display(textPtr->tkwin), Tk_WindowId(textPtr->tkwin),
- Tk_WindowId(textPtr->tkwin), dInfoPtr->scrollGC,
- dInfoPtr->x - textPtr->padX, dlPtr->oldY,
- (unsigned) (dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX)),
- (unsigned) height, dInfoPtr->x - textPtr->padX, y);
- numCopies++;
- 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 just overwrote 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;
- }
- }
-
- /*
- * It's possible that part of the area copied above was obscured.
- * To handle this situation, read expose-related events generated
- * during the XCopyArea operation.
- */
-
- while (1) {
- XEvent event;
-
- XWindowEvent(Tk_Display(textPtr->tkwin),
- Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
- if (event.type == NoExpose) {
- break;
- } else if (event.type == GraphicsExpose) {
- TkTextRedrawRegion(textPtr, event.xgraphicsexpose.x,
- event.xgraphicsexpose.y, event.xgraphicsexpose.width,
- event.xgraphicsexpose.height);
- if (event.xgraphicsexpose.count == 0) {
- damagedCopies++;
- break;
- }
- } else if (event.type == Expose) {
- /*
- * A tricky situation. This event must already have been
- * queued up before the XCopyArea was issued. If the area
- * in this event overlaps the area copied, then some of the
- * bits that were copied were bogus. The easiest way to
- * handle this is to issue two redisplays: one for the
- * original area and one for the area shifted as if it was
- * in the copied area.
- */
-
- TkTextRedrawRegion(textPtr, event.xexpose.x,
- event.xexpose.y, event.xexpose.width,
- event.xexpose.height);
- TkTextRedrawRegion(textPtr, event.xexpose.x,
- event.xexpose.y + offset, event.xexpose.width,
- event.xexpose.height);
- } else {
- panic("DisplayText received unknown exposure event");
- }
- }
- }
-
- /*
- * 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(textPtr->interp, "tk_textRedraw",
- (char *) NULL, "borders",
- TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
- }
- 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 gc;
-
- if (textPtr->flags & GOT_FOCUS) {
- gc = Tk_GCForColor(textPtr->highlightColorPtr,
- Tk_WindowId(textPtr->tkwin));
- } else {
- gc = Tk_GCForColor(textPtr->highlightBgColorPtr,
- Tk_WindowId(textPtr->tkwin));
- }
- Tk_DrawFocusHighlight(textPtr->tkwin, gc, 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) {
- pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
- Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
- maxHeight, Tk_Depth(textPtr->tkwin));
- for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
- (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
- prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
- 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) {
- Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
- return;
- }
- dlPtr->oldY = dlPtr->y;
- dlPtr->flags &= ~NEW_LAYOUT;
- }
- }
- Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
- }
-
- /*
- * 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);
- }
- 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);
- }
-
- /*
- * Update the horizontal scrollbar, if any.
- */
-
- if (textPtr->xScrollCmd != NULL) {
- GetXView(textPtr->interp, textPtr, 1);
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * 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. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
-
- dInfoPtr->flags |= REPICK_NEEDED;
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tk_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
- * textPtr's window. */
- int width, height; /* Width and height of area to be redrawn. */
- {
- register DLine *dlPtr;
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- int maxY, inset;
-
- /*
- * Find all lines that overlap the given region and mark them for
- * redisplay.
- */
-
- maxY = y + height;
- for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
- dlPtr = dlPtr->nextPtr) {
- if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
- dlPtr->oldY = -1;
- }
- }
- if (dInfoPtr->topOfEof < maxY) {
- dInfoPtr->topOfEof = maxY;
- }
-
- /*
- * Schedule the redisplay operation if there isn't one already
- * scheduled.
- */
-
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- inset = textPtr->borderWidth + textPtr->highlightWidth;
- if ((x < inset) || (y < inset)
- || ((x + width) > (Tk_Width(textPtr->tkwin) - inset))
- || (maxY > (Tk_Height(textPtr->tkwin) - inset))) {
- dInfoPtr->flags |= REDRAW_BORDERS;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextChanged --
- *
- * This procedure is invoked when info in a text widget is about
- * to be modified in a way that changes how it is displayed (e.g.
- * characters were inserted or deleted, or tag information was
- * changed). This procedure must be called *before* a change is
- * made, so that indexes in the display information are still
- * valid.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The range of character between index1Ptr (inclusive) and
- * index2Ptr (exclusive) will be redisplayed at some point in the
- * future (the actual redisplay is scheduled as a when-idle handler).
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextChanged(textPtr, index1Ptr, index2Ptr)
- TkText *textPtr; /* Widget record for text widget. */
- TkTextIndex *index1Ptr; /* Index of first character to redisplay. */
- TkTextIndex *index2Ptr; /* Index of character just after last one
- * to redisplay. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- DLine *firstPtr, *lastPtr;
- TkTextIndex rounded;
-
- /*
- * 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.
- */
-
- rounded = *index1Ptr;
- rounded.charIndex = 0;
- firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
- if (firstPtr == NULL) {
- return;
- }
- lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
- while ((lastPtr != NULL)
- && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
- lastPtr = lastPtr->nextPtr;
- }
-
- /*
- * Schedule both a redisplay and a recomputation of display information.
- * It's important to do this BEFORE calling FreeDLines. The reason for
- * this is subtle and has to do with embedded windows. The chunk delete
- * procedure for an embedded window will schedule an idle handler to
- * unmap the window. However, we want the idle handler for redisplay
- * to be called first, so that it can put the embedded window back on
- * the screen again (if appropriate). This will prevent the window from
- * ever being unmapped, and thereby avoid flashing.
- */
-
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
-
- /*
- * Delete all the DLines from firstPtr up to but not including lastPtr.
- */
-
- FreeDLines(textPtr, firstPtr, lastPtr, 1);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextRedrawTag --
- *
- * This procedure is invoked to request a redraw of all characters
- * in a given range that have a particular tag on or off. It's
- * called, for example, when tag options change.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information on the screen may be redrawn, and the layout of
- * the screen may change.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
- TkText *textPtr; /* Widget record for text widget. */
- TkTextIndex *index1Ptr; /* First character in range to consider
- * for redisplay. NULL means start at
- * beginning of text. */
- TkTextIndex *index2Ptr; /* Character just after last one to consider
- * for redisplay. NULL means process all
- * the characters in the text. */
- TkTextTag *tagPtr; /* Information about tag. */
- int withTag; /* 1 means redraw characters that have the
- * tag, 0 means redraw those without. */
- {
- register DLine *dlPtr;
- DLine *endPtr;
- int tagOn;
- TkTextSearch search;
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- TkTextIndex endOfText, *endIndexPtr;
-
- /*
- * Round up the starting position if it's before the first line
- * visible on the screen (we only care about what's on the screen).
- */
-
- dlPtr = dInfoPtr->dLinePtr;
- if (dlPtr == NULL) {
- return;
- }
- if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
- index1Ptr = &dlPtr->index;
- }
-
- /*
- * Set the stopping position if it wasn't specified.
- */
-
- if (index2Ptr == NULL) {
- index2Ptr = TkTextMakeIndex(textPtr->tree,
- TkBTreeNumLines(textPtr->tree), 0, &endOfText);
- }
-
- /*
- * Initialize a search through all transitions on the tag, starting
- * with the first transition where the tag's current state is different
- * from what it will eventually be.
- */
-
- TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
- tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
- if (tagOn != withTag) {
- if (!TkBTreeNextTag(&search)) {
- return;
- }
- }
-
- /*
- * Schedule a redisplay and layout recalculation if they aren't
- * already pending. This has to be done before calling FreeDLines,
- * for the reason given in TkTextChanged.
- */
-
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
-
- /*
- * Each loop through the loop below is for one range of characters
- * where the tag's current state is different than its eventual
- * state. At the top of the loop, search contains information about
- * the first character in the range.
- */
-
- while (1) {
- /*
- * Find the first DLine structure in the range. Note: if the
- * desired character isn't the first in its text line, then look
- * for the character just before it instead. This is needed to
- * handle the case where the first character of a wrapped
- * display line just got smaller, so that it now fits on the
- * line before: need to relayout the line containing the
- * previous character.
- */
-
- if (search.curIndex.charIndex == 0) {
- dlPtr = FindDLine(dlPtr, &search.curIndex);
- } else {
- TkTextIndex tmp;
-
- tmp = search.curIndex;
- tmp.charIndex -= 1;
- dlPtr = FindDLine(dlPtr, &tmp);
- }
- if (dlPtr == NULL) {
- break;
- }
-
- /*
- * Find the first DLine structure that's past the end of the range.
- */
-
- if (!TkBTreeNextTag(&search)) {
- endIndexPtr = index2Ptr;
- } else {
- endIndexPtr = &search.curIndex;
- }
- endPtr = FindDLine(dlPtr, endIndexPtr);
- if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
- && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
- endPtr = endPtr->nextPtr;
- }
-
- /*
- * Delete all of the display lines in the range, so that they'll
- * be re-layed out and redrawn.
- */
-
- FreeDLines(textPtr, dlPtr, endPtr, 1);
- dlPtr = endPtr;
-
- /*
- * Find the first text line in the next range.
- */
-
- if (!TkBTreeNextTag(&search)) {
- break;
- }
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextRelayoutWindow --
- *
- * This procedure is called when something has happened that
- * invalidates the whole layout of characters on the screen, such
- * as a change in a configuration option for the overall text
- * widget or a change in the window size. It causes all display
- * information to be recomputed and the window to be redrawn.
- *
- * Results:
- * None.
- *
- * Side effects:
- * All the display information will be recomputed for the window
- * and the window will be redrawn.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextRelayoutWindow(textPtr)
- TkText *textPtr; /* Widget record for text widget. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- GC new;
- XGCValues gcValues;
-
- /*
- * Schedule the window redisplay. See TkTextChanged for the
- * reason why this has to be done before any calls to FreeDLines.
- */
-
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
- |REPICK_NEEDED;
-
- /*
- * (Re-)create the graphics context for drawing the traversal
- * highlight.
- */
-
- gcValues.graphics_exposures = False;
- new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
- if (dInfoPtr->copyGC != None) {
- Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
- }
- dInfoPtr->copyGC = new;
-
- /*
- * Throw away all the current layout information.
- */
-
- FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
- dInfoPtr->dLinePtr = NULL;
-
- /*
- * Recompute some overall things for the layout. Even if the
- * window gets very small, pretend that there's at least one
- * pixel of drawing space in it.
- */
-
- if (textPtr->highlightWidth < 0) {
- textPtr->highlightWidth = 0;
- }
- dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
- + textPtr->padX;
- dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
- + textPtr->padY;
- dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
- - textPtr->borderWidth - textPtr->padX;
- if (dInfoPtr->maxX <= dInfoPtr->x) {
- dInfoPtr->maxX = dInfoPtr->x + 1;
- }
- dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
- - textPtr->borderWidth - textPtr->padY;
- if (dInfoPtr->maxY <= dInfoPtr->y) {
- dInfoPtr->maxY = dInfoPtr->y + 1;
- }
- dInfoPtr->topOfEof = dInfoPtr->maxY;
-
- /*
- * If the upper-left character isn't the first in a line, recompute
- * it. This is necessary because a change in the window's size
- * or options could change the way lines wrap.
- */
-
- if (textPtr->topIndex.charIndex != 0) {
- MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextSetYView --
- *
- * This procedure is called to specify what lines are to be
- * displayed in a text widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The display will (eventually) be updated so that the position
- * given by "indexPtr" is visible on the screen at the position
- * determined by "pickPlace".
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextSetYView(textPtr, indexPtr, pickPlace)
- TkText *textPtr; /* Widget record for text widget. */
- TkTextIndex *indexPtr; /* Position that is to appear somewhere
- * in the view. */
- int pickPlace; /* 0 means topLine must appear at top of
- * screen. 1 means we get to pick where it
- * appears: minimize screen motion or else
- * display line at center of screen. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- register DLine *dlPtr;
- int bottomY, close, lineIndex;
- TkTextIndex tmpIndex, rounded;
-
- /*
- * If the specified position is the extra line at the end of the
- * text, round it back to the last real line.
- */
-
- lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
- if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
- TkTextIndexBackChars(indexPtr, 1, &rounded);
- indexPtr = &rounded;
- }
-
- if (!pickPlace) {
- /*
- * The specified position must go at the top of the screen.
- * Just leave all the DLine's alone: we may be able to reuse
- * some of the information that's currently on the screen
- * without redisplaying it all.
- */
-
- if (indexPtr->charIndex == 0) {
- textPtr->topIndex = *indexPtr;
- } else {
- MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
- }
- goto scheduleUpdate;
- }
-
- /*
- * We have to pick where to display the index. First, bring
- * the display information up to date and see if the index will be
- * completely visible in the current screen configuration. If so
- * then there's nothing to do.
- */
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
- if (dlPtr != NULL) {
- if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
- /*
- * Part of the line hangs off the bottom of the screen;
- * pretend the whole line is off-screen.
- */
-
- dlPtr = NULL;
- } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
- && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
- return;
- }
- }
-
- /*
- * The desired line isn't already on-screen.
- */
-
- bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
- close = (dInfoPtr->maxY - dInfoPtr->y)/3;
- if (dlPtr != NULL) {
- /*
- * The desired line is above the top of screen. If it is
- * "close" to the top of the window then make it the top
- * line on the screen.
- */
-
- MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
- if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
- MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
- goto scheduleUpdate;
- }
- } else {
- /*
- * The desired line is below the bottom of the screen. If it is
- * "close" to the bottom of the screen then position it at the
- * bottom of the screen.
- */
-
- MeasureUp(textPtr, indexPtr, close, &tmpIndex);
- if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
- bottomY = dInfoPtr->maxY - dInfoPtr->y;
- }
- }
-
- /*
- * Our job now is to arrange the display so that indexPtr appears
- * as low on the screen as possible but with its bottom no lower
- * than bottomY. BottomY is the bottom of the window if the
- * desired line is just below the current screen, otherwise it
- * is the center of the window.
- */
-
- MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
-
- scheduleUpdate:
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * MeasureUp --
- *
- * Given one index, find the index of the first character
- * on the highest display line that would be displayed no more
- * than "distance" pixels above the given index.
- *
- * Results:
- * *dstPtr is filled in with the index of the first character
- * on a display line. The display line is found by measuring
- * up "distance" pixels above the pixel just below an imaginary
- * display line that contains srcPtr. If the display line
- * that covers this coordinate actually extends above the
- * coordinate, then return the index of the next lower line
- * instead (i.e. the returned index will be completely visible
- * at or below the given y-coordinate).
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
-
- static void
- MeasureUp(textPtr, srcPtr, distance, dstPtr)
- TkText *textPtr; /* Text widget in which to measure. */
- TkTextIndex *srcPtr; /* Index of character from which to start
- * measuring. */
- int distance; /* Vertical distance in pixels measured
- * from the pixel just below the lowest
- * one in srcPtr's line. */
- TkTextIndex *dstPtr; /* Index to fill in with result. */
- {
- int lineNum; /* Number of current line. */
- int charsToCount; /* Maximum number of characters to measure
- * in current line. */
- TkTextIndex bestIndex; /* Best candidate seen so far for result. */
- TkTextIndex index;
- DLine *dlPtr, *lowestPtr;
- int noBestYet; /* 1 means bestIndex hasn't been set. */
-
- noBestYet = 1;
- charsToCount = srcPtr->charIndex + 1;
- index.tree = srcPtr->tree;
- for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
- lineNum--) {
- /*
- * Layout an entire text line (potentially > 1 display line).
- * For the first line, which contains srcPtr, only layout the
- * part up through srcPtr (charsToCount is non-infinite to
- * accomplish this). Make a list of all the display lines
- * in backwards order (the lowest DLine on the screen is first
- * in the list).
- */
-
- index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
- index.charIndex = 0;
- lowestPtr = NULL;
- do {
- dlPtr = LayoutDLine(textPtr, &index);
- dlPtr->nextPtr = lowestPtr;
- lowestPtr = dlPtr;
- TkTextIndexForwChars(&index, dlPtr->count, &index);
- charsToCount -= dlPtr->count;
- } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
-
- /*
- * Scan through the display lines to see if we've covered enough
- * vertical distance. If so, save the starting index for the
- * line at the desired location.
- */
-
- for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
- distance -= dlPtr->height;
- if (distance < 0) {
- *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
- break;
- }
- bestIndex = dlPtr->index;
- noBestYet = 0;
- }
-
- /*
- * Discard the display lines, then either return or prepare
- * for the next display line to lay out.
- */
-
- FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
- if (distance < 0) {
- return;
- }
- charsToCount = INT_MAX; /* Consider all chars. in next line. */
- }
-
- /*
- * Ran off the beginning of the text. Return the first character
- * in the text.
- */
-
- TkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TkTextSeeCmd --
- *
- * This procedure is invoked to process the "see" option for
- * the widget command for text widgets. See the user documentation
- * for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- int
- TkTextSeeCmd(textPtr, interp, argc, argv)
- TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. Someone else has already
- * parsed this command enough to know that
- * argv[1] is "see". */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- TkTextIndex index;
- int x, y, width, height, lineWidth, charCount, oneThird, delta;
- DLine *dlPtr;
- TkTextDispChunk *chunkPtr;
-
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " see index\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
- return TCL_ERROR;
- }
-
- /*
- * If the specified position is the extra line at the end of the
- * text, round it back to the last real line.
- */
-
- if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
- TkTextIndexBackChars(&index, 1, &index);
- }
-
- /*
- * First get the desired position into the vertical range of the window.
- */
-
- TkTextSetYView(textPtr, &index, 1);
-
- /*
- * Now make sure that the character is in view horizontally.
- */
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
- lineWidth = dInfoPtr->maxX - dInfoPtr->x;
- if (dInfoPtr->maxLength < lineWidth) {
- return TCL_OK;
- }
-
- /*
- * Find the chunk that contains the desired index.
- */
-
- dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
- charCount = index.charIndex - dlPtr->index.charIndex;
- for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
- if (charCount < chunkPtr->numChars) {
- break;
- }
- charCount -= chunkPtr->numChars;
- }
-
- /*
- * Call a chunk-specific procedure to find the horizontal range of
- * the character within the chunk.
- */
-
- (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
- &height);
- delta = x - dInfoPtr->curPixelOffset;
- oneThird = lineWidth/3;
- if (delta < 0) {
- if (delta < -oneThird) {
- dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
- } else {
- dInfoPtr->newCharOffset -= ((-delta) + textPtr->charWidth - 1)
- / textPtr->charWidth;
- }
- } else {
- delta -= (lineWidth - width);
- if (delta > 0) {
- if (delta > oneThird) {
- dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
- } else {
- dInfoPtr->newCharOffset += (delta + textPtr->charWidth - 1)
- / textPtr->charWidth;
- }
- } else {
- return TCL_OK;
- }
- }
- dInfoPtr->flags |= DINFO_OUT_OF_DATE;
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- return TCL_OK;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TkTextXviewCmd --
- *
- * This procedure is invoked to process the "xview" option for
- * the widget command for text widgets. See the user documentation
- * for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- int
- TkTextXviewCmd(textPtr, interp, argc, argv)
- TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. Someone else has already
- * parsed this command enough to know that
- * argv[1] is "xview". */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- int type, charsPerPage, count, newOffset;
- double fraction;
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
-
- if (argc == 2) {
- GetXView(interp, textPtr, 0);
- return TCL_OK;
- }
-
- newOffset = dInfoPtr->newCharOffset;
- type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
- switch (type) {
- case TK_SCROLL_ERROR:
- return TCL_ERROR;
- case TK_SCROLL_MOVETO:
- newOffset = (fraction * dInfoPtr->maxLength) / textPtr->charWidth;
- break;
- case TK_SCROLL_PAGES:
- charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
- - 2;
- if (charsPerPage < 1) {
- charsPerPage = 1;
- }
- newOffset += charsPerPage*count;
- break;
- case TK_SCROLL_UNITS:
- newOffset += count;
- break;
- }
-
- dInfoPtr->newCharOffset = newOffset;
- dInfoPtr->flags |= DINFO_OUT_OF_DATE;
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * ScrollByLines --
- *
- * This procedure is called to scroll a text widget up or down
- * by a given number of lines.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The view in textPtr's window changes to reflect the value
- * of "offset".
- *
- *----------------------------------------------------------------------
- */
-
- static void
- ScrollByLines(textPtr, offset)
- TkText *textPtr; /* Widget to scroll. */
- int offset; /* Amount by which to scroll, in *screen*
- * lines. Positive means that information
- * later in text becomes visible, negative
- * means that information earlier in the
- * text becomes visible. */
- {
- int i, charsToCount, lineNum;
- TkTextIndex new, index;
- TkTextLine *lastLinePtr;
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- DLine *dlPtr, *lowestPtr;
-
- if (offset < 0) {
- /*
- * Must scroll up (to show earlier information in the text).
- * The code below is similar to that in MeasureUp, except that
- * it counts lines instead of pixels.
- */
-
- charsToCount = textPtr->topIndex.charIndex + 1;
- index.tree = textPtr->tree;
- offset--; /* Skip line containing topIndex. */
- for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
- lineNum >= 0; lineNum--) {
- index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
- index.charIndex = 0;
- lowestPtr = NULL;
- do {
- dlPtr = LayoutDLine(textPtr, &index);
- dlPtr->nextPtr = lowestPtr;
- lowestPtr = dlPtr;
- TkTextIndexForwChars(&index, dlPtr->count, &index);
- charsToCount -= dlPtr->count;
- } while ((charsToCount > 0)
- && (index.linePtr == dlPtr->index.linePtr));
-
- for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
- offset++;
- if (offset == 0) {
- textPtr->topIndex = dlPtr->index;
- break;
- }
- }
-
- /*
- * Discard the display lines, then either return or prepare
- * for the next display line to lay out.
- */
-
- FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
- if (offset >= 0) {
- goto scheduleUpdate;
- }
- charsToCount = INT_MAX;
- }
-
- /*
- * Ran off the beginning of the text. Return the first character
- * in the text.
- */
-
- TkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
- } else {
- /*
- * Scrolling down, to show later information in the text.
- * Just count lines from the current top of the window.
- */
-
- lastLinePtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
- for (i = 0; i < offset; i++) {
- dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
- dlPtr->nextPtr = NULL;
- TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
- FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
- if (new.linePtr == lastLinePtr) {
- break;
- }
- textPtr->topIndex = new;
- }
- }
-
- scheduleUpdate:
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TkTextYviewCmd --
- *
- * This procedure is invoked to process the "yview" option for
- * the widget command for text widgets. See the user documentation
- * for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- int
- TkTextYviewCmd(textPtr, interp, argc, argv)
- TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. Someone else has already
- * parsed this command enough to know that
- * argv[1] is "yview". */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- int pickPlace, lineNum, type, lineHeight;
- int pixels, count;
- size_t switchLength;
- double fraction;
- TkTextIndex index, new;
- TkTextLine *lastLinePtr;
- DLine *dlPtr;
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
-
- if (argc == 2) {
- GetYView(interp, textPtr, 0);
- return TCL_OK;
- }
-
- /*
- * Next, handle the old syntax: "pathName yview ?-pickplace? where"
- */
-
- pickPlace = 0;
- if (argv[2][0] == '-') {
- switchLength = strlen(argv[2]);
- if ((switchLength >= 2)
- && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
- pickPlace = 1;
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " yview -pickplace lineNum|index\"",
- (char *) NULL);
- return TCL_ERROR;
- }
- }
- }
- if ((argc == 3) || pickPlace) {
- if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
- TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
- TkTextSetYView(textPtr, &index, 0);
- return TCL_OK;
- }
-
- /*
- * The argument must be a regular text index.
- */
-
- Tcl_ResetResult(interp);
- if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- TkTextSetYView(textPtr, &index, pickPlace);
- return TCL_OK;
- }
-
- /*
- * New syntax: dispatch based on argv[2].
- */
-
- type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
- switch (type) {
- case TK_SCROLL_ERROR:
- return TCL_ERROR;
- case TK_SCROLL_MOVETO:
- fraction *= TkBTreeNumLines(textPtr->tree);
- lineNum = fraction;
- TkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
- TkTextIndexBackChars(&index, 1, &index);
- index.charIndex = (index.charIndex+1)*(fraction-lineNum);
- TkTextSetYView(textPtr, &index, 0);
- break;
- case TK_SCROLL_PAGES:
- /*
- * Scroll up or down by screenfulls. Actually, use the
- * window height minus two lines, so that there's some
- * overlap between adjacent pages.
- */
-
- lineHeight = textPtr->fontPtr->ascent + textPtr->fontPtr->descent;
- if (count < 0) {
- pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*(-count)
- + lineHeight;
- MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
- if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
- /*
- * A page of scrolling ended up being less than one line.
- * Scroll one line anyway.
- */
-
- count = -1;
- goto scrollByLines;
- }
- textPtr->topIndex = new;
- } else {
- /*
- * Scrolling down by pages. Layout lines starting at the
- * top index and count through the desired vertical distance.
- */
-
- pixels = (dInfoPtr->maxY - 2*lineHeight - dInfoPtr->y)*count;
- lastLinePtr = TkBTreeFindLine(textPtr->tree,
- TkBTreeNumLines(textPtr->tree));
- do {
- dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
- dlPtr->nextPtr = NULL;
- TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
- &new);
- pixels -= dlPtr->height;
- FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
- if (new.linePtr == lastLinePtr) {
- break;
- }
- textPtr->topIndex = new;
- } while (pixels > 0);
- }
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
- break;
- case TK_SCROLL_UNITS:
- scrollByLines:
- ScrollByLines(textPtr, count);
- break;
- }
- return TCL_OK;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TkTextScanCmd --
- *
- * This procedure is invoked to process the "scan" option for
- * the widget command for text widgets. See the user documentation
- * for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *--------------------------------------------------------------
- */
-
- int
- TkTextScanCmd(textPtr, interp, argc, argv)
- register TkText *textPtr; /* Information about text widget. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. Someone else has already
- * parsed this command enough to know that
- * argv[1] is "scan". */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- TkTextIndex index;
- int c, x, y, totalScroll, newChar, maxChar;
- size_t length;
-
- if (argc != 5) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " scan mark|dragto x y\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
- return TCL_ERROR;
- }
- if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
- return TCL_ERROR;
- }
- c = argv[2][0];
- length = strlen(argv[2]);
- if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
- /*
- * Amplify the difference between the current position and the
- * mark position to compute how much the view should shift, then
- * update the mark position to correspond to the new view. If we
- * run off the edge of the text, reset the mark point so that the
- * current position continues to correspond to the edge of the
- * window. This means that the picture will start dragging as
- * soon as the mouse reverses direction (without this reset, might
- * have to slide mouse a long ways back before the picture starts
- * moving again).
- */
-
- newChar = dInfoPtr->scanMarkChar + (10*(dInfoPtr->scanMarkX - x))
- / (textPtr->charWidth);
- maxChar = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
- + textPtr->charWidth - 1)/textPtr->charWidth;
- if (newChar < 0) {
- dInfoPtr->scanMarkChar = newChar = 0;
- dInfoPtr->scanMarkX = x;
- } else if (newChar > maxChar) {
- dInfoPtr->scanMarkChar = newChar = maxChar;
- dInfoPtr->scanMarkX = x;
- }
- dInfoPtr->newCharOffset = newChar;
-
- totalScroll = (10*(dInfoPtr->scanMarkY - y))
- / (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
- if (totalScroll != dInfoPtr->scanTotalScroll) {
- index = textPtr->topIndex;
- ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
- dInfoPtr->scanTotalScroll = totalScroll;
- if ((index.linePtr == textPtr->topIndex.linePtr) &&
- (index.charIndex == textPtr->topIndex.charIndex)) {
- dInfoPtr->scanTotalScroll = 0;
- dInfoPtr->scanMarkY = y;
- }
- }
- } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
- dInfoPtr->scanMarkChar = dInfoPtr->newCharOffset;
- dInfoPtr->scanMarkX = x;
- dInfoPtr->scanTotalScroll = 0;
- dInfoPtr->scanMarkY = y;
- } else {
- Tcl_AppendResult(interp, "bad scan option \"", argv[2],
- "\": must be mark or dragto", (char *) NULL);
- return TCL_ERROR;
- }
- dInfoPtr->flags |= DINFO_OUT_OF_DATE;
- if (!(dInfoPtr->flags & REDRAW_PENDING)) {
- dInfoPtr->flags |= REDRAW_PENDING;
- Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetXView --
- *
- * This procedure computes the fractions that indicate what's
- * visible in a text window and, optionally, evaluates a
- * Tcl script to report them to the text's associated scrollbar.
- *
- * Results:
- * If report is zero, then interp->result is filled in with
- * two real numbers separated by a space, giving the position of
- * the left and right edges of the window as fractions from 0 to
- * 1, where 0 means the left edge of the text and 1 means the right
- * edge. If report is non-zero, then interp->result isn't modified
- * directly, but instead a script is evaluated in interp to report
- * the new horizontal scroll position to the scrollbar (if the scroll
- * position hasn't changed then no script is invoked).
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- GetXView(interp, textPtr, report)
- Tcl_Interp *interp; /* If "report" is FALSE, string
- * describing visible range gets
- * stored in interp->result. */
- TkText *textPtr; /* Information about text widget. */
- int report; /* Non-zero means report info to
- * scrollbar if it has changed. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- char buffer[200];
- double first, last;
- int code;
-
- if (dInfoPtr->maxLength > 0) {
- first = ((double) dInfoPtr->curPixelOffset)
- / dInfoPtr->maxLength;
- last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
- / dInfoPtr->maxLength;
- if (last > 1.0) {
- last = 1.0;
- }
- } else {
- first = 0;
- last = 1.0;
- }
- if (!report) {
- sprintf(interp->result, "%g %g", first, last);
- return;
- }
- if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
- return;
- }
- dInfoPtr->xScrollFirst = first;
- dInfoPtr->xScrollLast = last;
- sprintf(buffer, " %g %g", first, last);
- code = Tcl_VarEval(interp, textPtr->xScrollCmd,
- buffer, (char *) NULL);
- if (code != TCL_OK) {
- Tcl_AddErrorInfo(interp,
- "\n (horizontal scrolling command executed by text)");
- Tk_BackgroundError(interp);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetYView --
- *
- * This procedure computes the fractions that indicate what's
- * visible in a text window and, optionally, evaluates a
- * Tcl script to report them to the text's associated scrollbar.
- *
- * Results:
- * If report is zero, then interp->result is filled in with
- * two real numbers separated by a space, giving the position of
- * the top and bottom of the window as fractions from 0 to 1, where
- * 0 means the beginning of the text and 1 means the end. If
- * report is non-zero, then interp->result isn't modified directly,
- * but a script is evaluated in interp to report the new scroll
- * position to the scrollbar (if the scroll position hasn't changed
- * then no script is invoked).
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- GetYView(interp, textPtr, report)
- Tcl_Interp *interp; /* If "report" is FALSE, string
- * describing visible range gets
- * stored in interp->result. */
- TkText *textPtr; /* Information about text widget. */
- int report; /* Non-zero means report info to
- * scrollbar if it has changed. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- char buffer[200];
- double first, last;
- DLine *dlPtr;
- int totalLines, code, count;
-
- dlPtr = dInfoPtr->dLinePtr;
- totalLines = TkBTreeNumLines(textPtr->tree);
- first = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
- + ((double) dlPtr->index.charIndex)
- / (TkBTreeCharsInLine(dlPtr->index.linePtr));
- first /= totalLines;
- while (1) {
- if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
- /*
- * The last line is only partially visible, so don't
- * count its characters in what's visible.
- */
- count = 0;
- break;
- }
- if (dlPtr->nextPtr == NULL) {
- count = dlPtr->count;
- break;
- }
- dlPtr = dlPtr->nextPtr;
- }
- last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
- + ((double) (dlPtr->index.charIndex + count))
- / (TkBTreeCharsInLine(dlPtr->index.linePtr));
- last /= totalLines;
- if (!report) {
- sprintf(interp->result, "%g %g", first, last);
- return;
- }
- if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
- return;
- }
- dInfoPtr->yScrollFirst = first;
- dInfoPtr->yScrollLast = last;
- sprintf(buffer, " %g %g", first, last);
- code = Tcl_VarEval(interp, textPtr->yScrollCmd,
- buffer, (char *) NULL);
- if (code != TCL_OK) {
- Tcl_AddErrorInfo(interp,
- "\n (vertical scrolling command executed by text)");
- Tk_BackgroundError(interp);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * FindDLine --
- *
- * This procedure is called to find the DLine corresponding to a
- * given text index.
- *
- * Results:
- * The return value is a pointer to the first DLine found in the
- * list headed by dlPtr that displays information at or after the
- * specified position. If there is no such line in the list then
- * NULL is returned.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static DLine *
- FindDLine(dlPtr, indexPtr)
- register DLine *dlPtr; /* Pointer to first in list of DLines
- * to search. */
- TkTextIndex *indexPtr; /* Index of desired character. */
- {
- TkTextLine *linePtr;
-
- if (dlPtr == NULL) {
- return NULL;
- }
- if (TkBTreeLineIndex(indexPtr->linePtr)
- < TkBTreeLineIndex(dlPtr->index.linePtr)) {
- /*
- * The first display line is already past the desired line.
- */
- return dlPtr;
- }
-
- /*
- * Find the first display line that covers the desired text line.
- */
-
- linePtr = dlPtr->index.linePtr;
- while (linePtr != indexPtr->linePtr) {
- while (dlPtr->index.linePtr == linePtr) {
- dlPtr = dlPtr->nextPtr;
- if (dlPtr == NULL) {
- return NULL;
- }
- }
- linePtr = TkBTreeNextLine(linePtr);
- if (linePtr == NULL) {
- panic("FindDLine reached end of text");
- }
- }
- if (indexPtr->linePtr != dlPtr->index.linePtr) {
- return dlPtr;
- }
-
- /*
- * Now get to the right position within the text line.
- */
-
- while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
- dlPtr = dlPtr->nextPtr;
- if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
- break;
- }
- }
- return dlPtr;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextPixelIndex --
- *
- * Given an (x,y) coordinate on the screen, find the location of
- * the character closest to that location.
- *
- * Results:
- * The index at *indexPtr is modified to refer to the character
- * on the display that is closest to (x,y).
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- void
- TkTextPixelIndex(textPtr, x, y, indexPtr)
- TkText *textPtr; /* Widget record for text widget. */
- int x, y; /* Pixel coordinates of point in widget's
- * window. */
- TkTextIndex *indexPtr; /* This index gets filled in with the
- * index of the character nearest to (x,y). */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- register DLine *dlPtr;
- register TkTextDispChunk *chunkPtr;
-
- /*
- * Make sure that all of the layout information about what's
- * displayed where on the screen is up-to-date.
- */
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
-
- /*
- * If the coordinates are above the top of the window, then adjust
- * them to refer to the upper-right corner of the window. If they're
- * off to one side or the other, then adjust to the closest side.
- */
-
- if (y < dInfoPtr->y) {
- y = dInfoPtr->y;
- x = dInfoPtr->x;
- }
- if (x >= dInfoPtr->maxX) {
- x = dInfoPtr->maxX - 1;
- }
- if (x < dInfoPtr->x) {
- x = dInfoPtr->x;
- }
-
- /*
- * Find the display line containing the desired y-coordinate.
- */
-
- for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
- dlPtr = dlPtr->nextPtr) {
- if (dlPtr->nextPtr == NULL) {
- /*
- * Y-coordinate is off the bottom of the displayed text.
- * Use the last character on the last line.
- */
-
- x = dInfoPtr->maxX - 1;
- break;
- }
- }
-
- /*
- * Scan through the line's chunks to find the one that contains
- * the desired x-coordinate. Before doing this, translate the
- * x-coordinate from the coordinate system of the window to the
- * coordinate system of the line (to take account of x-scrolling).
- */
-
- *indexPtr = dlPtr->index;
- x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
- for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
- indexPtr->charIndex += chunkPtr->numChars,
- chunkPtr = chunkPtr->nextPtr) {
- if (chunkPtr->nextPtr == NULL) {
- indexPtr->charIndex += chunkPtr->numChars - 1;
- return;
- }
- }
-
- /*
- * If the chunk has more than one character in it, ask it which
- * character is at the desired location.
- */
-
- if (chunkPtr->numChars > 1) {
- indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
- }
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextCharBbox --
- *
- * Given an index, find the bounding box of the screen area
- * occupied by that character.
- *
- * Results:
- * Zero is returned if the character is on the screen. -1
- * means the character isn't on the screen. If the return value
- * is 0, then the bounding box of the part of the character that's
- * visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
- * and *heightPtr.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- int
- TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
- TkText *textPtr; /* Widget record for text widget. */
- TkTextIndex *indexPtr; /* Index of character whose bounding
- * box is desired. */
- int *xPtr, *yPtr; /* Filled with character's upper-left
- * coordinate. */
- int *widthPtr, *heightPtr; /* Filled in with character's dimensions. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- DLine *dlPtr;
- register TkTextDispChunk *chunkPtr;
- int index;
-
- /*
- * Make sure that all of the screen layout information is up to date.
- */
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
-
- /*
- * Find the display line containing the desired index.
- */
-
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
- if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
- return -1;
- }
-
- /*
- * Find the chunk within the line that contains the desired
- * index.
- */
-
- index = indexPtr->charIndex - dlPtr->index.charIndex;
- for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
- if (chunkPtr == NULL) {
- return -1;
- }
- if (index < chunkPtr->numChars) {
- break;
- }
- index -= chunkPtr->numChars;
- }
-
- /*
- * Call a chunk-specific procedure to find the horizontal range of
- * the character within the chunk, then fill in the vertical range.
- * The x-coordinate returned by bboxProc is a coordinate within a
- * line, not a coordinate on the screen. Translate it to reflect
- * horizontal scrolling.
- */
-
- (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y + dlPtr->spaceAbove,
- dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
- dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
- heightPtr);
- *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
- if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
- /*
- * Last character in display line. Give it all the space up to
- * the line.
- */
-
- if (*xPtr > dInfoPtr->maxX) {
- *xPtr = dInfoPtr->maxX;
- }
- *widthPtr = dInfoPtr->maxX - *xPtr;
- }
- if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
- return -1;
- }
- if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
- *widthPtr = dInfoPtr->maxX - *xPtr;
- if (*widthPtr <= 0) {
- return -1;
- }
- }
- if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
- *heightPtr = dInfoPtr->maxY - *yPtr;
- if (*heightPtr <= 0) {
- return -1;
- }
- }
- return 0;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * TkTextDLineInfo --
- *
- * Given an index, return information about the display line
- * containing that character.
- *
- * Results:
- * Zero is returned if the character is on the screen. -1
- * means the character isn't on the screen. If the return value
- * is 0, then information is returned in the variables pointed
- * to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- int
- TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
- TkText *textPtr; /* Widget record for text widget. */
- TkTextIndex *indexPtr; /* Index of character whose bounding
- * box is desired. */
- int *xPtr, *yPtr; /* Filled with line's upper-left
- * coordinate. */
- int *widthPtr, *heightPtr; /* Filled in with line's dimensions. */
- int *basePtr; /* Filled in with the baseline position,
- * measured as an offset down from *yPtr. */
- {
- DInfo *dInfoPtr = textPtr->dInfoPtr;
- DLine *dlPtr;
-
- /*
- * Make sure that all of the screen layout information is up to date.
- */
-
- if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
- UpdateDisplayInfo(textPtr);
- }
-
- /*
- * Find the display line containing the desired index.
- */
-
- dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
- if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
- return -1;
- }
-
- *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlPtr->chunkPtr->x;
- *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
- *yPtr = dlPtr->y;
- if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
- *heightPtr = dInfoPtr->maxY - dlPtr->y;
- } else {
- *heightPtr = dlPtr->height;
- }
- *basePtr = dlPtr->baseline;
- return 0;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TkTextCharLayoutProc --
- *
- * This procedure is the "layoutProc" for character segments.
- *
- * Results:
- * If there is something to display for the chunk then a
- * non-zero value is returned and the fields of chunkPtr
- * will be filled in (see the declaration of TkTextDispChunk
- * in tkText.h for details). If zero is returned it means
- * that no characters from this chunk fit in the window.
- * If -1 is returned it means that this segment just doesn't
- * need to be displayed (never happens for text).
- *
- * Side effects:
- * Memory is allocated to hold additional information about
- * the chunk.
- *
- *--------------------------------------------------------------
- */
-
- int
- TkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
- noCharsYet, wrapMode, chunkPtr)
- TkText *textPtr; /* Text widget being layed out. */
- TkTextIndex *indexPtr; /* Index of first character to lay out
- * (corresponds to segPtr and offset). */
- TkTextSegment *segPtr; /* Segment being layed out. */
- int offset; /* Offset within segment of first character
- * to consider. */
- int maxX; /* Chunk must not occupy pixels at this
- * position or higher. */
- int maxChars; /* Chunk must not include more than this
- * many characters. */
- int noCharsYet; /* Non-zero means no characters have been
- * assigned to this display line yet. */
- Tk_Uid wrapMode; /* How to handle line wrapping: tkTextCharUid,
- * tkTextNoneUid, or tkTextWordUid. */
- register TkTextDispChunk *chunkPtr;
- /* Structure to fill in with information
- * about this chunk. The x field has already
- * been set by the caller. */
- {
- XFontStruct *fontPtr;
- int nextX, charsThatFit, count;
- CharInfo *ciPtr;
- char *p;
- TkTextSegment *nextPtr;
-
- /*
- * Figure out how many characters will fit in the space we've got.
- * Include the next character, even though it won't fit completely,
- * if any of the following is true:
- * (a) the chunk contains no characters and the display line contains
- * no characters yet (i.e. the line isn't wide enough to hold
- * even a single character).
- * (b) at least one pixel of the character is visible, we haven't
- * already exceeded the character limit, and the next character
- * is a white space character.
- */
-
- p = segPtr->body.chars + offset;
- fontPtr = chunkPtr->stylePtr->sValuePtr->fontPtr;
- charsThatFit = TkMeasureChars(fontPtr, p, maxChars, chunkPtr->x,
- maxX, 0, TK_IGNORE_TABS, &nextX);
- if (charsThatFit < maxChars) {
- if ((charsThatFit == 0) && noCharsYet) {
- charsThatFit = 1;
- TkMeasureChars(fontPtr, p, 1, chunkPtr->x, INT_MAX, 0,
- TK_IGNORE_TABS, &nextX);
- }
- if (p[charsThatFit] == '\n') {
- /*
- * A newline character takes up no space, so if the previous
- * character fits then so does the newline.
- */
-
- charsThatFit++;
- } else if ((nextX < maxX) && (isspace(UCHAR(p[charsThatFit])))) {
- /*
- * Space characters are funny, in that they are considered
- * to fit if there is at least one pixel of space left on the
- * line. Just give the space character whatever space is left.
- */
-
- nextX = maxX;
- charsThatFit++;
- }
- if (charsThatFit == 0) {
- return 0;
- }
- }
-
- /*
- * Fill in the chunk structure and allocate and initialize a
- * CharInfo structure. If the last character is a newline
- * then don't bother to display it.
- */
-
- chunkPtr->displayProc = CharDisplayProc;
- chunkPtr->undisplayProc = CharUndisplayProc;
- chunkPtr->measureProc = CharMeasureProc;
- chunkPtr->bboxProc = CharBboxProc;
- chunkPtr->numChars = charsThatFit;
- chunkPtr->minAscent = fontPtr->ascent
- + chunkPtr->stylePtr->sValuePtr->offset;
- chunkPtr->minDescent = fontPtr->descent
- - chunkPtr->stylePtr->sValuePtr->offset;;
- chunkPtr->minHeight = 0;
- chunkPtr->width = nextX - chunkPtr->x;
- chunkPtr->breakIndex = -1;
- ciPtr = (CharInfo *) ckalloc((unsigned)
- (sizeof(CharInfo) - 3 + charsThatFit));
- chunkPtr->clientData = (ClientData) ciPtr;
- ciPtr->numChars = charsThatFit;
- strncpy(ciPtr->chars, p, (size_t) charsThatFit);
- if (p[charsThatFit-1] == '\n') {
- ciPtr->numChars--;
- }
-
- /*
- * Compute a break location. If we're in word wrap mode, a
- * break can occur after any space character, or at the end of
- * the chunk if the next segment (ignoring those with zero size)
- * is not a character segment.
- */
-
- if (wrapMode != tkTextWordUid) {
- chunkPtr->breakIndex = chunkPtr->numChars;
- } else {
- for (count = charsThatFit, p += charsThatFit-1; count > 0;
- count--, p--) {
- if (isspace(UCHAR(*p))) {
- chunkPtr->breakIndex = count;
- break;
- }
- }
- if ((charsThatFit+offset) == segPtr->size) {
- for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
- nextPtr = nextPtr->nextPtr) {
- if (nextPtr->size != 0) {
- if (nextPtr->typePtr != &tkTextCharType) {
- chunkPtr->breakIndex = chunkPtr->numChars;
- }
- break;
- }
- }
- }
- }
- return 1;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * CharDisplayProc --
- *
- * This procedure is called to display a character chunk on
- * the screen or in an off-screen pixmap.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Graphics are drawn.
- *
- *--------------------------------------------------------------
- */
-
- static void
- CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
- TkTextDispChunk *chunkPtr; /* Chunk that is to be drawn. */
- int x; /* X-position in dst at which to
- * draw this chunk (may differ from
- * the x-position in the chunk because
- * of scrolling). */
- int y; /* Y-position at which to draw this
- * chunk in dst. */
- int height; /* Total height of line. */
- int baseline; /* Offset of baseline from y. */
- Display *display; /* Display to use for drawing. */
- Drawable dst; /* Pixmap or window in which to draw
- * chunk. */
- int screenY; /* Y-coordinate in text window that
- * corresponds to y. */
- {
- CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
- Style *stylePtr;
- StyleValues *sValuePtr;
-
- if ((x + chunkPtr->width) <= 0) {
- /*
- * The chunk is off-screen.
- */
-
- return;
- }
-
- stylePtr = chunkPtr->stylePtr;
- sValuePtr = stylePtr->sValuePtr;
-
- /*
- * Draw the text, underline, and overstrike for this chunk.
- */
-
- if (ciPtr->numChars > 0) {
- TkDisplayChars(display, dst, stylePtr->fgGC, sValuePtr->fontPtr,
- ciPtr->chars, ciPtr->numChars, x,
- y + baseline - sValuePtr->offset, x - chunkPtr->x,
- TK_IGNORE_TABS);
- if (sValuePtr->underline) {
- TkUnderlineChars(display, dst, stylePtr->fgGC,
- sValuePtr->fontPtr, ciPtr->chars, x,
- y + baseline - sValuePtr->offset, x - chunkPtr->x,
- TK_IGNORE_TABS, 0, ciPtr->numChars-1);
- }
- if (sValuePtr->overstrike) {
- TkUnderlineChars(display, dst, stylePtr->fgGC,
- sValuePtr->fontPtr, ciPtr->chars, x,
- y + baseline - sValuePtr->offset
- - sValuePtr->fontPtr->descent
- - (sValuePtr->fontPtr->ascent*3)/10,
- x - chunkPtr->x, TK_IGNORE_TABS, 0, ciPtr->numChars-1);
- }
- }
- }
-
- /*
- *--------------------------------------------------------------
- *
- * CharUndisplayProc --
- *
- * This procedure is called when a character chunk is no
- * longer going to be displayed. It frees up resources
- * that were allocated to display the chunk.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Memory and other resources get freed.
- *
- *--------------------------------------------------------------
- */
-
- static void
- CharUndisplayProc(textPtr, chunkPtr)
- TkText *textPtr; /* Overall information about text
- * widget. */
- TkTextDispChunk *chunkPtr; /* Chunk that is about to be freed. */
- {
- CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
-
- ckfree((char *) ciPtr);
- }
-
- /*
- *--------------------------------------------------------------
- *
- * CharMeasureProc --
- *
- * This procedure is called to determine which character in
- * a character chunk lies over a given x-coordinate.
- *
- * Results:
- * The return value is the index *within the chunk* of the
- * character that covers the position given by "x".
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
-
- static int
- CharMeasureProc(chunkPtr, x)
- TkTextDispChunk *chunkPtr; /* Chunk containing desired coord. */
- int x; /* X-coordinate, in same coordinate
- * system as chunkPtr->x. */
- {
- CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
- int endX;
-
- return TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
- ciPtr->chars, chunkPtr->numChars-1, chunkPtr->x, x, 0,
- TK_IGNORE_TABS, &endX);
- }
-
- /*
- *--------------------------------------------------------------
- *
- * CharBboxProc --
- *
- * This procedure is called to compute the bounding box of
- * the area occupied by a single character.
- *
- * Results:
- * There is no return value. *xPtr and *yPtr are filled in
- * with the coordinates of the upper left corner of the
- * character, and *widthPtr and *heightPtr are filled in with
- * the dimensions of the character in pixels. Note: not all
- * of the returned bbox is necessarily visible on the screen
- * (the rightmost part might be off-screen to the right,
- * and the bottommost part might be off-screen to the bottom).
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
-
- static void
- CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
- widthPtr, heightPtr)
- TkTextDispChunk *chunkPtr; /* Chunk containing desired char. */
- int index; /* Index of desired character within
- * the chunk. */
- int y; /* Topmost pixel in area allocated
- * for this line. */
- int lineHeight; /* Height of line, in pixels. */
- int baseline; /* Location of line's baseline, in
- * pixels measured down from y. */
- int *xPtr, *yPtr; /* Gets filled in with coords of
- * character's upper-left pixel.
- * X-coord is in same coordinate
- * system as chunkPtr->x. */
- int *widthPtr; /* Gets filled in with width of
- * character, in pixels. */
- int *heightPtr; /* Gets filled in with height of
- * character, in pixels. */
- {
- CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
- int maxX;
-
- maxX = chunkPtr->width + chunkPtr->x;
- TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
- ciPtr->chars, index, chunkPtr->x, 1000000, 0, TK_IGNORE_TABS,
- xPtr);
- if (index == ciPtr->numChars) {
- /*
- * This situation only happens if the last character in a line
- * is a space character, in which case it absorbs all of the
- * extra space in the line (see TkTextCharLayoutProc).
- */
-
- *widthPtr = maxX - *xPtr;
- } else if ((ciPtr->chars[index] == '\t')
- && (index == (ciPtr->numChars-1))) {
- /*
- * The desired character is a tab character that terminates a
- * chunk; give it all the space left in the chunk.
- */
-
- *widthPtr = maxX - *xPtr;
- } else {
- TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
- ciPtr->chars + index, 1, *xPtr, 1000000, 0, TK_IGNORE_TABS,
- widthPtr);
- if (*widthPtr > maxX) {
- *widthPtr = maxX - *xPtr;
- } else {
- *widthPtr -= *xPtr;
- }
- }
- *yPtr = y + baseline - chunkPtr->minAscent;
- *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * AdjustForTab --
- *
- * This procedure is called to move a series of chunks right
- * in order to align them with a tab stop.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The width of chunkPtr gets adjusted so that it absorbs the
- * extra space due to the tab. The x locations in all the chunks
- * after chunkPtr are adjusted rightward to align with the tab
- * stop given by tabArrayPtr and index.
- *
- *----------------------------------------------------------------------
- */
-
- static void
- AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
- TkText *textPtr; /* Information about the text widget as
- * a whole. */
- TkTextTabArray *tabArrayPtr; /* Information about the tab stops
- * that apply to this line. May be
- * NULL to indicate default tabbing
- * (every 8 chars). */
- int index; /* Index of current tab stop. */
- TkTextDispChunk *chunkPtr; /* Chunk whose last character is
- * the tab; the following chunks
- * contain information to be shifted
- * right. */
-
- {
- int x, desired, delta, width, decimal, i, gotDigit;
- TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
- TkTextTab *tabPtr;
- CharInfo *ciPtr = NULL; /* Initialization needed only to
- * prevent compiler warnings. */
- int tabX, prev, spaceWidth;
- char *p;
- TkTextTabAlign alignment;
-
- if (chunkPtr->nextPtr == NULL) {
- /*
- * Nothing after the actual tab; just return.
- */
-
- return;
- }
-
- /*
- * If no tab information has been given, do the usual thing:
- * round up to the next boundary of 8 average-sized characters.
- */
-
- x = chunkPtr->nextPtr->x;
- if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
- /*
- * No tab information has been given, so use the default
- * interpretation of tabs.
- */
-
- TkMeasureChars(textPtr->fontPtr, "\t", 1, x, INT_MAX, 0, 0, &desired);
- goto update;
- }
-
- if (index < tabArrayPtr->numTabs) {
- alignment = tabArrayPtr->tabs[index].alignment;
- tabX = tabArrayPtr->tabs[index].location;
- } else {
- /*
- * Ran out of tab stops; compute a tab position by extrapolating
- * from the last two tab positions.
- */
-
- if (tabArrayPtr->numTabs > 1) {
- prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
- } else {
- prev = 0;
- }
- alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
- tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
- + (index + 1 - tabArrayPtr->numTabs)
- * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
- }
-
- tabPtr = &tabArrayPtr->tabs[index];
- if (alignment == LEFT) {
- desired = tabX;
- goto update;
- }
-
- if ((alignment == CENTER) || (alignment == RIGHT)) {
- /*
- * Compute the width of all the information in the tab group,
- * then use it to pick a desired location.
- */
-
- width = 0;
- for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
- chunkPtr2 = chunkPtr2->nextPtr) {
- width += chunkPtr2->width;
- }
- if (alignment == CENTER) {
- desired = tabX - width/2;
- } else {
- desired = tabX - width;
- }
- goto update;
- }
-
- /*
- * Must be numeric alignment. Search through the text to be
- * tabbed, looking for the last , or . before the first character
- * that isn't a number, comma, period, or sign.
- */
-
- decimalChunkPtr = NULL;
- decimal = gotDigit = 0;
- for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
- chunkPtr2 = chunkPtr2->nextPtr) {
- if (chunkPtr2->displayProc != CharDisplayProc) {
- continue;
- }
- ciPtr = (CharInfo *) chunkPtr2->clientData;
- for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
- if (isdigit(UCHAR(*p))) {
- gotDigit = 1;
- } else if ((*p == '.') || (*p == ',')) {
- decimal = p-ciPtr->chars;
- decimalChunkPtr = chunkPtr2;
- } else if (gotDigit) {
- if (decimalChunkPtr == NULL) {
- decimal = p-ciPtr->chars;
- decimalChunkPtr = chunkPtr2;
- }
- goto endOfNumber;
- }
- }
- }
- endOfNumber:
- if (decimalChunkPtr != NULL) {
- int curX;
-
- ciPtr = (CharInfo *) decimalChunkPtr->clientData;
- TkMeasureChars(decimalChunkPtr->stylePtr->sValuePtr->fontPtr,
- ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0,
- TK_IGNORE_TABS, &curX);
- desired = tabX - (curX - x);
- goto update;
- } else {
- /*
- * There wasn't a decimal point. Right justify the text.
- */
-
- width = 0;
- for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
- chunkPtr2 = chunkPtr2->nextPtr) {
- width += chunkPtr2->width;
- }
- desired = tabX - width;
- }
-
- /*
- * Shift all of the chunks to the right so that the left edge is
- * at the desired location, then expand the chunk containing the
- * tab. Be sure that the tab occupies at least the width of a
- * space character.
- */
-
- update:
- delta = desired - x;
- TkMeasureChars(textPtr->fontPtr, " ", 1, 0, INT_MAX, 0, 0, &spaceWidth);
- if (delta < spaceWidth) {
- delta = spaceWidth;
- }
- for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
- chunkPtr2 = chunkPtr2->nextPtr) {
- chunkPtr2->x += delta;
- }
- chunkPtr->width += delta;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * SizeOfTab --
- *
- * This returns an estimate of the amount of white space that will
- * be consumed by a tab.
- *
- * Results:
- * The return value is the minimum number of pixels that will
- * be occupied by the index'th tab of tabArrayPtr, assuming that
- * the current position on the line is x and the end of the
- * line is maxX. For numeric tabs, this is a conservative
- * estimate. The return value is always >= 0.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
- TkText *textPtr; /* Information about the text widget as
- * a whole. */
- TkTextTabArray *tabArrayPtr; /* Information about the tab stops
- * that apply to this line. NULL
- * means use default tabbing (every
- * 8 chars.) */
- int index; /* Index of current tab stop. */
- int x; /* Current x-location in line. Only
- * used if tabArrayPtr == NULL. */
- int maxX; /* X-location of pixel just past the
- * right edge of the line. */
- {
- int tabX, prev, result, spaceWidth;
- TkTextTabAlign alignment;
-
- if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
- TkMeasureChars(textPtr->fontPtr, "\t", 1, x, INT_MAX, 0, 0, &tabX);
- return tabX - x;
- }
- if (index < tabArrayPtr->numTabs) {
- tabX = tabArrayPtr->tabs[index].location;
- alignment = tabArrayPtr->tabs[index].alignment;
- } else {
- /*
- * Ran out of tab stops; compute a tab position by extrapolating
- * from the last two tab positions.
- */
-
- if (tabArrayPtr->numTabs > 1) {
- prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
- } else {
- prev = 0;
- }
- tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
- + (index + 1 - tabArrayPtr->numTabs)
- * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
- alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
- }
- if (alignment == CENTER) {
- /*
- * Be very careful in the arithmetic below, because maxX may
- * be the largest positive number: watch out for integer
- * overflow.
- */
-
- if ((maxX-tabX) < (tabX - x)) {
- result = (maxX - x) - 2*(maxX - tabX);
- } else {
- result = 0;
- }
- goto done;
- }
- if (alignment == RIGHT) {
- result = 0;
- goto done;
- }
-
- /*
- * Note: this treats NUMERIC alignment the same as LEFT
- * alignment, which is somewhat conservative. However, it's
- * pretty tricky at this point to figure out exactly where
- * the damn decimal point will be.
- */
-
- if (tabX > x) {
- result = tabX - x;
- } else {
- result = 0;
- }
-
- done:
- TkMeasureChars(textPtr->fontPtr, " ", 1, 0, INT_MAX, 0, 0, &spaceWidth);
- if (result < spaceWidth) {
- result = spaceWidth;
- }
- return result;
- }
-