home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkTextDisp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  148.6 KB  |  5,016 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkTextDisp.c --
  3.  *
  4.  *    This module provides facilities to display text widgets.  It is
  5.  *    the only place where information is kept about the screen layout
  6.  *    of text widgets.
  7.  *
  8.  * Copyright (c) 1992-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkTextDisp.c 1.124 97/07/11 18:01:03
  15.  */
  16.  
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19. #include "tkText.h"
  20.  
  21. /*
  22.  * The following structure describes how to display a range of characters.
  23.  * The information is generated by scanning all of the tags associated
  24.  * with the characters and combining that with default information for
  25.  * the overall widget.  These structures form the hash keys for
  26.  * dInfoPtr->styleTable.
  27.  */
  28.  
  29. typedef struct StyleValues {
  30.     Tk_3DBorder border;        /* Used for drawing background under text.
  31.                  * NULL means use widget background. */
  32.     int borderWidth;        /* Width of 3-D border for background. */
  33.     int relief;            /* 3-D relief for background. */
  34.     Pixmap bgStipple;        /* Stipple bitmap for background.  None
  35.                  * means draw solid. */
  36.     XColor *fgColor;        /* Foreground color for text. */
  37.     Tk_Font tkfont;        /* Font for displaying text. */
  38.     Pixmap fgStipple;        /* Stipple bitmap for text and other
  39.                  * foreground stuff.   None means draw
  40.                  * solid.*/
  41.     int justify;        /* Justification style for text. */
  42.     int lMargin1;        /* Left margin, in pixels, for first display
  43.                  * line of each text line. */
  44.     int lMargin2;        /* Left margin, in pixels, for second and
  45.                  * later display lines of each text line. */
  46.     int offset;            /* Offset in pixels of baseline, relative to
  47.                  * baseline of line. */
  48.     int overstrike;        /* Non-zero means draw overstrike through
  49.                  * text. */
  50.     int rMargin;        /* Right margin, in pixels. */
  51.     int spacing1;        /* Spacing above first dline in text line. */
  52.     int spacing2;        /* Spacing between lines of dline. */
  53.     int spacing3;        /* Spacing below last dline in text line. */
  54.     TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
  55.                  * be NULL). */
  56.     int underline;        /* Non-zero means draw underline underneath
  57.                  * text. */
  58.     Tk_Uid wrapMode;        /* How to handle wrap-around for this tag.
  59.                  * One of tkTextCharUid, tkTextNoneUid,
  60.                  * or tkTextWordUid. */
  61. } StyleValues;
  62.  
  63. /*
  64.  * The following structure extends the StyleValues structure above with
  65.  * graphics contexts used to actually draw the characters.  The entries
  66.  * in dInfoPtr->styleTable point to structures of this type.
  67.  */
  68.  
  69. typedef struct TextStyle {
  70.     int refCount;        /* Number of times this structure is
  71.                  * referenced in Chunks. */
  72.     GC bgGC;            /* Graphics context for background.  None
  73.                  * means use widget background. */
  74.     GC fgGC;            /* Graphics context for foreground. */
  75.     StyleValues *sValuePtr;    /* Raw information from which GCs were
  76.                  * derived. */
  77.     Tcl_HashEntry *hPtr;    /* Pointer to entry in styleTable.  Used
  78.                  * to delete entry. */
  79. } TextStyle;
  80.  
  81. /*
  82.  * The following macro determines whether two styles have the same
  83.  * background so that, for example, no beveled border should be drawn
  84.  * between them.
  85.  */
  86.  
  87. #define SAME_BACKGROUND(s1, s2) \
  88.     (((s1)->sValuePtr->border == (s2)->sValuePtr->border) \
  89.         && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) \
  90.         && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) \
  91.         && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
  92.  
  93. /*
  94.  * The following structure describes one line of the display, which may
  95.  * be either part or all of one line of the text.
  96.  */
  97.  
  98. typedef struct DLine {
  99.     TkTextIndex index;        /* Identifies first character in text
  100.                  * that is displayed on this line. */
  101.     int count;            /* Number of characters accounted for by this
  102.                  * display line, including a trailing space
  103.                  * or newline that isn't actually displayed. */
  104.     int y;            /* Y-position at which line is supposed to
  105.                  * be drawn (topmost pixel of rectangular
  106.                  * area occupied by line). */
  107.     int oldY;            /* Y-position at which line currently
  108.                  * appears on display.  -1 means line isn't
  109.                  * currently visible on display and must be
  110.                  * redrawn.  This is used to move lines by
  111.                  * scrolling rather than re-drawing. */
  112.     int height;            /* Height of line, in pixels. */
  113.     int baseline;        /* Offset of text baseline from y, in
  114.                  * pixels. */
  115.     int spaceAbove;        /* How much extra space was added to the
  116.                  * top of the line because of spacing
  117.                  * options.  This is included in height
  118.                  * and baseline. */
  119.     int spaceBelow;        /* How much extra space was added to the
  120.                  * bottom of the line because of spacing
  121.                  * options.  This is included in height. */
  122.     int length;            /* Total length of line, in pixels. */
  123.     TkTextDispChunk *chunkPtr;    /* Pointer to first chunk in list of all
  124.                  * of those that are displayed on this
  125.                  * line of the screen. */
  126.     struct DLine *nextPtr;    /* Next in list of all display lines for
  127.                  * this window.   The list is sorted in
  128.                  * order from top to bottom.  Note:  the
  129.                  * next DLine doesn't always correspond
  130.                  * to the next line of text:  (a) can have
  131.                  * multiple DLines for one text line, and
  132.                  * (b) can have gaps where DLine's have been
  133.                  * deleted because they're out of date. */
  134.     int flags;            /* Various flag bits:  see below for values. */
  135. } DLine;
  136.  
  137. /*
  138.  * Flag bits for DLine structures:
  139.  *
  140.  * HAS_3D_BORDER -        Non-zero means that at least one of the
  141.  *                chunks in this line has a 3D border, so
  142.  *                it potentially interacts with 3D borders
  143.  *                in neighboring lines (see
  144.  *                DisplayLineBackground).
  145.  * NEW_LAYOUT -            Non-zero means that the line has been
  146.  *                re-layed out since the last time the
  147.  *                display was updated.
  148.  * TOP_LINE -            Non-zero means that this was the top line
  149.  *                in the window the last time that the window
  150.  *                was laid out.  This is important because
  151.  *                a line may be displayed differently if its
  152.  *                at the top or bottom than if it's in the
  153.  *                middle (e.g. beveled edges aren't displayed
  154.  *                for middle lines if the adjacent line has
  155.  *                a similar background).
  156.  * BOTTOM_LINE -        Non-zero means that this was the bottom line
  157.  *                in the window the last time that the window
  158.  *                was laid out.
  159.  */
  160.  
  161. #define HAS_3D_BORDER    1
  162. #define NEW_LAYOUT    2
  163. #define TOP_LINE    4
  164. #define BOTTOM_LINE    8
  165.  
  166. /*
  167.  * Overall display information for a text widget:
  168.  */
  169.  
  170. typedef struct TextDInfo {
  171.     Tcl_HashTable styleTable;    /* Hash table that maps from StyleValues
  172.                  * to TextStyles for this widget. */
  173.     DLine *dLinePtr;        /* First in list of all display lines for
  174.                  * this widget, in order from top to bottom. */
  175.     GC copyGC;            /* Graphics context for copying from off-
  176.                  * screen pixmaps onto screen. */
  177.     GC scrollGC;        /* Graphics context for copying from one place
  178.                  * in the window to another (scrolling):
  179.                  * differs from copyGC in that we need to get
  180.                  * GraphicsExpose events. */
  181.     int x;            /* First x-coordinate that may be used for
  182.                  * actually displaying line information.
  183.                  * Leaves space for border, etc. */
  184.     int y;            /* First y-coordinate that may be used for
  185.                  * actually displaying line information.
  186.                  * Leaves space for border, etc. */
  187.     int maxX;            /* First x-coordinate to right of available
  188.                  * space for displaying lines. */
  189.     int maxY;            /* First y-coordinate below available
  190.                  * space for displaying lines. */
  191.     int topOfEof;        /* Top-most pixel (lowest y-value) that has
  192.                  * been drawn in the appropriate fashion for
  193.                  * the portion of the window after the last
  194.                  * line of the text.  This field is used to
  195.                  * figure out when to redraw part or all of
  196.                  * the eof field. */
  197.  
  198.     /*
  199.      * Information used for scrolling:
  200.      */
  201.  
  202.     int newCharOffset;        /* Desired x scroll position, measured as the
  203.                  * number of average-size characters off-screen
  204.                  * to the left for a line with no left
  205.                  * margin. */
  206.     int curPixelOffset;        /* Actual x scroll position, measured as the
  207.                  * number of pixels off-screen to the left. */
  208.     int maxLength;        /* Length in pixels of longest line that's
  209.                  * visible in window (length may exceed window
  210.                  * size).  If there's no wrapping, this will
  211.                  * be zero. */
  212.     double xScrollFirst, xScrollLast;
  213.                 /* Most recent values reported to horizontal
  214.                  * scrollbar;  used to eliminate unnecessary
  215.                  * reports. */
  216.     double yScrollFirst, yScrollLast;
  217.                 /* Most recent values reported to vertical
  218.                  * scrollbar;  used to eliminate unnecessary
  219.                  * reports. */
  220.  
  221.     /*
  222.      * The following information is used to implement scanning:
  223.      */
  224.  
  225.     int scanMarkChar;        /* Character that was at the left edge of
  226.                  * the window when the scan started. */
  227.     int scanMarkX;        /* X-position of mouse at time scan started. */
  228.     int scanTotalScroll;    /* Total scrolling (in screen lines) that has
  229.                  * occurred since scanMarkY was set. */
  230.     int scanMarkY;        /* Y-position of mouse at time scan started. */
  231.  
  232.     /*
  233.      * Miscellaneous information:
  234.      */
  235.  
  236.     int dLinesInvalidated;    /* This value is set to 1 whenever something
  237.                  * happens that invalidates information in
  238.                  * DLine structures;  if a redisplay
  239.                  * is in progress, it will see this and
  240.                  * abort the redisplay.  This is needed
  241.                  * because, for example, an embedded window
  242.                  * could change its size when it is first
  243.                  * displayed, invalidating the DLine that
  244.                  * is currently being displayed.  If redisplay
  245.                  * continues, it will use freed memory and
  246.                  * could dump core. */
  247.     int flags;            /* Various flag values:  see below for
  248.                  * definitions. */
  249. } TextDInfo;
  250.  
  251. /*
  252.  * In TkTextDispChunk structures for character segments, the clientData
  253.  * field points to one of the following structures:
  254.  */
  255.  
  256. typedef struct CharInfo {
  257.     int numChars;        /* Number of characters to display. */
  258.     char chars[4];        /* Characters to display.  Actual size
  259.                  * will be numChars, not 4.  THIS MUST BE
  260.                  * THE LAST FIELD IN THE STRUCTURE. */
  261. } CharInfo;
  262.  
  263. /*
  264.  * Flag values for TextDInfo structures:
  265.  *
  266.  * DINFO_OUT_OF_DATE:        Non-zero means that the DLine structures
  267.  *                for this window are partially or completely
  268.  *                out of date and need to be recomputed.
  269.  * REDRAW_PENDING:        Means that a when-idle handler has been
  270.  *                scheduled to update the display.
  271.  * REDRAW_BORDERS:        Means window border or pad area has
  272.  *                potentially been damaged and must be redrawn.
  273.  * REPICK_NEEDED:        1 means that the widget has been modified
  274.  *                in a way that could change the current
  275.  *                character (a different character might be
  276.  *                under the mouse cursor now).  Need to
  277.  *                recompute the current character before
  278.  *                the next redisplay.
  279.  */
  280.  
  281. #define DINFO_OUT_OF_DATE    1
  282. #define REDRAW_PENDING        2
  283. #define REDRAW_BORDERS        4
  284. #define REPICK_NEEDED        8
  285.  
  286. /*
  287.  * The following counters keep statistics about redisplay that can be
  288.  * checked to see how clever this code is at reducing redisplays.
  289.  */
  290.  
  291. static int numRedisplays;    /* Number of calls to DisplayText. */
  292. static int linesRedrawn;    /* Number of calls to DisplayDLine. */
  293. static int numCopies;        /* Number of calls to XCopyArea to copy part
  294.                  * of the screen. */
  295.  
  296. /*
  297.  * Forward declarations for procedures defined later in this file:
  298.  */
  299.  
  300. static void        AdjustForTab _ANSI_ARGS_((TkText *textPtr,
  301.                 TkTextTabArray *tabArrayPtr, int index,
  302.                 TkTextDispChunk *chunkPtr));
  303. static void        CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  304.                 int index, int y, int lineHeight, int baseline,
  305.                 int *xPtr, int *yPtr, int *widthPtr,
  306.                 int *heightPtr));
  307. static void        CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  308.                 int x, int y, int height, int baseline,
  309.                 Display *display, Drawable dst, int screenY));
  310. static int        CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  311.                 int x));
  312. static void        CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  313.                 TkTextDispChunk *chunkPtr));
  314. static void        DisplayDLine _ANSI_ARGS_((TkText *textPtr,
  315.                 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  316. static void        DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
  317.                 DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  318. static void        DisplayText _ANSI_ARGS_((ClientData clientData));
  319. static DLine *        FindDLine _ANSI_ARGS_((DLine *dlPtr,
  320.                 TkTextIndex *indexPtr));
  321. static void        FreeDLines _ANSI_ARGS_((TkText *textPtr,
  322.                 DLine *firstPtr, DLine *lastPtr, int unlink));
  323. static void        FreeStyle _ANSI_ARGS_((TkText *textPtr,
  324.                 TextStyle *stylePtr));
  325. static TextStyle *    GetStyle _ANSI_ARGS_((TkText *textPtr,
  326.                 TkTextIndex *indexPtr));
  327. static void        GetXView _ANSI_ARGS_((Tcl_Interp *interp,
  328.                 TkText *textPtr, int report));
  329. static void        GetYView _ANSI_ARGS_((Tcl_Interp *interp,
  330.                 TkText *textPtr, int report));
  331. static DLine *        LayoutDLine _ANSI_ARGS_((TkText *textPtr,
  332.                 TkTextIndex *indexPtr));
  333. static int        MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
  334.                 CONST char *source, int maxChars, int startX,
  335.                 int maxX, int tabOrigin, int *nextXPtr));
  336. static void        MeasureUp _ANSI_ARGS_((TkText *textPtr,
  337.                 TkTextIndex *srcPtr, int distance,
  338.                 TkTextIndex *dstPtr));
  339. static int        NextTabStop _ANSI_ARGS_((Tk_Font tkfont, int x,
  340.                 int tabOrigin));
  341. static void        UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
  342. static void        ScrollByLines _ANSI_ARGS_((TkText *textPtr,
  343.                 int offset));
  344. static int        SizeOfTab _ANSI_ARGS_((TkText *textPtr,
  345.                 TkTextTabArray *tabArrayPtr, int index, int x,
  346.                 int maxX));
  347. static void        TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
  348.                 TkRegion region));
  349.  
  350.  
  351. /*
  352.  *----------------------------------------------------------------------
  353.  *
  354.  * TkTextCreateDInfo --
  355.  *
  356.  *    This procedure is called when a new text widget is created.
  357.  *    Its job is to set up display-related information for the widget.
  358.  *
  359.  * Results:
  360.  *    None.
  361.  *
  362.  * Side effects:
  363.  *    A TextDInfo data structure is allocated and initialized and attached
  364.  *    to textPtr.
  365.  *
  366.  *----------------------------------------------------------------------
  367.  */
  368.  
  369. void
  370. TkTextCreateDInfo(textPtr)
  371.     TkText *textPtr;        /* Overall information for text widget. */
  372. {
  373.     register TextDInfo *dInfoPtr;
  374.     XGCValues gcValues;
  375.  
  376.     dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
  377.     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
  378.     dInfoPtr->dLinePtr = NULL;
  379.     dInfoPtr->copyGC = None;
  380.     gcValues.graphics_exposures = True;
  381.     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
  382.         &gcValues);
  383.     dInfoPtr->topOfEof = 0;
  384.     dInfoPtr->newCharOffset = 0;
  385.     dInfoPtr->curPixelOffset = 0;
  386.     dInfoPtr->maxLength = 0;
  387.     dInfoPtr->xScrollFirst = -1;
  388.     dInfoPtr->xScrollLast = -1;
  389.     dInfoPtr->yScrollFirst = -1;
  390.     dInfoPtr->yScrollLast = -1;
  391.     dInfoPtr->scanMarkChar = 0;
  392.     dInfoPtr->scanMarkX = 0;
  393.     dInfoPtr->scanTotalScroll = 0;
  394.     dInfoPtr->scanMarkY = 0;
  395.     dInfoPtr->dLinesInvalidated = 0;
  396.     dInfoPtr->flags = DINFO_OUT_OF_DATE;
  397.     textPtr->dInfoPtr = dInfoPtr;
  398. }
  399.  
  400. /*
  401.  *----------------------------------------------------------------------
  402.  *
  403.  * TkTextFreeDInfo --
  404.  *
  405.  *    This procedure is called to free up all of the private display
  406.  *    information kept by this file for a text widget.
  407.  *
  408.  * Results:
  409.  *    None.
  410.  *
  411.  * Side effects:
  412.  *    Lots of resources get freed.
  413.  *
  414.  *----------------------------------------------------------------------
  415.  */
  416.  
  417. void
  418. TkTextFreeDInfo(textPtr)
  419.     TkText *textPtr;        /* Overall information for text widget. */
  420. {
  421.     register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  422.  
  423.     /*
  424.      * Be careful to free up styleTable *after* freeing up all the
  425.      * DLines, so that the hash table is still intact to free up the
  426.      * style-related information from the lines.  Once the lines are
  427.      * all free then styleTable will be empty.
  428.      */
  429.  
  430.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  431.     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
  432.     if (dInfoPtr->copyGC != None) {
  433.     Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  434.     }
  435.     Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
  436.     if (dInfoPtr->flags & REDRAW_PENDING) {
  437.     Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
  438.     }
  439.     ckfree((char *) dInfoPtr);
  440. }
  441.  
  442. /*
  443.  *----------------------------------------------------------------------
  444.  *
  445.  * GetStyle --
  446.  *
  447.  *    This procedure creates all the information needed to display
  448.  *    text at a particular location.
  449.  *
  450.  * Results:
  451.  *    The return value is a pointer to a TextStyle structure that
  452.  *    corresponds to *sValuePtr.
  453.  *
  454.  * Side effects:
  455.  *    A new entry may be created in the style table for the widget.
  456.  *
  457.  *----------------------------------------------------------------------
  458.  */
  459.  
  460. static TextStyle *
  461. GetStyle(textPtr, indexPtr)
  462.     TkText *textPtr;        /* Overall information about text widget. */
  463.     TkTextIndex *indexPtr;    /* The character in the text for which
  464.                  * display information is wanted. */
  465. {
  466.     TkTextTag **tagPtrs;
  467.     register TkTextTag *tagPtr;
  468.     StyleValues styleValues;
  469.     TextStyle *stylePtr;
  470.     Tcl_HashEntry *hPtr;
  471.     int numTags, new, i;
  472.     XGCValues gcValues;
  473.     unsigned long mask;
  474.  
  475.     /*
  476.      * The variables below keep track of the highest-priority specification
  477.      * that has occurred for each of the various fields of the StyleValues.
  478.      */
  479.  
  480.     int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
  481.     int fgPrio, fontPrio, fgStipplePrio;
  482.     int underlinePrio, justifyPrio, offsetPrio;
  483.     int lMargin1Prio, lMargin2Prio, rMarginPrio;
  484.     int spacing1Prio, spacing2Prio, spacing3Prio;
  485.     int overstrikePrio, tabPrio, wrapPrio;
  486.  
  487.     /*
  488.      * Find out what tags are present for the character, then compute
  489.      * a StyleValues structure corresponding to those tags (scan
  490.      * through all of the tags, saving information for the highest-
  491.      * priority tag).
  492.      */
  493.  
  494.     tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
  495.     borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
  496.     fgPrio = fontPrio = fgStipplePrio = -1;
  497.     underlinePrio = justifyPrio = offsetPrio = -1;
  498.     lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
  499.     spacing1Prio = spacing2Prio = spacing3Prio = -1;
  500.     overstrikePrio = tabPrio = wrapPrio = -1;
  501.     memset((VOID *) &styleValues, 0, sizeof(StyleValues));
  502.     styleValues.relief = TK_RELIEF_FLAT;
  503.     styleValues.fgColor = textPtr->fgColor;
  504.     styleValues.tkfont = textPtr->tkfont;
  505.     styleValues.justify = TK_JUSTIFY_LEFT;
  506.     styleValues.spacing1 = textPtr->spacing1;
  507.     styleValues.spacing2 = textPtr->spacing2;
  508.     styleValues.spacing3 = textPtr->spacing3;
  509.     styleValues.tabArrayPtr = textPtr->tabArrayPtr;
  510.     styleValues.wrapMode = textPtr->wrapMode;
  511.     for (i = 0 ; i < numTags; i++) {
  512.     tagPtr = tagPtrs[i];
  513.  
  514.     /*
  515.      * On Windows and Mac, we need to skip the selection tag if
  516.      * we don't have focus.
  517.      */
  518.  
  519. #ifndef ALWAYS_SHOW_SELECTION
  520.     if ((tagPtr == textPtr->selTagPtr) && !(textPtr->flags & GOT_FOCUS)) {
  521.         continue;
  522.     }
  523. #endif
  524.  
  525.     if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
  526.         styleValues.border = tagPtr->border;
  527.         borderPrio = tagPtr->priority;
  528.     }
  529.     if ((tagPtr->bdString != NULL)
  530.         && (tagPtr->priority > borderWidthPrio)) {
  531.         styleValues.borderWidth = tagPtr->borderWidth;
  532.         borderWidthPrio = tagPtr->priority;
  533.     }
  534.     if ((tagPtr->reliefString != NULL)
  535.         && (tagPtr->priority > reliefPrio)) {
  536.         if (styleValues.border == NULL) {
  537.         styleValues.border = textPtr->border;
  538.         }
  539.         styleValues.relief = tagPtr->relief;
  540.         reliefPrio = tagPtr->priority;
  541.     }
  542.     if ((tagPtr->bgStipple != None)
  543.         && (tagPtr->priority > bgStipplePrio)) {
  544.         styleValues.bgStipple = tagPtr->bgStipple;
  545.         bgStipplePrio = tagPtr->priority;
  546.     }
  547.     if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
  548.         styleValues.fgColor = tagPtr->fgColor;
  549.         fgPrio = tagPtr->priority;
  550.     }
  551.     if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
  552.         styleValues.tkfont = tagPtr->tkfont;
  553.         fontPrio = tagPtr->priority;
  554.     }
  555.     if ((tagPtr->fgStipple != None)
  556.         && (tagPtr->priority > fgStipplePrio)) {
  557.         styleValues.fgStipple = tagPtr->fgStipple;
  558.         fgStipplePrio = tagPtr->priority;
  559.     }
  560.     if ((tagPtr->justifyString != NULL)
  561.         && (tagPtr->priority > justifyPrio)) {
  562.         styleValues.justify = tagPtr->justify;
  563.         justifyPrio = tagPtr->priority;
  564.     }
  565.     if ((tagPtr->lMargin1String != NULL)
  566.         && (tagPtr->priority > lMargin1Prio)) {
  567.         styleValues.lMargin1 = tagPtr->lMargin1;
  568.         lMargin1Prio = tagPtr->priority;
  569.     }
  570.     if ((tagPtr->lMargin2String != NULL)
  571.         && (tagPtr->priority > lMargin2Prio)) {
  572.         styleValues.lMargin2 = tagPtr->lMargin2;
  573.         lMargin2Prio = tagPtr->priority;
  574.     }
  575.     if ((tagPtr->offsetString != NULL)
  576.         && (tagPtr->priority > offsetPrio)) {
  577.         styleValues.offset = tagPtr->offset;
  578.         offsetPrio = tagPtr->priority;
  579.     }
  580.     if ((tagPtr->overstrikeString != NULL)
  581.         && (tagPtr->priority > overstrikePrio)) {
  582.         styleValues.overstrike = tagPtr->overstrike;
  583.         overstrikePrio = tagPtr->priority;
  584.     }
  585.     if ((tagPtr->rMarginString != NULL)
  586.         && (tagPtr->priority > rMarginPrio)) {
  587.         styleValues.rMargin = tagPtr->rMargin;
  588.         rMarginPrio = tagPtr->priority;
  589.     }
  590.     if ((tagPtr->spacing1String != NULL)
  591.         && (tagPtr->priority > spacing1Prio)) {
  592.         styleValues.spacing1 = tagPtr->spacing1;
  593.         spacing1Prio = tagPtr->priority;
  594.     }
  595.     if ((tagPtr->spacing2String != NULL)
  596.         && (tagPtr->priority > spacing2Prio)) {
  597.         styleValues.spacing2 = tagPtr->spacing2;
  598.         spacing2Prio = tagPtr->priority;
  599.     }
  600.     if ((tagPtr->spacing3String != NULL)
  601.         && (tagPtr->priority > spacing3Prio)) {
  602.         styleValues.spacing3 = tagPtr->spacing3;
  603.         spacing3Prio = tagPtr->priority;
  604.     }
  605.     if ((tagPtr->tabString != NULL)
  606.         && (tagPtr->priority > tabPrio)) {
  607.         styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
  608.         tabPrio = tagPtr->priority;
  609.     }
  610.     if ((tagPtr->underlineString != NULL)
  611.         && (tagPtr->priority > underlinePrio)) {
  612.         styleValues.underline = tagPtr->underline;
  613.         underlinePrio = tagPtr->priority;
  614.     }
  615.     if ((tagPtr->wrapMode != NULL)
  616.         && (tagPtr->priority > wrapPrio)) {
  617.         styleValues.wrapMode = tagPtr->wrapMode;
  618.         wrapPrio = tagPtr->priority;
  619.     }
  620.     }
  621.     if (tagPtrs != NULL) {
  622.     ckfree((char *) tagPtrs);
  623.     }
  624.  
  625.     /*
  626.      * Use an existing style if there's one around that matches.
  627.      */
  628.  
  629.     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
  630.         (char *) &styleValues, &new);
  631.     if (!new) {
  632.     stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
  633.     stylePtr->refCount++;
  634.     return stylePtr;
  635.     }
  636.  
  637.     /*
  638.      * No existing style matched.  Make a new one.
  639.      */
  640.  
  641.     stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
  642.     stylePtr->refCount = 1;
  643.     if (styleValues.border != NULL) {
  644.     gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
  645.     mask = GCForeground;
  646.     if (styleValues.bgStipple != None) {
  647.         gcValues.stipple = styleValues.bgStipple;
  648.         gcValues.fill_style = FillStippled;
  649.         mask |= GCStipple|GCFillStyle;
  650.     }
  651.     stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  652.     } else {
  653.     stylePtr->bgGC = None;
  654.     }
  655.     mask = GCForeground|GCFont;
  656.     gcValues.foreground = styleValues.fgColor->pixel;
  657.     gcValues.font = Tk_FontId(styleValues.tkfont);
  658.     if (styleValues.fgStipple != None) {
  659.     gcValues.stipple = styleValues.fgStipple;
  660.     gcValues.fill_style = FillStippled;
  661.     mask |= GCStipple|GCFillStyle;
  662.     }
  663.     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  664.     stylePtr->sValuePtr = (StyleValues *)
  665.         Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
  666.     stylePtr->hPtr = hPtr;
  667.     Tcl_SetHashValue(hPtr, stylePtr);
  668.     return stylePtr;
  669. }
  670.  
  671. /*
  672.  *----------------------------------------------------------------------
  673.  *
  674.  * FreeStyle --
  675.  *
  676.  *    This procedure is called when a TextStyle structure is no longer
  677.  *    needed.  It decrements the reference count and frees up the
  678.  *    space for the style structure if the reference count is 0.
  679.  *
  680.  * Results:
  681.  *    None.
  682.  *
  683.  * Side effects:
  684.  *    The storage and other resources associated with the style
  685.  *    are freed up if no-one's still using it.
  686.  *
  687.  *----------------------------------------------------------------------
  688.  */
  689.  
  690. static void
  691. FreeStyle(textPtr, stylePtr)
  692.     TkText *textPtr;            /* Information about overall widget. */
  693.     register TextStyle *stylePtr;    /* Information about style to free. */
  694.  
  695. {
  696.     stylePtr->refCount--;
  697.     if (stylePtr->refCount == 0) {
  698.     if (stylePtr->bgGC != None) {
  699.         Tk_FreeGC(textPtr->display, stylePtr->bgGC);
  700.     }
  701.     Tk_FreeGC(textPtr->display, stylePtr->fgGC);
  702.     Tcl_DeleteHashEntry(stylePtr->hPtr);
  703.     ckfree((char *) stylePtr);
  704.     }
  705. }
  706.  
  707. /*
  708.  *----------------------------------------------------------------------
  709.  *
  710.  * LayoutDLine --
  711.  *
  712.  *    This procedure generates a single DLine structure for a display
  713.  *    line whose leftmost character is given by indexPtr.
  714.  *    
  715.  * Results:
  716.  *    The return value is a pointer to a DLine structure desribing the
  717.  *    display line.  All fields are filled in and correct except for
  718.  *    y and nextPtr.
  719.  *
  720.  * Side effects:
  721.  *    Storage is allocated for the new DLine.
  722.  *
  723.  *----------------------------------------------------------------------
  724.  */
  725.  
  726. static DLine *
  727. LayoutDLine(textPtr, indexPtr)
  728.     TkText *textPtr;        /* Overall information about text widget. */
  729.     TkTextIndex *indexPtr;    /* Beginning of display line.  May not
  730.                  * necessarily point to a character segment. */
  731. {
  732.     register DLine *dlPtr;        /* New display line. */
  733.     TkTextSegment *segPtr;        /* Current segment in text. */
  734.     TkTextDispChunk *lastChunkPtr;    /* Last chunk allocated so far
  735.                      * for line. */
  736.     TkTextDispChunk *chunkPtr;        /* Current chunk. */
  737.     TkTextIndex curIndex;
  738.     TkTextDispChunk *breakChunkPtr;    /* Chunk containing best word break
  739.                      * point, if any. */
  740.     TkTextIndex breakIndex;        /* Index of first character in
  741.                      * breakChunkPtr. */
  742.     int breakCharOffset;        /* Character within breakChunkPtr just
  743.                      * to right of best break point. */
  744.     int noCharsYet;            /* Non-zero means that no characters
  745.                      * have been placed on the line yet. */
  746.     int justify;            /* How to justify line: taken from
  747.                      * style for first character in line. */
  748.     int jIndent;            /* Additional indentation (beyond
  749.                      * margins) due to justification. */
  750.     int rMargin;            /* Right margin width for line. */
  751.     Tk_Uid wrapMode;            /* Wrap mode to use for this line. */
  752.     int x = 0, maxX = 0;        /* Initializations needed only to
  753.                      * stop compiler warnings. */
  754.     int wholeLine;            /* Non-zero means this display line
  755.                      * runs to the end of the text line. */
  756.     int tabIndex;            /* Index of the current tab stop. */
  757.     int gotTab;                /* Non-zero means the current chunk
  758.                      * contains a tab. */
  759.     TkTextDispChunk *tabChunkPtr;    /* Pointer to the chunk containing
  760.                      * the previous tab stop. */
  761.     int maxChars;            /* Maximum number of characters to
  762.                      * include in this chunk. */
  763.     TkTextTabArray *tabArrayPtr;    /* Tab stops for line;  taken from
  764.                      * style for first character on line. */
  765.     int tabSize;            /* Number of pixels consumed by current
  766.                      * tab stop. */
  767.     TkTextDispChunk *lastCharChunkPtr;    /* Pointer to last chunk in display
  768.                      * lines with numChars > 0.  Used to
  769.                      * drop 0-sized chunks from the end
  770.                      * of the line. */
  771.     int offset, ascent, descent, code;
  772.     StyleValues *sValuePtr;
  773.  
  774.     /*
  775.      * Create and initialize a new DLine structure.
  776.      */
  777.  
  778.     dlPtr = (DLine *) ckalloc(sizeof(DLine));
  779.     dlPtr->index = *indexPtr;
  780.     dlPtr->count = 0;
  781.     dlPtr->y = 0;
  782.     dlPtr->oldY = -1;
  783.     dlPtr->height = 0;
  784.     dlPtr->baseline = 0;
  785.     dlPtr->chunkPtr = NULL;
  786.     dlPtr->nextPtr = NULL;
  787.     dlPtr->flags = NEW_LAYOUT;
  788.  
  789.     /*
  790.      * Each iteration of the loop below creates one TkTextDispChunk for
  791.      * the new display line.  The line will always have at least one
  792.      * chunk (for the newline character at the end, if there's nothing
  793.      * else available).
  794.      */
  795.  
  796.     curIndex = *indexPtr;
  797.     lastChunkPtr = NULL;
  798.     chunkPtr = NULL;
  799.     noCharsYet = 1;
  800.     breakChunkPtr = NULL;
  801.     breakCharOffset = 0;
  802.     justify = TK_JUSTIFY_LEFT;
  803.     tabIndex = -1;
  804.     tabChunkPtr = NULL;
  805.     tabArrayPtr = NULL;
  806.     rMargin = 0;
  807.     wrapMode = tkTextCharUid;
  808.     tabSize = 0;
  809.     lastCharChunkPtr = NULL;
  810.  
  811.     /*
  812.      * Find the first segment to consider for the line.  Can't call
  813.      * TkTextIndexToSeg for this because it won't return a segment
  814.      * with zero size (such as the insertion cursor's mark).
  815.      */
  816.  
  817.     for (offset = curIndex.charIndex, segPtr = curIndex.linePtr->segPtr;
  818.         (offset > 0) && (offset >= segPtr->size);
  819.         offset -= segPtr->size, segPtr = segPtr->nextPtr) {
  820.     /* Empty loop body. */
  821.     }
  822.  
  823.     while (segPtr != NULL) {
  824.     if (segPtr->typePtr->layoutProc == NULL) {
  825.         segPtr = segPtr->nextPtr;
  826.         offset = 0;
  827.         continue;
  828.     }
  829.     if (chunkPtr == NULL) {
  830.         chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
  831.         chunkPtr->nextPtr = NULL;
  832.     }
  833.     chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
  834.  
  835.     /*
  836.      * Save style information such as justification and indentation,
  837.      * up until the first character is encountered, then retain that
  838.      * information for the rest of the line.
  839.      */
  840.  
  841.     if (noCharsYet) {
  842.         tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
  843.         justify = chunkPtr->stylePtr->sValuePtr->justify;
  844.         rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
  845.         wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
  846.         x = ((curIndex.charIndex == 0)
  847.             ? chunkPtr->stylePtr->sValuePtr->lMargin1
  848.             : chunkPtr->stylePtr->sValuePtr->lMargin2);
  849.         if (wrapMode == tkTextNoneUid) {
  850.         maxX = INT_MAX;
  851.         } else {
  852.         maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
  853.             - rMargin;
  854.         if (maxX < x) {
  855.             maxX = x;
  856.         }
  857.         }
  858.     }
  859.  
  860.     /*
  861.      * See if there is a tab in the current chunk; if so, only
  862.      * layout characters up to (and including) the tab.
  863.      */
  864.  
  865.     gotTab = 0;
  866.     maxChars = segPtr->size - offset;
  867.     if (justify == TK_JUSTIFY_LEFT) {
  868.         if (segPtr->typePtr == &tkTextCharType) {
  869.         char *p;
  870.  
  871.         for (p = segPtr->body.chars  + offset; *p != 0; p++) {
  872.             if (*p == '\t') {
  873.             maxChars = (p + 1 - segPtr->body.chars) - offset;
  874.             gotTab = 1;
  875.             break;
  876.             }
  877.         }
  878.         }
  879.     }
  880.  
  881.     chunkPtr->x = x;
  882.     code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
  883.         offset, maxX-tabSize, maxChars, noCharsYet, wrapMode,
  884.         chunkPtr);
  885.     if (code <= 0) {
  886.         FreeStyle(textPtr, chunkPtr->stylePtr);
  887.         if (code < 0) {
  888.         /*
  889.          * This segment doesn't wish to display itself (e.g. most
  890.          * marks).
  891.          */
  892.  
  893.         segPtr = segPtr->nextPtr;
  894.         offset = 0;
  895.         continue;
  896.         }
  897.  
  898.         /*
  899.          * No characters from this segment fit in the window: this
  900.          * means we're at the end of the display line.
  901.          */
  902.  
  903.         if (chunkPtr != NULL) {
  904.         ckfree((char *) chunkPtr);
  905.         }
  906.         break;
  907.     }
  908.     if (chunkPtr->numChars > 0) {
  909.         noCharsYet = 0;
  910.         lastCharChunkPtr = chunkPtr;
  911.     }
  912.     if (lastChunkPtr == NULL) {
  913.         dlPtr->chunkPtr = chunkPtr;
  914.     } else {
  915.         lastChunkPtr->nextPtr = chunkPtr;
  916.     }
  917.     lastChunkPtr = chunkPtr;
  918.     x += chunkPtr->width;
  919.     if (chunkPtr->breakIndex > 0) {
  920.         breakCharOffset = chunkPtr->breakIndex;
  921.         breakIndex = curIndex;
  922.         breakChunkPtr = chunkPtr;
  923.     }
  924.     if (chunkPtr->numChars != maxChars) {
  925.         break;
  926.     }
  927.  
  928.     /*
  929.      * If we're at a new tab, adjust the layout for all the chunks
  930.      * pertaining to the previous tab.  Also adjust the amount of
  931.      * space left in the line to account for space that will be eaten
  932.      * up by the tab.
  933.      */
  934.  
  935.     if (gotTab) {
  936.         if (tabIndex >= 0) {
  937.         AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  938.         x = chunkPtr->x + chunkPtr->width;
  939.         }
  940.         tabIndex++;
  941.         tabChunkPtr = chunkPtr;
  942.         tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
  943.         if (tabSize >= (maxX - x)) {
  944.         break;
  945.         }
  946.     }
  947.     curIndex.charIndex += chunkPtr->numChars;
  948.     offset += chunkPtr->numChars;
  949.     if (offset >= segPtr->size) {
  950.         offset = 0;
  951.         segPtr = segPtr->nextPtr;
  952.     }
  953.     chunkPtr = NULL;
  954.     }
  955.     if (noCharsYet) {
  956.     panic("LayoutDLine couldn't place any characters on a line");
  957.     }
  958.     wholeLine = (segPtr == NULL);
  959.  
  960.     /*
  961.      * We're at the end of the display line.  Throw away everything
  962.      * after the most recent word break, if there is one;  this may
  963.      * potentially require the last chunk to be layed out again.
  964.      */
  965.  
  966.     if (breakChunkPtr == NULL) {
  967.     /*
  968.      * This code makes sure that we don't accidentally display
  969.      * chunks with no characters at the end of the line (such as
  970.      * the insertion cursor).  These chunks belong on the next
  971.      * line.  So, throw away everything after the last chunk that
  972.      * has characters in it.
  973.      */
  974.  
  975.     breakChunkPtr = lastCharChunkPtr;
  976.     breakCharOffset = breakChunkPtr->numChars;
  977.     }
  978.     if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
  979.         || (breakCharOffset != lastChunkPtr->numChars))) {
  980.     while (1) {
  981.         chunkPtr = breakChunkPtr->nextPtr;
  982.         if (chunkPtr == NULL) {
  983.         break;
  984.         }
  985.         FreeStyle(textPtr, chunkPtr->stylePtr);
  986.         breakChunkPtr->nextPtr = chunkPtr->nextPtr;
  987.         (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  988.         ckfree((char *) chunkPtr);
  989.     }
  990.     if (breakCharOffset != breakChunkPtr->numChars) {
  991.         (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
  992.         segPtr = TkTextIndexToSeg(&breakIndex, &offset);
  993.         (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
  994.             segPtr, offset, maxX, breakCharOffset, 0, 
  995.             wrapMode, breakChunkPtr);
  996.     }
  997.     lastChunkPtr = breakChunkPtr;
  998.     wholeLine = 0;
  999.     }
  1000.  
  1001.     /*
  1002.      * Make tab adjustments for the last tab stop, if there is one.
  1003.      */
  1004.  
  1005.     if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
  1006.     AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  1007.     }
  1008.  
  1009.     /*
  1010.      * Make one more pass over the line to recompute various things
  1011.      * like its height, length, and total number of characters.  Also
  1012.      * modify the x-locations of chunks to reflect justification.
  1013.      * If we're not wrapping, I'm not sure what is the best way to
  1014.      * handle left and center justification:  should the total length,
  1015.      * for purposes of justification, be (a) the window width, (b)
  1016.      * the length of the longest line in the window, or (c) the length
  1017.      * of the longest line in the text?  (c) isn't available, (b) seems
  1018.      * weird, since it can change with vertical scrolling, so (a) is
  1019.      * what is implemented below.
  1020.      */
  1021.  
  1022.     if (wrapMode == tkTextNoneUid) {
  1023.     maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
  1024.     }
  1025.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  1026.     if (justify == TK_JUSTIFY_LEFT) {
  1027.     jIndent = 0;
  1028.     } else if (justify == TK_JUSTIFY_RIGHT) {
  1029.     jIndent = maxX - dlPtr->length;
  1030.     } else {
  1031.     jIndent = (maxX - dlPtr->length)/2;
  1032.     }
  1033.     ascent = descent = 0;
  1034.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1035.         chunkPtr = chunkPtr->nextPtr) {
  1036.     chunkPtr->x += jIndent;
  1037.     dlPtr->count += chunkPtr->numChars;
  1038.     if (chunkPtr->minAscent > ascent) {
  1039.         ascent = chunkPtr->minAscent;
  1040.     }
  1041.     if (chunkPtr->minDescent > descent) {
  1042.         descent = chunkPtr->minDescent;
  1043.     }
  1044.     if (chunkPtr->minHeight > dlPtr->height) {
  1045.         dlPtr->height = chunkPtr->minHeight;
  1046.     }
  1047.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1048.     if ((sValuePtr->borderWidth > 0)
  1049.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1050.         dlPtr->flags |= HAS_3D_BORDER;
  1051.     }
  1052.     }
  1053.     if (dlPtr->height < (ascent + descent)) {
  1054.     dlPtr->height = ascent + descent;
  1055.     dlPtr->baseline = ascent;
  1056.     } else {
  1057.     dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
  1058.     }
  1059.     sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
  1060.     if (dlPtr->index.charIndex == 0) {
  1061.     dlPtr->spaceAbove = sValuePtr->spacing1;
  1062.     } else {
  1063.     dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
  1064.     }
  1065.     if (wholeLine) {
  1066.     dlPtr->spaceBelow = sValuePtr->spacing3;
  1067.     } else {
  1068.     dlPtr->spaceBelow = sValuePtr->spacing2/2;
  1069.     }
  1070.     dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
  1071.     dlPtr->baseline += dlPtr->spaceAbove;
  1072.  
  1073.     /*
  1074.      * Recompute line length:  may have changed because of justification.
  1075.      */
  1076.  
  1077.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  1078.     return dlPtr;
  1079. }
  1080.  
  1081. /*
  1082.  *----------------------------------------------------------------------
  1083.  *
  1084.  * UpdateDisplayInfo --
  1085.  *
  1086.  *    This procedure is invoked to recompute some or all of the
  1087.  *    DLine structures for a text widget.  At the time it is called
  1088.  *    the DLine structures still left in the widget are guaranteed
  1089.  *    to be correct except that (a) the y-coordinates aren't
  1090.  *    necessarily correct, (b) there may be missing structures
  1091.  *    (the DLine structures get removed as soon as they are potentially
  1092.  *    out-of-date), and (c) DLine structures that don't start at the
  1093.  *    beginning of a line may be incorrect if previous information in
  1094.  *    the same line changed size in a way that moved a line boundary
  1095.  *    (DLines for any info that changed will have been deleted, but
  1096.  *    not DLines for unchanged info in the same text line).
  1097.  *
  1098.  * Results:
  1099.  *    None.
  1100.  *
  1101.  * Side effects:
  1102.  *    Upon return, the DLine information for textPtr correctly reflects
  1103.  *    the positions where characters will be displayed.  However, this
  1104.  *    procedure doesn't actually bring the display up-to-date.
  1105.  *
  1106.  *----------------------------------------------------------------------
  1107.  */
  1108.  
  1109. static void
  1110. UpdateDisplayInfo(textPtr)
  1111.     TkText *textPtr;            /* Text widget to update. */
  1112. {
  1113.     register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1114.     register DLine *dlPtr, *prevPtr;
  1115.     TkTextIndex index;
  1116.     TkTextLine *lastLinePtr;
  1117.     int y, maxY, pixelOffset, maxOffset;
  1118.  
  1119.     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
  1120.     return;
  1121.     }
  1122.     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
  1123.  
  1124.     /*
  1125.      * Delete any DLines that are now above the top of the window.
  1126.      */
  1127.  
  1128.     index = textPtr->topIndex;
  1129.     dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
  1130.     if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
  1131.     FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
  1132.     }
  1133.  
  1134.     /*
  1135.      *--------------------------------------------------------------
  1136.      * Scan through the contents of the window from top to bottom,
  1137.      * recomputing information for lines that are missing.
  1138.      *--------------------------------------------------------------
  1139.      */
  1140.  
  1141.     lastLinePtr = TkBTreeFindLine(textPtr->tree,
  1142.         TkBTreeNumLines(textPtr->tree));
  1143.     dlPtr = dInfoPtr->dLinePtr;
  1144.     prevPtr = NULL;
  1145.     y = dInfoPtr->y;
  1146.     maxY = dInfoPtr->maxY;
  1147.     while (1) {
  1148.     register DLine *newPtr;
  1149.  
  1150.     if (index.linePtr == lastLinePtr) {
  1151.         break;
  1152.     }
  1153.  
  1154.     /*
  1155.      * There are three possibilities right now:
  1156.      * (a) the next DLine (dlPtr) corresponds exactly to the next
  1157.      *     information we want to display: just use it as-is.
  1158.      * (b) the next DLine corresponds to a different line, or to
  1159.      *     a segment that will be coming later in the same line:
  1160.      *     leave this DLine alone in the hopes that we'll be able
  1161.      *     to use it later, then create a new DLine in front of
  1162.      *     it.
  1163.      * (c) the next DLine corresponds to a segment in the line we
  1164.      *     want, but it's a segment that has already been processed
  1165.      *     or will never be processed.  Delete the DLine and try
  1166.      *     again.
  1167.      *
  1168.      * One other twist on all this.  It's possible for 3D borders
  1169.      * to interact between lines (see DisplayLineBackground) so if
  1170.      * a line is relayed out and has styles with 3D borders, its
  1171.      * neighbors have to be redrawn if they have 3D borders too,
  1172.      * since the interactions could have changed (the neighbors
  1173.      * don't have to be relayed out, just redrawn).
  1174.      */
  1175.  
  1176.     if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
  1177.         /*
  1178.          * Case (b) -- must make new DLine.
  1179.          */
  1180.  
  1181.         makeNewDLine:
  1182.         if (tkTextDebug) {
  1183.         char string[TK_POS_CHARS];
  1184.  
  1185.         /*
  1186.          * Debugging is enabled, so keep a log of all the lines
  1187.          * that were re-layed out.  The test suite uses this
  1188.          * information.
  1189.          */
  1190.  
  1191.         TkTextPrintIndex(&index, string);
  1192.         Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
  1193.             string,
  1194.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1195.         }
  1196.         newPtr = LayoutDLine(textPtr, &index);
  1197.         if (prevPtr == NULL) {
  1198.         dInfoPtr->dLinePtr = newPtr;
  1199.         } else {
  1200.         prevPtr->nextPtr = newPtr;
  1201.         if (prevPtr->flags & HAS_3D_BORDER) {
  1202.             prevPtr->oldY = -1;
  1203.         }
  1204.         }
  1205.         newPtr->nextPtr = dlPtr;
  1206.         dlPtr = newPtr;
  1207.     } else {
  1208.         /*
  1209.          * DlPtr refers to the line we want.  Next check the
  1210.          * index within the line.
  1211.          */
  1212.  
  1213.         if (index.charIndex == dlPtr->index.charIndex) {
  1214.         /*
  1215.          * Case (a) -- can use existing display line as-is.
  1216.          */
  1217.  
  1218.         if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
  1219.             && (prevPtr->flags & (NEW_LAYOUT))) {
  1220.             dlPtr->oldY = -1;
  1221.         }
  1222.         goto lineOK;
  1223.         }
  1224.         if (index.charIndex < dlPtr->index.charIndex) {
  1225.         goto makeNewDLine;
  1226.         }
  1227.  
  1228.         /*
  1229.          * Case (c) -- dlPtr is useless.  Discard it and start
  1230.          * again with the next display line.
  1231.          */
  1232.  
  1233.         newPtr = dlPtr->nextPtr;
  1234.         FreeDLines(textPtr, dlPtr, newPtr, 0);
  1235.         dlPtr = newPtr;
  1236.         if (prevPtr != NULL) {
  1237.         prevPtr->nextPtr = newPtr;
  1238.         } else {
  1239.         dInfoPtr->dLinePtr = newPtr;
  1240.         }
  1241.         continue;
  1242.     }
  1243.  
  1244.     /*
  1245.      * Advance to the start of the next line.
  1246.      */
  1247.  
  1248.     lineOK:
  1249.     dlPtr->y = y;
  1250.     y += dlPtr->height;
  1251.     TkTextIndexForwChars(&index, dlPtr->count, &index);
  1252.     prevPtr = dlPtr;
  1253.     dlPtr = dlPtr->nextPtr;
  1254.  
  1255.     /*
  1256.      * If we switched text lines, delete any DLines left for the
  1257.      * old text line.
  1258.      */
  1259.  
  1260.     if (index.linePtr != prevPtr->index.linePtr) {
  1261.         register DLine *nextPtr;
  1262.  
  1263.         nextPtr = dlPtr;
  1264.         while ((nextPtr != NULL)
  1265.             && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
  1266.         nextPtr = nextPtr->nextPtr;
  1267.         }
  1268.         if (nextPtr != dlPtr) {
  1269.         FreeDLines(textPtr, dlPtr, nextPtr, 0);
  1270.         prevPtr->nextPtr = nextPtr;
  1271.         dlPtr = nextPtr;
  1272.         }
  1273.     }
  1274.  
  1275.     /*
  1276.      * It's important to have the following check here rather than in
  1277.      * the while statement for the loop, so that there's always at least
  1278.      * one DLine generated, regardless of how small the window is.  This
  1279.      * keeps a lot of other code from breaking.
  1280.      */
  1281.  
  1282.     if (y >= maxY) {
  1283.         break;
  1284.     }
  1285.     }
  1286.  
  1287.     /*
  1288.      * Delete any DLine structures that don't fit on the screen.
  1289.      */
  1290.  
  1291.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
  1292.  
  1293.     /*
  1294.      *--------------------------------------------------------------
  1295.      * If there is extra space at the bottom of the window (because
  1296.      * we've hit the end of the text), then bring in more lines at
  1297.      * the top of the window, if there are any, to fill in the view.
  1298.      *--------------------------------------------------------------
  1299.      */
  1300.  
  1301.     if (y < maxY) {
  1302.     int lineNum, spaceLeft, charsToCount;
  1303.     DLine *lowestPtr;
  1304.  
  1305.     /*
  1306.      * Layout an entire text line (potentially > 1 display line),
  1307.      * then link in as many display lines as fit without moving
  1308.      * the bottom line out of the window.  Repeat this until
  1309.      * all the extra space has been used up or we've reached the
  1310.      * beginning of the text.
  1311.      */
  1312.  
  1313.     spaceLeft = maxY - y;
  1314.     lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
  1315.     charsToCount = dInfoPtr->dLinePtr->index.charIndex;
  1316.     if (charsToCount == 0) {
  1317.         charsToCount = INT_MAX;
  1318.         lineNum--;
  1319.     }
  1320.     for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
  1321.         index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
  1322.         index.charIndex = 0;
  1323.         lowestPtr = NULL;
  1324.         do {
  1325.         dlPtr = LayoutDLine(textPtr, &index);
  1326.         dlPtr->nextPtr = lowestPtr;
  1327.         lowestPtr = dlPtr;
  1328.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  1329.         charsToCount -= dlPtr->count;
  1330.         } while ((charsToCount > 0)
  1331.             && (index.linePtr == lowestPtr->index.linePtr));
  1332.  
  1333.         /*
  1334.          * Scan through the display lines from the bottom one up to
  1335.          * the top one.
  1336.          */
  1337.  
  1338.         while (lowestPtr != NULL) {
  1339.         dlPtr = lowestPtr;
  1340.         spaceLeft -= dlPtr->height;
  1341.         if (spaceLeft < 0) {
  1342.             break;
  1343.         }
  1344.         lowestPtr = dlPtr->nextPtr;
  1345.         dlPtr->nextPtr = dInfoPtr->dLinePtr;
  1346.         dInfoPtr->dLinePtr = dlPtr;
  1347.         if (tkTextDebug) {
  1348.             char string[TK_POS_CHARS];
  1349.  
  1350.             TkTextPrintIndex(&dlPtr->index, string);
  1351.             Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
  1352.                 (char *) NULL, string,
  1353.                 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1354.         }
  1355.         }
  1356.         FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  1357.         charsToCount = INT_MAX;
  1358.     }
  1359.  
  1360.     /*
  1361.      * Now we're all done except that the y-coordinates in all the
  1362.      * DLines are wrong and the top index for the text is wrong.
  1363.      * Update them.
  1364.      */
  1365.  
  1366.     textPtr->topIndex = dInfoPtr->dLinePtr->index;
  1367.     y = dInfoPtr->y;
  1368.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1369.         dlPtr = dlPtr->nextPtr) {
  1370.         if (y > dInfoPtr->maxY) {
  1371.         panic("Added too many new lines in UpdateDisplayInfo");
  1372.         }
  1373.         dlPtr->y = y;
  1374.         y += dlPtr->height; 
  1375.     }
  1376.     }
  1377.  
  1378.     /*
  1379.      *--------------------------------------------------------------
  1380.      * If the old top or bottom line has scrolled elsewhere on the
  1381.      * screen, we may not be able to re-use its old contents by
  1382.      * copying bits (e.g., a beveled edge that was drawn when it was
  1383.      * at the top or bottom won't be drawn when the line is in the
  1384.      * middle and its neighbor has a matching background).  Similarly,
  1385.      * if the new top or bottom line came from somewhere else on the
  1386.      * screen, we may not be able to copy the old bits.
  1387.      *--------------------------------------------------------------
  1388.      */
  1389.  
  1390.     dlPtr = dInfoPtr->dLinePtr;
  1391.     if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
  1392.     dlPtr->oldY = -1;
  1393.     }
  1394.     while (1) {
  1395.     if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
  1396.         && (dlPtr->flags & HAS_3D_BORDER)) {
  1397.         dlPtr->oldY = -1;
  1398.     }
  1399.     if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
  1400.         && (dlPtr->flags & HAS_3D_BORDER)) {
  1401.         dlPtr->oldY = -1;
  1402.     }
  1403.     if (dlPtr->nextPtr == NULL) {
  1404.         if ((dlPtr->flags & HAS_3D_BORDER)
  1405.             && !(dlPtr->flags & BOTTOM_LINE)) {
  1406.         dlPtr->oldY = -1;
  1407.         }
  1408.         dlPtr->flags &= ~TOP_LINE;
  1409.         dlPtr->flags |= BOTTOM_LINE;
  1410.         break;
  1411.     }
  1412.     dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
  1413.     dlPtr = dlPtr->nextPtr;
  1414.     }
  1415.     dInfoPtr->dLinePtr->flags |= TOP_LINE;
  1416.  
  1417.     /*
  1418.      * Arrange for scrollbars to be updated.
  1419.      */
  1420.  
  1421.     textPtr->flags |= UPDATE_SCROLLBARS;
  1422.  
  1423.     /*
  1424.      *--------------------------------------------------------------
  1425.      * Deal with horizontal scrolling:
  1426.      * 1. If there's empty space to the right of the longest line,
  1427.      *    shift the screen to the right to fill in the empty space.
  1428.      * 2. If the desired horizontal scroll position has changed,
  1429.      *    force a full redisplay of all the lines in the widget.
  1430.      * 3. If the wrap mode isn't "none" then re-scroll to the base
  1431.      *    position.
  1432.      *--------------------------------------------------------------
  1433.      */
  1434.  
  1435.     dInfoPtr->maxLength = 0;
  1436.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1437.         dlPtr = dlPtr->nextPtr) {
  1438.     if (dlPtr->length > dInfoPtr->maxLength) {
  1439.         dInfoPtr->maxLength = dlPtr->length;
  1440.     }
  1441.     }
  1442.     maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
  1443.         + textPtr->charWidth - 1)/textPtr->charWidth;
  1444.     if (dInfoPtr->newCharOffset > maxOffset) {
  1445.     dInfoPtr->newCharOffset = maxOffset;
  1446.     }
  1447.     if (dInfoPtr->newCharOffset < 0) {
  1448.     dInfoPtr->newCharOffset = 0;
  1449.     }
  1450.     pixelOffset = dInfoPtr->newCharOffset * textPtr->charWidth;
  1451.     if (pixelOffset != dInfoPtr->curPixelOffset) {
  1452.     dInfoPtr->curPixelOffset = pixelOffset;
  1453.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1454.         dlPtr = dlPtr->nextPtr) {
  1455.         dlPtr->oldY = -1;
  1456.     }
  1457.     }
  1458. }
  1459.  
  1460. /*
  1461.  *----------------------------------------------------------------------
  1462.  *
  1463.  * FreeDLines --
  1464.  *
  1465.  *    This procedure is called to free up all of the resources
  1466.  *    associated with one or more DLine structures.
  1467.  *
  1468.  * Results:
  1469.  *    None.
  1470.  *
  1471.  * Side effects:
  1472.  *    Memory gets freed and various other resources are released.
  1473.  *
  1474.  *----------------------------------------------------------------------
  1475.  */
  1476.  
  1477. static void
  1478. FreeDLines(textPtr, firstPtr, lastPtr, unlink)
  1479.     TkText *textPtr;            /* Information about overall text
  1480.                      * widget. */
  1481.     register DLine *firstPtr;        /* Pointer to first DLine to free up. */
  1482.     DLine *lastPtr;            /* Pointer to DLine just after last
  1483.                      * one to free (NULL means everything
  1484.                      * starting with firstPtr). */
  1485.     int unlink;                /* 1 means DLines are currently linked
  1486.                      * into the list rooted at
  1487.                      * textPtr->dInfoPtr->dLinePtr and
  1488.                      * they have to be unlinked.  0 means
  1489.                      * just free without unlinking. */
  1490. {
  1491.     register TkTextDispChunk *chunkPtr, *nextChunkPtr;
  1492.     register DLine *nextDLinePtr;
  1493.  
  1494.     if (unlink) {
  1495.     if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
  1496.         textPtr->dInfoPtr->dLinePtr = lastPtr;
  1497.     } else {
  1498.         register DLine *prevPtr;
  1499.         for (prevPtr = textPtr->dInfoPtr->dLinePtr;
  1500.             prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
  1501.         /* Empty loop body. */
  1502.         }
  1503.         prevPtr->nextPtr = lastPtr;
  1504.     }
  1505.     }
  1506.     while (firstPtr != lastPtr) {
  1507.     nextDLinePtr = firstPtr->nextPtr;
  1508.     for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
  1509.         chunkPtr = nextChunkPtr) {
  1510.         if (chunkPtr->undisplayProc != NULL) {
  1511.         (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  1512.         }
  1513.         FreeStyle(textPtr, chunkPtr->stylePtr);
  1514.         nextChunkPtr = chunkPtr->nextPtr;
  1515.         ckfree((char *) chunkPtr);
  1516.     }
  1517.     ckfree((char *) firstPtr);
  1518.     firstPtr = nextDLinePtr;
  1519.     }
  1520.     textPtr->dInfoPtr->dLinesInvalidated = 1;
  1521. }
  1522.  
  1523. /*
  1524.  *----------------------------------------------------------------------
  1525.  *
  1526.  * DisplayDLine --
  1527.  *
  1528.  *    This procedure is invoked to draw a single line on the
  1529.  *    screen.
  1530.  *
  1531.  * Results:
  1532.  *    None.
  1533.  *
  1534.  * Side effects:
  1535.  *    The line given by dlPtr is drawn at its correct position in
  1536.  *    textPtr's window.  Note that this is one *display* line, not
  1537.  *    one *text* line.
  1538.  *
  1539.  *----------------------------------------------------------------------
  1540.  */
  1541.  
  1542. static void
  1543. DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
  1544.     TkText *textPtr;        /* Text widget in which to draw line. */
  1545.     register DLine *dlPtr;    /* Information about line to draw. */
  1546.     DLine *prevPtr;        /* Line just before one to draw, or NULL
  1547.                  * if dlPtr is the top line. */
  1548.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1549.                  * Caller must make sure it's large enough
  1550.                  * to hold line. */
  1551. {
  1552.     register TkTextDispChunk *chunkPtr;
  1553.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1554.     Display *display;
  1555.     int height, x;
  1556.  
  1557.     /*
  1558.      * First, clear the area of the line to the background color for the
  1559.      * text widget.
  1560.      */
  1561.  
  1562.     display = Tk_Display(textPtr->tkwin);
  1563.     Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, 0,
  1564.         Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
  1565.  
  1566.     /*
  1567.      * Next, draw background information for the whole line.
  1568.      */
  1569.  
  1570.     DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
  1571.  
  1572.     /*
  1573.      * Make another pass through all of the chunks to redraw the
  1574.      * insertion cursor, if it is visible on this line.  Must do
  1575.      * it here rather than in the foreground pass below because
  1576.      * otherwise a wide insertion cursor will obscure the character
  1577.      * to its left.
  1578.      */
  1579.  
  1580.     if (textPtr->state == tkNormalUid) {
  1581.     for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1582.         chunkPtr = chunkPtr->nextPtr) {
  1583.         x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1584.         if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1585.         (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
  1586.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1587.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1588.             dlPtr->y + dlPtr->spaceAbove);
  1589.         }
  1590.     }
  1591.     }
  1592.  
  1593.     /*
  1594.      * Make yet another pass through all of the chunks to redraw all of
  1595.      * foreground information.  Note:  we have to call the displayProc
  1596.      * even for chunks that are off-screen.  This is needed, for
  1597.      * example, so that embedded windows can be unmapped in this case.
  1598.      * Conve
  1599.      */
  1600.  
  1601.     for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1602.         chunkPtr = chunkPtr->nextPtr) {
  1603.     if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1604.         /*
  1605.          * Already displayed the insertion cursor above.  Don't
  1606.          * do it again here.
  1607.          */
  1608.  
  1609.         continue;
  1610.     }
  1611.     x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1612.     if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
  1613.         /*
  1614.          * Note:  we have to call the displayProc even for chunks
  1615.          * that are off-screen.  This is needed, for example, so
  1616.          * that embedded windows can be unmapped in this case.
  1617.          * Display the chunk at a coordinate that can be clearly
  1618.          * identified by the displayProc as being off-screen to
  1619.          * the left (the displayProc may not be able to tell if
  1620.          * something is off to the right).
  1621.          */
  1622.  
  1623.         (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
  1624.             dlPtr->spaceAbove,
  1625.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1626.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1627.             dlPtr->y + dlPtr->spaceAbove);
  1628.     } else {
  1629.         (*chunkPtr->displayProc)(chunkPtr, x, dlPtr->spaceAbove,
  1630.             dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1631.             dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1632.             dlPtr->y + dlPtr->spaceAbove);
  1633.     }
  1634.     if (dInfoPtr->dLinesInvalidated) {
  1635.         return;
  1636.     }
  1637.     }
  1638.  
  1639.     /*
  1640.      * Copy the pixmap onto the screen.  If this is the last line on
  1641.      * the screen then copy a piece of the line, so that it doesn't
  1642.      * overflow into the border area.  Another special trick:  copy the
  1643.      * padding area to the left of the line;  this is because the
  1644.      * insertion cursor sometimes overflows onto that area and we want
  1645.      * to get as much of the cursor as possible.
  1646.      */
  1647.  
  1648.     height = dlPtr->height;
  1649.     if ((height + dlPtr->y) > dInfoPtr->maxY) {
  1650.     height = dInfoPtr->maxY - dlPtr->y;
  1651.     }
  1652.     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
  1653.         dInfoPtr->x, 0, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
  1654.         (unsigned) height, dInfoPtr->x, dlPtr->y);
  1655.     linesRedrawn++;
  1656. }
  1657.  
  1658. /*
  1659.  *--------------------------------------------------------------
  1660.  *
  1661.  * DisplayLineBackground --
  1662.  *
  1663.  *    This procedure is called to fill in the background for
  1664.  *    a display line.  It draws 3D borders cleverly so that
  1665.  *    adjacent chunks with the same style (whether on the same
  1666.  *    line or different lines) have a single 3D border around
  1667.  *    the whole region.
  1668.  *
  1669.  * Results:
  1670.  *    There is no return value.  Pixmap is filled in with background
  1671.  *    information for dlPtr.
  1672.  *
  1673.  * Side effects:
  1674.  *    None.
  1675.  *
  1676.  *--------------------------------------------------------------
  1677.  */
  1678.  
  1679. static void
  1680. DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
  1681.     TkText *textPtr;        /* Text widget containing line. */
  1682.     register DLine *dlPtr;    /* Information about line to draw. */
  1683.     DLine *prevPtr;        /* Line just above dlPtr, or NULL if dlPtr
  1684.                  * is the top-most line in the window. */
  1685.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1686.                  * Caller must make sure it's large enough
  1687.                  * to hold line.  Caller must also have
  1688.                  * filled it with the background color for
  1689.                  * the widget. */
  1690. {
  1691.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1692.     TkTextDispChunk *chunkPtr;  /* Pointer to chunk in the current line. */
  1693.     TkTextDispChunk *chunkPtr2;    /* Pointer to chunk in the line above or
  1694.                  * below the current one.  NULL if we're to
  1695.                  * the left of or to the right of the chunks
  1696.                  * in the line. */
  1697.     TkTextDispChunk *nextPtr2;    /* Next chunk after chunkPtr2 (it's not the
  1698.                  * same as chunkPtr2->nextPtr in the case
  1699.                  * where chunkPtr2 is NULL because the line
  1700.                  * is indented). */
  1701.     int leftX;            /* The left edge of the region we're
  1702.                  * currently working on. */
  1703.     int leftXIn;        /* 1 means beveled edge at leftX slopes right
  1704.                  * as it goes down, 0 means it slopes left
  1705.                  * as it goes down. */
  1706.     int rightX;            /* Right edge of chunkPtr. */
  1707.     int rightX2;        /* Right edge of chunkPtr2. */
  1708.     int matchLeft;        /* Does the style of this line match that
  1709.                  * of its neighbor just to the left of
  1710.                  * the current x coordinate? */
  1711.     int matchRight;        /* Does line's style match its neighbor
  1712.                  * just to the right of the current x-coord? */
  1713.     int minX, maxX, xOffset;
  1714.     StyleValues *sValuePtr;
  1715.     Display *display;
  1716.  
  1717.     /*
  1718.      * Pass 1: scan through dlPtr from left to right.  For each range of
  1719.      * chunks with the same style, draw the main background for the style
  1720.      * plus the vertical parts of the 3D borders (the left and right
  1721.      * edges).
  1722.      */
  1723.  
  1724.     display = Tk_Display(textPtr->tkwin);
  1725.     minX = dInfoPtr->curPixelOffset;
  1726.     xOffset = dInfoPtr->x - minX;
  1727.     maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
  1728.     chunkPtr = dlPtr->chunkPtr;
  1729.  
  1730.     /*
  1731.      * Note A: in the following statement, and a few others later in
  1732.      * this file marked with "See Note A above", the right side of the
  1733.      * assignment was replaced with 0 on 6/18/97.  This has the effect
  1734.      * of highlighting the empty space to the left of a line whenever
  1735.      * the leftmost character of the line is highlighted.  This way,
  1736.      * multi-line highlights always line up along their left edges. 
  1737.      * However, this may look funny in the case where a single word is
  1738.      * highlighted. To undo the change, replace "leftX = 0" with "leftX
  1739.      * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
  1740.      * here and at all the marked points below.  This restores the old
  1741.      * behavior where empty space to the left of a line is not
  1742.      * highlighted, leaving a ragged left edge for multi-line
  1743.      * highlights.
  1744.      */
  1745.  
  1746.     leftX = 0;
  1747.     for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
  1748.     if ((chunkPtr->nextPtr != NULL)
  1749.         && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
  1750.         chunkPtr->stylePtr)) {
  1751.         continue;
  1752.     }
  1753.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1754.     rightX = chunkPtr->x + chunkPtr->width;
  1755.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1756.         rightX = maxX;
  1757.     }
  1758.     if (chunkPtr->stylePtr->bgGC != None) {
  1759.         XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
  1760.             leftX + xOffset, 0, (unsigned int) (rightX - leftX),
  1761.             (unsigned int) dlPtr->height);
  1762.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1763.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1764.             leftX + xOffset, 0, sValuePtr->borderWidth,
  1765.             dlPtr->height, 1, sValuePtr->relief);
  1766.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1767.             rightX - sValuePtr->borderWidth + xOffset,
  1768.             0, sValuePtr->borderWidth, dlPtr->height, 0,
  1769.             sValuePtr->relief);
  1770.         }
  1771.     }
  1772.     leftX = rightX;
  1773.     }
  1774.  
  1775.     /*
  1776.      * Pass 2: draw the horizontal bevels along the top of the line.  To
  1777.      * do this, scan through dlPtr from left to right while simultaneously
  1778.      * scanning through the line just above dlPtr.  ChunkPtr2 and nextPtr2
  1779.      * refer to two adjacent chunks in the line above.
  1780.      */
  1781.  
  1782.     chunkPtr = dlPtr->chunkPtr;
  1783.     leftX = 0;                /* See Note A above. */
  1784.     leftXIn = 1;
  1785.     rightX = chunkPtr->x + chunkPtr->width;
  1786.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1787.     rightX = maxX;
  1788.     }
  1789.     chunkPtr2 = NULL;
  1790.     if (prevPtr != NULL) {
  1791.     /*
  1792.      * Find the chunk in the previous line that covers leftX.
  1793.      */
  1794.  
  1795.     nextPtr2 = prevPtr->chunkPtr;
  1796.     rightX2 = 0;            /* See Note A above. */
  1797.     while (rightX2 <= leftX) {
  1798.         chunkPtr2 = nextPtr2;
  1799.         if (chunkPtr2 == NULL) {
  1800.         break;
  1801.         }
  1802.         nextPtr2 = chunkPtr2->nextPtr;
  1803.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1804.         if (nextPtr2 == NULL) {
  1805.         rightX2 = INT_MAX;
  1806.         }
  1807.     }
  1808.     } else {
  1809.     nextPtr2 = NULL;
  1810.     rightX2 = INT_MAX;
  1811.     }
  1812.  
  1813.     while (leftX < maxX) {
  1814.     matchLeft = (chunkPtr2 != NULL)
  1815.         && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1816.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1817.     if (rightX <= rightX2) {
  1818.         /*
  1819.          * The chunk in our line is about to end.  If its style
  1820.          * changes then draw the bevel for the current style.
  1821.          */
  1822.  
  1823.         if ((chunkPtr->nextPtr == NULL)
  1824.             || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1825.             chunkPtr->nextPtr->stylePtr)) {
  1826.         if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1827.             Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1828.                 sValuePtr->border, leftX + xOffset, 0,
  1829.                 rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1830.                 1, 1, sValuePtr->relief);
  1831.         }
  1832.         leftX = rightX;
  1833.         leftXIn = 1;
  1834.  
  1835.         /*
  1836.          * If the chunk in the line above is also ending at
  1837.          * the same point then advance to the next chunk in
  1838.          * that line.
  1839.          */
  1840.  
  1841.         if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1842.             goto nextChunk2;
  1843.         }
  1844.         }
  1845.         chunkPtr = chunkPtr->nextPtr;
  1846.         if (chunkPtr == NULL) {
  1847.         break;
  1848.         }
  1849.         rightX = chunkPtr->x + chunkPtr->width;
  1850.         if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1851.         rightX = maxX;
  1852.         }
  1853.         continue;
  1854.     }
  1855.  
  1856.     /*
  1857.      * The chunk in the line above is ending at an x-position where
  1858.      * there is no change in the style of the current line.  If the
  1859.      * style above matches the current line on one side of the change
  1860.      * but not on the other, we have to draw an L-shaped piece of
  1861.      * bevel.
  1862.      */
  1863.  
  1864.     matchRight = (nextPtr2 != NULL)
  1865.         && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1866.     if (matchLeft && !matchRight) {
  1867.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1868.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1869.             rightX2 - sValuePtr->borderWidth + xOffset, 0,
  1870.             sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1871.             sValuePtr->relief);
  1872.         }
  1873.         leftX = rightX2 - sValuePtr->borderWidth;
  1874.         leftXIn = 0;
  1875.     } else if (!matchLeft && matchRight
  1876.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1877.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1878.             rightX2 + xOffset, 0, sValuePtr->borderWidth,
  1879.             sValuePtr->borderWidth, 1, sValuePtr->relief);
  1880.         Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1881.             leftX + xOffset, 0, rightX2 + sValuePtr->borderWidth -leftX,
  1882.             sValuePtr->borderWidth, leftXIn, 0, 1,
  1883.             sValuePtr->relief);
  1884.     }
  1885.  
  1886.     nextChunk2:
  1887.     chunkPtr2 = nextPtr2;
  1888.     if (chunkPtr2 == NULL) {
  1889.         rightX2 = INT_MAX;
  1890.     } else {
  1891.         nextPtr2 = chunkPtr2->nextPtr;
  1892.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1893.         if (nextPtr2 == NULL) {
  1894.         rightX2 = INT_MAX;
  1895.         }
  1896.     }
  1897.     }
  1898.     /*
  1899.      * Pass 3: draw the horizontal bevels along the bottom of the line.
  1900.      * This uses the same approach as pass 2.
  1901.      */
  1902.  
  1903.     chunkPtr = dlPtr->chunkPtr;
  1904.     leftX = 0;                /* See Note A above. */
  1905.     leftXIn = 0;
  1906.     rightX = chunkPtr->x + chunkPtr->width;
  1907.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1908.     rightX = maxX;
  1909.     }
  1910.     chunkPtr2 = NULL;
  1911.     if (dlPtr->nextPtr != NULL) {
  1912.     /*
  1913.      * Find the chunk in the previous line that covers leftX.
  1914.      */
  1915.  
  1916.     nextPtr2 = dlPtr->nextPtr->chunkPtr;
  1917.     rightX2 = 0;            /* See Note A above. */
  1918.     while (rightX2 <= leftX) {
  1919.         chunkPtr2 = nextPtr2;
  1920.         if (chunkPtr2 == NULL) {
  1921.         break;
  1922.         }
  1923.         nextPtr2 = chunkPtr2->nextPtr;
  1924.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1925.         if (nextPtr2 == NULL) {
  1926.         rightX2 = INT_MAX;
  1927.         }
  1928.     }
  1929.     } else {
  1930.     nextPtr2 = NULL;
  1931.     rightX2 = INT_MAX;
  1932.     }
  1933.  
  1934.     while (leftX < maxX) {
  1935.     matchLeft = (chunkPtr2 != NULL)
  1936.         && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1937.     sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1938.     if (rightX <= rightX2) {
  1939.         if ((chunkPtr->nextPtr == NULL)
  1940.             || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1941.             chunkPtr->nextPtr->stylePtr)) {
  1942.         if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1943.             Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1944.                 sValuePtr->border, leftX + xOffset,
  1945.                 dlPtr->height - sValuePtr->borderWidth,
  1946.                 rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1947.                 0, 0, sValuePtr->relief);
  1948.         }
  1949.         leftX = rightX;
  1950.         leftXIn = 0;
  1951.         if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1952.             goto nextChunk2b;
  1953.         }
  1954.         }
  1955.         chunkPtr = chunkPtr->nextPtr;
  1956.         if (chunkPtr == NULL) {
  1957.         break;
  1958.         }
  1959.         rightX = chunkPtr->x + chunkPtr->width;
  1960.         if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1961.         rightX = maxX;
  1962.         }
  1963.         continue;
  1964.     }
  1965.  
  1966.     matchRight = (nextPtr2 != NULL)
  1967.         && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1968.     if (matchLeft && !matchRight) {
  1969.         if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1970.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1971.             rightX2 - sValuePtr->borderWidth + xOffset,
  1972.             dlPtr->height - sValuePtr->borderWidth,
  1973.             sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1974.             sValuePtr->relief);
  1975.         }
  1976.         leftX = rightX2 - sValuePtr->borderWidth;
  1977.         leftXIn = 1;
  1978.     } else if (!matchLeft && matchRight
  1979.         && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1980.         Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1981.             rightX2 + xOffset, dlPtr->height - sValuePtr->borderWidth,
  1982.             sValuePtr->borderWidth, sValuePtr->borderWidth,
  1983.             1, sValuePtr->relief);
  1984.         Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1985.             leftX + xOffset, dlPtr->height - sValuePtr->borderWidth,
  1986.             rightX2 + sValuePtr->borderWidth - leftX,
  1987.             sValuePtr->borderWidth, leftXIn, 1, 0, sValuePtr->relief);
  1988.     }
  1989.  
  1990.     nextChunk2b:
  1991.     chunkPtr2 = nextPtr2;
  1992.     if (chunkPtr2 == NULL) {
  1993.         rightX2 = INT_MAX;
  1994.     } else {
  1995.         nextPtr2 = chunkPtr2->nextPtr;
  1996.         rightX2 = chunkPtr2->x + chunkPtr2->width;
  1997.         if (nextPtr2 == NULL) {
  1998.         rightX2 = INT_MAX;
  1999.         }
  2000.     }
  2001.     }
  2002. }
  2003.  
  2004. /*
  2005.  *----------------------------------------------------------------------
  2006.  *
  2007.  * DisplayText --
  2008.  *
  2009.  *    This procedure is invoked as a when-idle handler to update the
  2010.  *    display.  It only redisplays the parts of the text widget that
  2011.  *    are out of date.
  2012.  *
  2013.  * Results:
  2014.  *    None.
  2015.  *
  2016.  * Side effects:
  2017.  *    Information is redrawn on the screen.
  2018.  *
  2019.  *----------------------------------------------------------------------
  2020.  */
  2021.  
  2022. static void
  2023. DisplayText(clientData)
  2024.     ClientData clientData;    /* Information about widget. */
  2025. {
  2026.     register TkText *textPtr = (TkText *) clientData;
  2027.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2028.     Tk_Window tkwin;
  2029.     register DLine *dlPtr;
  2030.     DLine *prevPtr;
  2031.     Pixmap pixmap;
  2032.     int maxHeight, borders;
  2033.     int bottomY = 0;        /* Initialization needed only to stop
  2034.                  * compiler warnings. */
  2035.     Tcl_Interp *interp;
  2036.  
  2037.     if (textPtr->tkwin == NULL) {
  2038.  
  2039.     /*
  2040.      * The widget has been deleted.  Don't do anything.
  2041.      */
  2042.  
  2043.     return;
  2044.     }
  2045.  
  2046.     interp = textPtr->interp;
  2047.     Tcl_Preserve((ClientData) interp);
  2048.  
  2049.     if (tkTextDebug) {
  2050.     Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
  2051.                 TCL_GLOBAL_ONLY);
  2052.     }
  2053.  
  2054.     if (textPtr->tkwin == NULL) {
  2055.  
  2056.     /*
  2057.      * The widget has been deleted.  Don't do anything.
  2058.      */
  2059.  
  2060.         goto end;
  2061.     }
  2062.  
  2063.     if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
  2064.         || (dInfoPtr->maxY <= dInfoPtr->y)) {
  2065.     UpdateDisplayInfo(textPtr);
  2066.     dInfoPtr->flags &= ~REDRAW_PENDING;
  2067.     goto doScrollbars;
  2068.     }
  2069.     numRedisplays++;
  2070.     if (tkTextDebug) {
  2071.     Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
  2072.                 TCL_GLOBAL_ONLY);
  2073.     }
  2074.  
  2075.     if (textPtr->tkwin == NULL) {
  2076.  
  2077.     /*
  2078.      * The widget has been deleted.  Don't do anything.
  2079.      */
  2080.  
  2081.     goto end;
  2082.     }
  2083.  
  2084.     /*
  2085.      * Choose a new current item if that is needed (this could cause
  2086.      * event handlers to be invoked, hence the preserve/release calls
  2087.      * and the loop, since the handlers could conceivably necessitate
  2088.      * yet another current item calculation).  The tkwin check is because
  2089.      * the whole window could go away in the Tcl_Release call.
  2090.      */
  2091.  
  2092.     while (dInfoPtr->flags & REPICK_NEEDED) {
  2093.     Tcl_Preserve((ClientData) textPtr);
  2094.     dInfoPtr->flags &= ~REPICK_NEEDED;
  2095.     TkTextPickCurrent(textPtr, &textPtr->pickEvent);
  2096.     tkwin = textPtr->tkwin;
  2097.     Tcl_Release((ClientData) textPtr);
  2098.     if (tkwin == NULL) {
  2099.         goto end;
  2100.     }
  2101.     }
  2102.  
  2103.     /*
  2104.      * First recompute what's supposed to be displayed.
  2105.      */
  2106.  
  2107.     UpdateDisplayInfo(textPtr);
  2108.     dInfoPtr->dLinesInvalidated = 0;
  2109.  
  2110.     /*
  2111.      * See if it's possible to bring some parts of the screen up-to-date
  2112.      * by scrolling (copying from other parts of the screen).
  2113.      */
  2114.  
  2115.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  2116.     register DLine *dlPtr2;
  2117.     int offset, height, y, oldY;
  2118.     TkRegion damageRgn;
  2119.  
  2120.     if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
  2121.         || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
  2122.         continue;
  2123.     }
  2124.  
  2125.     /*
  2126.      * This line is already drawn somewhere in the window so it only
  2127.      * needs to be copied to its new location.  See if there's a group
  2128.      * of lines that can all be copied together.
  2129.      */
  2130.  
  2131.     offset = dlPtr->y - dlPtr->oldY;
  2132.     height = dlPtr->height;
  2133.     y = dlPtr->y;
  2134.     for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
  2135.         dlPtr2 = dlPtr2->nextPtr) {
  2136.         if ((dlPtr2->oldY == -1)
  2137.             || ((dlPtr2->oldY + offset) != dlPtr2->y)
  2138.             || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
  2139.         break;
  2140.         }
  2141.         height += dlPtr2->height;
  2142.     }
  2143.  
  2144.     /*
  2145.      * Reduce the height of the area being copied if necessary to
  2146.      * avoid overwriting the border area.
  2147.      */
  2148.  
  2149.     if ((y + height) > dInfoPtr->maxY) {
  2150.         height = dInfoPtr->maxY -y;
  2151.     }
  2152.     oldY = dlPtr->oldY;
  2153.  
  2154.     /*
  2155.      * Update the lines we are going to scroll to show that they
  2156.      * have been copied.
  2157.      */
  2158.  
  2159.     while (1) {
  2160.         dlPtr->oldY = dlPtr->y;
  2161.         if (dlPtr->nextPtr == dlPtr2) {
  2162.         break;
  2163.         }
  2164.         dlPtr = dlPtr->nextPtr;
  2165.     }
  2166.  
  2167.     /*
  2168.      * Scan through the lines following the copied ones to see if
  2169.      * we are going to overwrite them with the copy operation.
  2170.      * If so, mark them for redisplay.
  2171.      */
  2172.  
  2173.     for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  2174.         if ((dlPtr2->oldY != -1)
  2175.             && ((dlPtr2->oldY + dlPtr2->height) > y)
  2176.             && (dlPtr2->oldY < (y + height))) {
  2177.         dlPtr2->oldY = -1;
  2178.         }
  2179.     }
  2180.  
  2181.     /*
  2182.      * Now scroll the lines.  This may generate damage which we
  2183.      * handle by calling TextInvalidateRegion to mark the display
  2184.      * blocks as stale.
  2185.      */
  2186.  
  2187.     damageRgn = TkCreateRegion();
  2188.     if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
  2189.         dInfoPtr->x, oldY,
  2190.         (dInfoPtr->maxX - dInfoPtr->x), height,
  2191.         0, y - oldY, damageRgn)) {
  2192.         TextInvalidateRegion(textPtr, damageRgn);
  2193.     }
  2194.     numCopies++;
  2195.     TkDestroyRegion(damageRgn);
  2196.     }
  2197.  
  2198.     /*
  2199.      * Clear the REDRAW_PENDING flag here.  This is actually pretty
  2200.      * tricky.  We want to wait until *after* doing the scrolling,
  2201.      * since that could generate more areas to redraw and don't
  2202.      * want to reschedule a redisplay for them.  On the other hand,
  2203.      * we can't wait until after all the redisplaying, because the
  2204.      * act of redisplaying could actually generate more redisplays
  2205.      * (e.g. in the case of a nested window with event bindings triggered
  2206.      * by redisplay).
  2207.      */
  2208.  
  2209.     dInfoPtr->flags &= ~REDRAW_PENDING;
  2210.  
  2211.     /*
  2212.      * Redraw the borders if that's needed.
  2213.      */
  2214.  
  2215.     if (dInfoPtr->flags & REDRAW_BORDERS) {
  2216.     if (tkTextDebug) {
  2217.         Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
  2218.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2219.     }
  2220.  
  2221.         if (textPtr->tkwin == NULL) {
  2222.  
  2223.         /*
  2224.              * The widget has been deleted.  Don't do anything.
  2225.              */
  2226.  
  2227.             goto end;
  2228.         }
  2229.  
  2230.     Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2231.         textPtr->border, textPtr->highlightWidth,
  2232.         textPtr->highlightWidth,
  2233.         Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2234.         Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2235.         textPtr->borderWidth, textPtr->relief);
  2236.     if (textPtr->highlightWidth != 0) {
  2237.         GC gc;
  2238.     
  2239.         if (textPtr->flags & GOT_FOCUS) {
  2240.         gc = Tk_GCForColor(textPtr->highlightColorPtr,
  2241.             Tk_WindowId(textPtr->tkwin));
  2242.         } else {
  2243.         gc = Tk_GCForColor(textPtr->highlightBgColorPtr,
  2244.             Tk_WindowId(textPtr->tkwin));
  2245.         }
  2246.         Tk_DrawFocusHighlight(textPtr->tkwin, gc, textPtr->highlightWidth,
  2247.             Tk_WindowId(textPtr->tkwin));
  2248.     }
  2249.     borders = textPtr->borderWidth + textPtr->highlightWidth;
  2250.     if (textPtr->padY > 0) {
  2251.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2252.             textPtr->border, borders, borders,
  2253.             Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
  2254.             0, TK_RELIEF_FLAT);
  2255.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2256.             textPtr->border, borders,
  2257.             Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
  2258.             Tk_Width(textPtr->tkwin) - 2*borders,
  2259.             textPtr->padY, 0, TK_RELIEF_FLAT);
  2260.     }
  2261.     if (textPtr->padX > 0) {
  2262.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2263.             textPtr->border, borders, borders + textPtr->padY,
  2264.             textPtr->padX,
  2265.             Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2266.             0, TK_RELIEF_FLAT);
  2267.         Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2268.             textPtr->border,
  2269.             Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
  2270.             borders + textPtr->padY, textPtr->padX,
  2271.             Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2272.             0, TK_RELIEF_FLAT);
  2273.     }
  2274.     dInfoPtr->flags &= ~REDRAW_BORDERS;
  2275.     }
  2276.  
  2277.     /*
  2278.      * Now we have to redraw the lines that couldn't be updated by
  2279.      * scrolling.  First, compute the height of the largest line and
  2280.      * allocate an off-screen pixmap to use for double-buffered
  2281.      * displays.
  2282.      */
  2283.  
  2284.     maxHeight = -1;
  2285.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  2286.         dlPtr = dlPtr->nextPtr) {
  2287.     if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
  2288.         maxHeight = dlPtr->height;
  2289.     }
  2290.     bottomY = dlPtr->y + dlPtr->height;
  2291.     }
  2292.     if (maxHeight > dInfoPtr->maxY) {
  2293.     maxHeight = dInfoPtr->maxY;
  2294.     }
  2295.     if (maxHeight > 0) {
  2296.     pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
  2297.         Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
  2298.         maxHeight, Tk_Depth(textPtr->tkwin));
  2299.     for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
  2300.         (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
  2301.         prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
  2302.         if (dlPtr->oldY != dlPtr->y) {
  2303.         if (tkTextDebug) {
  2304.             char string[TK_POS_CHARS];
  2305.             TkTextPrintIndex(&dlPtr->index, string);
  2306.             Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2307.                 (char *) NULL, string,
  2308.                 TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2309.         }
  2310.         DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
  2311.         if (dInfoPtr->dLinesInvalidated) {
  2312.             Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2313.             return;
  2314.         }
  2315.         dlPtr->oldY = dlPtr->y;
  2316.         dlPtr->flags &= ~NEW_LAYOUT;
  2317.         }
  2318.     }
  2319.     Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2320.     }
  2321.  
  2322.     /*
  2323.      * See if we need to refresh the part of the window below the
  2324.      * last line of text (if there is any such area).  Refresh the
  2325.      * padding area on the left too, since the insertion cursor might
  2326.      * have been displayed there previously).
  2327.      */
  2328.  
  2329.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  2330.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  2331.     }
  2332.     if (bottomY < dInfoPtr->topOfEof) {
  2333.     if (tkTextDebug) {
  2334.         Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2335.             (char *) NULL, "eof",
  2336.             TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2337.     }
  2338.  
  2339.         if (textPtr->tkwin == NULL) {
  2340.  
  2341.         /*
  2342.              * The widget has been deleted.  Don't do anything.
  2343.              */
  2344.  
  2345.             goto end;
  2346.         }
  2347.  
  2348.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2349.         textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
  2350.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  2351.         dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
  2352.     }
  2353.     dInfoPtr->topOfEof = bottomY;
  2354.  
  2355.     doScrollbars:
  2356.  
  2357.     /*
  2358.      * Update the vertical scrollbar, if there is one.  Note:  it's
  2359.      * important to clear REDRAW_PENDING here, just in case the
  2360.      * scroll procedure does something that requires redisplay.
  2361.      */
  2362.     
  2363.     if (textPtr->flags & UPDATE_SCROLLBARS) {
  2364.     textPtr->flags &= ~UPDATE_SCROLLBARS;
  2365.     if (textPtr->yScrollCmd != NULL) {
  2366.         GetYView(textPtr->interp, textPtr, 1);
  2367.     }
  2368.  
  2369.         if (textPtr->tkwin == NULL) {
  2370.  
  2371.         /*
  2372.              * The widget has been deleted.  Don't do anything.
  2373.              */
  2374.  
  2375.             goto end;
  2376.         }
  2377.  
  2378.     /*
  2379.      * Update the horizontal scrollbar, if any.
  2380.      */
  2381.  
  2382.     if (textPtr->xScrollCmd != NULL) {
  2383.         GetXView(textPtr->interp, textPtr, 1);
  2384.     }
  2385.     }
  2386.  
  2387. end:
  2388.     Tcl_Release((ClientData) interp);
  2389. }
  2390.  
  2391. /*
  2392.  *----------------------------------------------------------------------
  2393.  *
  2394.  * TkTextEventuallyRepick --
  2395.  *
  2396.  *    This procedure is invoked whenever something happens that
  2397.  *    could change the current character or the tags associated
  2398.  *    with it.
  2399.  *
  2400.  * Results:
  2401.  *    None.
  2402.  *
  2403.  * Side effects:
  2404.  *    A repick is scheduled as an idle handler.
  2405.  *
  2406.  *----------------------------------------------------------------------
  2407.  */
  2408.  
  2409.     /* ARGSUSED */
  2410. void
  2411. TkTextEventuallyRepick(textPtr)
  2412.     TkText *textPtr;        /* Widget record for text widget. */
  2413. {
  2414.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2415.  
  2416.     dInfoPtr->flags |= REPICK_NEEDED;
  2417.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2418.     dInfoPtr->flags |= REDRAW_PENDING;
  2419.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2420.     }
  2421. }
  2422.  
  2423. /*
  2424.  *----------------------------------------------------------------------
  2425.  *
  2426.  * TkTextRedrawRegion --
  2427.  *
  2428.  *    This procedure is invoked to schedule a redisplay for a given
  2429.  *    region of a text widget.  The redisplay itself may not occur
  2430.  *    immediately:  it's scheduled as a when-idle handler.
  2431.  *
  2432.  * Results:
  2433.  *    None.
  2434.  *
  2435.  * Side effects:
  2436.  *    Information will eventually be redrawn on the screen.
  2437.  *
  2438.  *----------------------------------------------------------------------
  2439.  */
  2440.  
  2441.     /* ARGSUSED */
  2442. void
  2443. TkTextRedrawRegion(textPtr, x, y, width, height)
  2444.     TkText *textPtr;        /* Widget record for text widget. */
  2445.     int x, y;            /* Coordinates of upper-left corner of area
  2446.                  * to be redrawn, in pixels relative to
  2447.                  * textPtr's window. */
  2448.     int width, height;        /* Width and height of area to be redrawn. */
  2449. {
  2450.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2451.     TkRegion damageRgn = TkCreateRegion();
  2452.     XRectangle rect;
  2453.  
  2454.     rect.x = x;
  2455.     rect.y = y;
  2456.     rect.width = width;
  2457.     rect.height = height;
  2458.     TkUnionRectWithRegion(&rect, damageRgn, damageRgn);
  2459.  
  2460.     TextInvalidateRegion(textPtr, damageRgn);
  2461.  
  2462.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2463.     dInfoPtr->flags |= REDRAW_PENDING;
  2464.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2465.     }
  2466.     TkDestroyRegion(damageRgn);
  2467. }
  2468.  
  2469. /*
  2470.  *----------------------------------------------------------------------
  2471.  *
  2472.  * TextInvalidateRegion --
  2473.  *
  2474.  *    Mark a region of text as invalid.
  2475.  *
  2476.  * Results:
  2477.  *    None.
  2478.  *
  2479.  * Side effects:
  2480.  *    Updates the display information for the text widget.
  2481.  *
  2482.  *----------------------------------------------------------------------
  2483.  */
  2484.  
  2485. static void
  2486. TextInvalidateRegion(textPtr, region)
  2487.     TkText *textPtr;        /* Widget record for text widget. */
  2488.     TkRegion region;        /* Region of area to redraw. */
  2489. {
  2490.     register DLine *dlPtr;
  2491.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2492.     int maxY, inset;
  2493.     XRectangle rect;
  2494.  
  2495.     /*
  2496.      * Find all lines that overlap the given region and mark them for
  2497.      * redisplay.
  2498.      */
  2499.  
  2500.     TkClipBox(region, &rect);
  2501.     maxY = rect.y + rect.height;
  2502.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  2503.         dlPtr = dlPtr->nextPtr) {
  2504.     if ((dlPtr->oldY != -1) && (TkRectInRegion(region, rect.x, dlPtr->y,
  2505.         rect.width, (unsigned int) dlPtr->height) != RectangleOut)) {
  2506.         dlPtr->oldY = -1;
  2507.     }
  2508.     }
  2509.     if (dInfoPtr->topOfEof < maxY) {
  2510.     dInfoPtr->topOfEof = maxY;
  2511.     }
  2512.  
  2513.     /*
  2514.      * Schedule the redisplay operation if there isn't one already
  2515.      * scheduled.
  2516.      */
  2517.  
  2518.     inset = textPtr->borderWidth + textPtr->highlightWidth;
  2519.     if ((rect.x < (inset + textPtr->padX))
  2520.         || (rect.y < (inset + textPtr->padY))
  2521.         || ((int) (rect.x + rect.width) > (Tk_Width(textPtr->tkwin)
  2522.             - inset - textPtr->padX))
  2523.         || (maxY > (Tk_Height(textPtr->tkwin) - inset - textPtr->padY))) {
  2524.     dInfoPtr->flags |= REDRAW_BORDERS;
  2525.     }
  2526. }
  2527.  
  2528. /*
  2529.  *----------------------------------------------------------------------
  2530.  *
  2531.  * TkTextChanged --
  2532.  *
  2533.  *    This procedure is invoked when info in a text widget is about
  2534.  *    to be modified in a way that changes how it is displayed (e.g.
  2535.  *    characters were inserted or deleted, or tag information was
  2536.  *    changed).  This procedure must be called *before* a change is
  2537.  *    made, so that indexes in the display information are still
  2538.  *    valid.
  2539.  *
  2540.  * Results:
  2541.  *    None.
  2542.  *
  2543.  * Side effects:
  2544.  *    The range of character between index1Ptr (inclusive) and
  2545.  *    index2Ptr (exclusive) will be redisplayed at some point in the
  2546.  *    future (the actual redisplay is scheduled as a when-idle handler).
  2547.  *
  2548.  *----------------------------------------------------------------------
  2549.  */
  2550.  
  2551. void
  2552. TkTextChanged(textPtr, index1Ptr, index2Ptr)
  2553.     TkText *textPtr;        /* Widget record for text widget. */
  2554.     TkTextIndex *index1Ptr;    /* Index of first character to redisplay. */
  2555.     TkTextIndex *index2Ptr;    /* Index of character just after last one
  2556.                  * to redisplay. */
  2557. {
  2558.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2559.     DLine *firstPtr, *lastPtr;
  2560.     TkTextIndex rounded;
  2561.  
  2562.     /*
  2563.      * Schedule both a redisplay and a recomputation of display information.
  2564.      * It's done here rather than the end of the procedure for two reasons:
  2565.      *
  2566.      * 1. If there are no display lines to update we'll want to return
  2567.      *    immediately, well before the end of the procedure.
  2568.      * 2. It's important to arrange for the redisplay BEFORE calling
  2569.      *    FreeDLines.  The reason for this is subtle and has to do with
  2570.      *    embedded windows.  The chunk delete procedure for an embedded
  2571.      *    window will schedule an idle handler to unmap the window.
  2572.      *    However, we want the idle handler for redisplay to be called
  2573.      *    first, so that it can put the embedded window back on the screen
  2574.      *    again (if appropriate).  This will prevent the window from ever
  2575.      *    being unmapped, and thereby avoid flashing.
  2576.      */
  2577.  
  2578.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2579.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2580.     }
  2581.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  2582.  
  2583.     /*
  2584.      * Find the DLines corresponding to index1Ptr and index2Ptr.  There
  2585.      * is one tricky thing here, which is that we have to relayout in
  2586.      * units of whole text lines:  round index1Ptr back to the beginning
  2587.      * of its text line, and include all the display lines after index2,
  2588.      * up to the end of its text line.  This is necessary because the
  2589.      * indices stored in the display lines will no longer be valid.  It's
  2590.      * also needed because any edit could change the way lines wrap.
  2591.      */
  2592.  
  2593.     rounded = *index1Ptr;
  2594.     rounded.charIndex = 0;
  2595.     firstPtr = FindDLine(dInfoPtr->dLinePtr, &rounded);
  2596.     if (firstPtr == NULL) {
  2597.     return;
  2598.     }
  2599.     lastPtr = FindDLine(dInfoPtr->dLinePtr, index2Ptr);
  2600.     while ((lastPtr != NULL)
  2601.         && (lastPtr->index.linePtr == index2Ptr->linePtr)) {
  2602.     lastPtr = lastPtr->nextPtr;
  2603.     }
  2604.  
  2605.     /*
  2606.      * Delete all the DLines from firstPtr up to but not including lastPtr.
  2607.      */
  2608.  
  2609.     FreeDLines(textPtr, firstPtr, lastPtr, 1);
  2610. }
  2611.  
  2612. /*
  2613.  *----------------------------------------------------------------------
  2614.  *
  2615.  * TkTextRedrawTag --
  2616.  *
  2617.  *    This procedure is invoked to request a redraw of all characters
  2618.  *    in a given range that have a particular tag on or off.  It's
  2619.  *    called, for example, when tag options change.
  2620.  *
  2621.  * Results:
  2622.  *    None.
  2623.  *
  2624.  * Side effects:
  2625.  *    Information on the screen may be redrawn, and the layout of
  2626.  *    the screen may change.
  2627.  *
  2628.  *----------------------------------------------------------------------
  2629.  */
  2630.  
  2631. void
  2632. TkTextRedrawTag(textPtr, index1Ptr, index2Ptr, tagPtr, withTag)
  2633.     TkText *textPtr;        /* Widget record for text widget. */
  2634.     TkTextIndex *index1Ptr;    /* First character in range to consider
  2635.                  * for redisplay.  NULL means start at
  2636.                  * beginning of text. */
  2637.     TkTextIndex *index2Ptr;    /* Character just after last one to consider
  2638.                  * for redisplay.  NULL means process all
  2639.                  * the characters in the text. */
  2640.     TkTextTag *tagPtr;        /* Information about tag. */
  2641.     int withTag;        /* 1 means redraw characters that have the
  2642.                  * tag, 0 means redraw those without. */
  2643. {
  2644.     register DLine *dlPtr;
  2645.     DLine *endPtr;
  2646.     int tagOn;
  2647.     TkTextSearch search;
  2648.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2649.     TkTextIndex *curIndexPtr;
  2650.     TkTextIndex endOfText, *endIndexPtr;
  2651.  
  2652.     /*
  2653.      * Round up the starting position if it's before the first line
  2654.      * visible on the screen (we only care about what's on the screen).
  2655.      */
  2656.  
  2657.     dlPtr = dInfoPtr->dLinePtr;
  2658.     if (dlPtr == NULL) {
  2659.     return;
  2660.     }
  2661.     if ((index1Ptr == NULL) || (TkTextIndexCmp(&dlPtr->index, index1Ptr) > 0)) {
  2662.     index1Ptr = &dlPtr->index;
  2663.     }
  2664.  
  2665.     /*
  2666.      * Set the stopping position if it wasn't specified.
  2667.      */
  2668.  
  2669.     if (index2Ptr == NULL) {
  2670.     index2Ptr = TkTextMakeIndex(textPtr->tree,
  2671.         TkBTreeNumLines(textPtr->tree), 0, &endOfText);
  2672.     }
  2673.  
  2674.     /* 
  2675.      * Initialize a search through all transitions on the tag, starting
  2676.      * with the first transition where the tag's current state is different
  2677.      * from what it will eventually be.
  2678.      */
  2679.  
  2680.     TkBTreeStartSearch(index1Ptr, index2Ptr, tagPtr, &search);
  2681.     /*
  2682.      * Make our own curIndex because at this point search.curIndex
  2683.      * may not equal index1Ptr->curIndex in the case the first tag toggle
  2684.      * comes after index1Ptr (See the use of FindTagStart in TkBTreeStartSearch)
  2685.      */
  2686.     curIndexPtr = index1Ptr;
  2687.     tagOn = TkBTreeCharTagged(index1Ptr, tagPtr);
  2688.     if (tagOn != withTag) {
  2689.     if (!TkBTreeNextTag(&search)) {
  2690.         return;
  2691.     }
  2692.     curIndexPtr = &search.curIndex;
  2693.     }
  2694.  
  2695.     /*
  2696.      * Schedule a redisplay and layout recalculation if they aren't
  2697.      * already pending.  This has to be done before calling FreeDLines,
  2698.      * for the reason given in TkTextChanged.
  2699.      */
  2700.  
  2701.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2702.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2703.     }
  2704.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  2705.  
  2706.     /*
  2707.      * Each loop through the loop below is for one range of characters
  2708.      * where the tag's current state is different than its eventual
  2709.      * state.  At the top of the loop, search contains information about
  2710.      * the first character in the range.
  2711.      */
  2712.  
  2713.     while (1) {
  2714.     /*
  2715.      * Find the first DLine structure in the range.  Note: if the
  2716.      * desired character isn't the first in its text line, then look
  2717.      * for the character just before it instead.  This is needed to
  2718.      * handle the case where the first character of a wrapped
  2719.      * display line just got smaller, so that it now fits on the
  2720.      * line before:  need to relayout the line containing the
  2721.      * previous character.
  2722.      */
  2723.  
  2724.     if (curIndexPtr->charIndex == 0) {
  2725.         dlPtr = FindDLine(dlPtr, curIndexPtr);
  2726.     } else {
  2727.         TkTextIndex tmp;
  2728.  
  2729.         tmp = *curIndexPtr;
  2730.         tmp.charIndex -= 1;
  2731.         dlPtr = FindDLine(dlPtr, &tmp);
  2732.     }
  2733.     if (dlPtr == NULL) {
  2734.         break;
  2735.     }
  2736.  
  2737.     /*
  2738.      * Find the first DLine structure that's past the end of the range.
  2739.      */
  2740.  
  2741.     if (!TkBTreeNextTag(&search)) {
  2742.         endIndexPtr = index2Ptr;
  2743.     } else {
  2744.         curIndexPtr = &search.curIndex;
  2745.         endIndexPtr = curIndexPtr;
  2746.     }
  2747.     endPtr = FindDLine(dlPtr, endIndexPtr);
  2748.     if ((endPtr != NULL) && (endPtr->index.linePtr == endIndexPtr->linePtr)
  2749.         && (endPtr->index.charIndex < endIndexPtr->charIndex)) {
  2750.         endPtr = endPtr->nextPtr;
  2751.     }
  2752.  
  2753.     /*
  2754.      * Delete all of the display lines in the range, so that they'll
  2755.      * be re-layed out and redrawn.
  2756.      */
  2757.  
  2758.     FreeDLines(textPtr, dlPtr, endPtr, 1);
  2759.     dlPtr = endPtr;
  2760.  
  2761.     /*
  2762.      * Find the first text line in the next range.
  2763.      */
  2764.  
  2765.     if (!TkBTreeNextTag(&search)) {
  2766.         break;
  2767.     }
  2768.     }
  2769. }
  2770.  
  2771. /*
  2772.  *----------------------------------------------------------------------
  2773.  *
  2774.  * TkTextRelayoutWindow --
  2775.  *
  2776.  *    This procedure is called when something has happened that
  2777.  *    invalidates the whole layout of characters on the screen, such
  2778.  *    as a change in a configuration option for the overall text
  2779.  *    widget or a change in the window size.  It causes all display
  2780.  *    information to be recomputed and the window to be redrawn.
  2781.  *
  2782.  * Results:
  2783.  *    None.
  2784.  *
  2785.  * Side effects:
  2786.  *    All the display information will be recomputed for the window
  2787.  *    and the window will be redrawn.
  2788.  *
  2789.  *----------------------------------------------------------------------
  2790.  */
  2791.  
  2792. void
  2793. TkTextRelayoutWindow(textPtr)
  2794.     TkText *textPtr;        /* Widget record for text widget. */
  2795. {
  2796.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2797.     GC new;
  2798.     XGCValues gcValues;
  2799.  
  2800.     /*
  2801.      * Schedule the window redisplay.  See TkTextChanged for the
  2802.      * reason why this has to be done before any calls to FreeDLines.
  2803.      */
  2804.  
  2805.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2806.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2807.     }
  2808.     dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
  2809.         |REPICK_NEEDED;
  2810.  
  2811.     /*
  2812.      * (Re-)create the graphics context for drawing the traversal
  2813.      * highlight.
  2814.      */
  2815.  
  2816.     gcValues.graphics_exposures = False;
  2817.     new = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
  2818.     if (dInfoPtr->copyGC != None) {
  2819.     Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  2820.     }
  2821.     dInfoPtr->copyGC = new;
  2822.  
  2823.     /*
  2824.      * Throw away all the current layout information.
  2825.      */
  2826.  
  2827.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  2828.     dInfoPtr->dLinePtr = NULL;
  2829.  
  2830.     /*
  2831.      * Recompute some overall things for the layout.  Even if the
  2832.      * window gets very small, pretend that there's at least one
  2833.      * pixel of drawing space in it.
  2834.      */
  2835.  
  2836.     if (textPtr->highlightWidth < 0) {
  2837.     textPtr->highlightWidth = 0;
  2838.     }
  2839.     dInfoPtr->x = textPtr->highlightWidth + textPtr->borderWidth
  2840.         + textPtr->padX;
  2841.     dInfoPtr->y = textPtr->highlightWidth + textPtr->borderWidth
  2842.         + textPtr->padY;
  2843.     dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - textPtr->highlightWidth
  2844.         - textPtr->borderWidth - textPtr->padX;
  2845.     if (dInfoPtr->maxX <= dInfoPtr->x) {
  2846.     dInfoPtr->maxX = dInfoPtr->x + 1;
  2847.     }
  2848.     dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - textPtr->highlightWidth
  2849.         - textPtr->borderWidth - textPtr->padY;
  2850.     if (dInfoPtr->maxY <= dInfoPtr->y) {
  2851.     dInfoPtr->maxY = dInfoPtr->y + 1;
  2852.     }
  2853.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  2854.  
  2855.     /*
  2856.      * If the upper-left character isn't the first in a line, recompute
  2857.      * it.  This is necessary because a change in the window's size
  2858.      * or options could change the way lines wrap.
  2859.      */
  2860.  
  2861.     if (textPtr->topIndex.charIndex != 0) {
  2862.     MeasureUp(textPtr, &textPtr->topIndex, 0, &textPtr->topIndex);
  2863.     }
  2864.  
  2865.     /*
  2866.      * Invalidate cached scrollbar positions, so that scrollbars
  2867.      * sliders will be udpated.
  2868.      */
  2869.  
  2870.     dInfoPtr->xScrollFirst = dInfoPtr->xScrollLast = -1;
  2871.     dInfoPtr->yScrollFirst = dInfoPtr->yScrollLast = -1;
  2872. }
  2873.  
  2874. /*
  2875.  *----------------------------------------------------------------------
  2876.  *
  2877.  * TkTextSetYView --
  2878.  *
  2879.  *    This procedure is called to specify what lines are to be
  2880.  *    displayed in a text widget.
  2881.  *
  2882.  * Results:
  2883.  *    None.
  2884.  *
  2885.  * Side effects:
  2886.  *    The display will (eventually) be updated so that the position
  2887.  *    given by "indexPtr" is visible on the screen at the position
  2888.  *    determined by "pickPlace".
  2889.  *
  2890.  *----------------------------------------------------------------------
  2891.  */
  2892.  
  2893. void
  2894. TkTextSetYView(textPtr, indexPtr, pickPlace)
  2895.     TkText *textPtr;        /* Widget record for text widget. */
  2896.     TkTextIndex *indexPtr;    /* Position that is to appear somewhere
  2897.                  * in the view. */
  2898.     int pickPlace;        /* 0 means topLine must appear at top of
  2899.                  * screen.  1 means we get to pick where it
  2900.                  * appears:  minimize screen motion or else
  2901.                  * display line at center of screen. */
  2902. {
  2903.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2904.     register DLine *dlPtr;
  2905.     int bottomY, close, lineIndex;
  2906.     TkTextIndex tmpIndex, rounded;
  2907.     Tk_FontMetrics fm;
  2908.  
  2909.     /*
  2910.      * If the specified position is the extra line at the end of the
  2911.      * text, round it back to the last real line.
  2912.      */
  2913.  
  2914.     lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
  2915.     if (lineIndex == TkBTreeNumLines(indexPtr->tree)) {
  2916.     TkTextIndexBackChars(indexPtr, 1, &rounded);
  2917.     indexPtr = &rounded;
  2918.     }
  2919.  
  2920.     if (!pickPlace) {
  2921.     /*
  2922.      * The specified position must go at the top of the screen.
  2923.      * Just leave all the DLine's alone: we may be able to reuse
  2924.      * some of the information that's currently on the screen
  2925.      * without redisplaying it all.
  2926.      */
  2927.  
  2928.     if (indexPtr->charIndex == 0) {
  2929.         textPtr->topIndex = *indexPtr;
  2930.     } else {
  2931.         MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
  2932.     }
  2933.     goto scheduleUpdate;
  2934.     }
  2935.  
  2936.     /*
  2937.      * We have to pick where to display the index.  First, bring
  2938.      * the display information up to date and see if the index will be
  2939.      * completely visible in the current screen configuration.  If so
  2940.      * then there's nothing to do.
  2941.      */
  2942.  
  2943.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  2944.     UpdateDisplayInfo(textPtr);
  2945.     }
  2946.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  2947.     if (dlPtr != NULL) {
  2948.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  2949.         /*
  2950.          * Part of the line hangs off the bottom of the screen;
  2951.          * pretend the whole line is off-screen.
  2952.          */
  2953.  
  2954.         dlPtr = NULL;
  2955.     } else if ((dlPtr->index.linePtr == indexPtr->linePtr)
  2956.         && (dlPtr->index.charIndex <= indexPtr->charIndex)) {
  2957.         return;
  2958.     }
  2959.     }
  2960.  
  2961.     /*
  2962.      * The desired line isn't already on-screen.  Figure out what
  2963.      * it means to be "close" to the top or bottom of the screen.
  2964.      * Close means within 1/3 of the screen height or within three
  2965.      * lines, whichever is greater.  Add one extra line also, to
  2966.      * account for the way MeasureUp rounds.
  2967.      */
  2968.  
  2969.     Tk_GetFontMetrics(textPtr->tkfont, &fm);
  2970.     bottomY = (dInfoPtr->y + dInfoPtr->maxY + fm.linespace)/2;
  2971.     close = (dInfoPtr->maxY - dInfoPtr->y)/3;
  2972.     if (close < 3*fm.linespace) {
  2973.     close = 3*fm.linespace;
  2974.     }
  2975.     close += fm.linespace;
  2976.     if (dlPtr != NULL) {
  2977.     /*
  2978.      * The desired line is above the top of screen.  If it is
  2979.      * "close" to the top of the window then make it the top
  2980.      * line on the screen.
  2981.      */
  2982.  
  2983.     MeasureUp(textPtr, &textPtr->topIndex, close, &tmpIndex);
  2984.     if (TkTextIndexCmp(&tmpIndex, indexPtr) <= 0) {
  2985.         MeasureUp(textPtr, indexPtr, 0, &textPtr->topIndex);
  2986.         goto scheduleUpdate;
  2987.     }
  2988.     } else {
  2989.     /*
  2990.      * The desired line is below the bottom of the screen.  If it is
  2991.      * "close" to the bottom of the screen then position it at the
  2992.      * bottom of the screen.
  2993.      */
  2994.  
  2995.     MeasureUp(textPtr, indexPtr, close, &tmpIndex);
  2996.     if (FindDLine(dInfoPtr->dLinePtr, &tmpIndex) != NULL) {
  2997.         bottomY = dInfoPtr->maxY - dInfoPtr->y;
  2998.     }
  2999.     }
  3000.  
  3001.     /*
  3002.      * Our job now is to arrange the display so that indexPtr appears
  3003.      * as low on the screen as possible but with its bottom no lower
  3004.      * than bottomY.  BottomY is the bottom of the window if the
  3005.      * desired line is just below the current screen, otherwise it
  3006.      * is a half-line lower than the center of the window.
  3007.      */
  3008.  
  3009.     MeasureUp(textPtr, indexPtr, bottomY, &textPtr->topIndex);
  3010.  
  3011.     scheduleUpdate:
  3012.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3013.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3014.     }
  3015.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  3016. }
  3017.  
  3018. /*
  3019.  *--------------------------------------------------------------
  3020.  *
  3021.  * MeasureUp --
  3022.  *
  3023.  *    Given one index, find the index of the first character
  3024.  *    on the highest display line that would be displayed no more
  3025.  *    than "distance" pixels above the given index.
  3026.  *
  3027.  * Results:
  3028.  *    *dstPtr is filled in with the index of the first character
  3029.  *    on a display line.  The display line is found by measuring
  3030.  *    up "distance" pixels above the pixel just below an imaginary
  3031.  *    display line that contains srcPtr.  If the display line
  3032.  *    that covers this coordinate actually extends above the 
  3033.  *    coordinate, then return the index of the next lower line
  3034.  *    instead (i.e. the returned index will be completely visible
  3035.  *    at or below the given y-coordinate).
  3036.  *
  3037.  * Side effects:
  3038.  *    None.
  3039.  *
  3040.  *--------------------------------------------------------------
  3041.  */
  3042.  
  3043. static void
  3044. MeasureUp(textPtr, srcPtr, distance, dstPtr)
  3045.     TkText *textPtr;        /* Text widget in which to measure. */
  3046.     TkTextIndex *srcPtr;    /* Index of character from which to start
  3047.                  * measuring. */
  3048.     int distance;        /* Vertical distance in pixels measured
  3049.                  * from the pixel just below the lowest
  3050.                  * one in srcPtr's line. */
  3051.     TkTextIndex *dstPtr;    /* Index to fill in with result. */
  3052. {
  3053.     int lineNum;        /* Number of current line. */
  3054.     int charsToCount;        /* Maximum number of characters to measure
  3055.                  * in current line. */
  3056.     TkTextIndex bestIndex;    /* Best candidate seen so far for result. */
  3057.     TkTextIndex index;
  3058.     DLine *dlPtr, *lowestPtr;
  3059.     int noBestYet;        /* 1 means bestIndex hasn't been set. */
  3060.  
  3061.     noBestYet = 1;
  3062.     charsToCount = srcPtr->charIndex + 1;
  3063.     index.tree = srcPtr->tree;
  3064.     for (lineNum = TkBTreeLineIndex(srcPtr->linePtr); lineNum >= 0;
  3065.         lineNum--) {
  3066.     /*
  3067.      * Layout an entire text line (potentially > 1 display line).
  3068.      * For the first line, which contains srcPtr, only layout the
  3069.      * part up through srcPtr (charsToCount is non-infinite to
  3070.      * accomplish this).  Make a list of all the display lines
  3071.      * in backwards order (the lowest DLine on the screen is first
  3072.      * in the list).
  3073.      */
  3074.  
  3075.     index.linePtr = TkBTreeFindLine(srcPtr->tree, lineNum);
  3076.     index.charIndex = 0;
  3077.     lowestPtr = NULL;
  3078.     do {
  3079.         dlPtr = LayoutDLine(textPtr, &index);
  3080.         dlPtr->nextPtr = lowestPtr;
  3081.         lowestPtr = dlPtr;
  3082.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  3083.         charsToCount -= dlPtr->count;
  3084.     } while ((charsToCount > 0) && (index.linePtr == dlPtr->index.linePtr));
  3085.  
  3086.     /*
  3087.      * Scan through the display lines to see if we've covered enough
  3088.      * vertical distance.  If so, save the starting index for the
  3089.      * line at the desired location.
  3090.      */
  3091.  
  3092.     for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  3093.         distance -= dlPtr->height;
  3094.         if (distance < 0) {
  3095.         *dstPtr = (noBestYet) ? dlPtr->index : bestIndex;
  3096.         break;
  3097.         }
  3098.         bestIndex = dlPtr->index;
  3099.         noBestYet = 0;
  3100.     }
  3101.  
  3102.     /*
  3103.      * Discard the display lines, then either return or prepare
  3104.      * for the next display line to lay out.
  3105.      */
  3106.  
  3107.     FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  3108.     if (distance < 0) {
  3109.         return;
  3110.     }
  3111.     charsToCount = INT_MAX;        /* Consider all chars. in next line. */
  3112.     }
  3113.  
  3114.     /*
  3115.      * Ran off the beginning of the text.  Return the first character
  3116.      * in the text.
  3117.      */
  3118.  
  3119.     TkTextMakeIndex(textPtr->tree, 0, 0, dstPtr);
  3120. }
  3121.  
  3122. /*
  3123.  *--------------------------------------------------------------
  3124.  *
  3125.  * TkTextSeeCmd --
  3126.  *
  3127.  *    This procedure is invoked to process the "see" option for
  3128.  *    the widget command for text widgets. See the user documentation
  3129.  *    for details on what it does.
  3130.  *
  3131.  * Results:
  3132.  *    A standard Tcl result.
  3133.  *
  3134.  * Side effects:
  3135.  *    See the user documentation.
  3136.  *
  3137.  *--------------------------------------------------------------
  3138.  */
  3139.  
  3140. int
  3141. TkTextSeeCmd(textPtr, interp, argc, argv)
  3142.     TkText *textPtr;        /* Information about text widget. */
  3143.     Tcl_Interp *interp;        /* Current interpreter. */
  3144.     int argc;            /* Number of arguments. */
  3145.     char **argv;        /* Argument strings.  Someone else has already
  3146.                  * parsed this command enough to know that
  3147.                  * argv[1] is "see". */
  3148. {
  3149.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3150.     TkTextIndex index;
  3151.     int x, y, width, height, lineWidth, charCount, oneThird, delta;
  3152.     DLine *dlPtr;
  3153.     TkTextDispChunk *chunkPtr;
  3154.  
  3155.     if (argc != 3) {
  3156.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  3157.         argv[0], " see index\"", (char *) NULL);
  3158.     return TCL_ERROR;
  3159.     }
  3160.     if (TkTextGetIndex(interp, textPtr, argv[2], &index) != TCL_OK) {
  3161.     return TCL_ERROR;
  3162.     }
  3163.  
  3164.     /*
  3165.      * If the specified position is the extra line at the end of the
  3166.      * text, round it back to the last real line.
  3167.      */
  3168.  
  3169.     if (TkBTreeLineIndex(index.linePtr) == TkBTreeNumLines(index.tree)) {
  3170.     TkTextIndexBackChars(&index, 1, &index);
  3171.     }
  3172.  
  3173.     /*
  3174.      * First get the desired position into the vertical range of the window.
  3175.      */
  3176.  
  3177.     TkTextSetYView(textPtr, &index, 1);
  3178.  
  3179.     /*
  3180.      * Now make sure that the character is in view horizontally.
  3181.      */
  3182.  
  3183.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3184.     UpdateDisplayInfo(textPtr);
  3185.     }
  3186.     lineWidth = dInfoPtr->maxX - dInfoPtr->x;
  3187.     if (dInfoPtr->maxLength < lineWidth) {
  3188.     return TCL_OK;
  3189.     }
  3190.  
  3191.     /*
  3192.      * Find the chunk that contains the desired index.
  3193.      */
  3194.  
  3195.     dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
  3196.     charCount = index.charIndex - dlPtr->index.charIndex;
  3197.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  3198.     if (charCount < chunkPtr->numChars) {
  3199.         break;
  3200.     }
  3201.     charCount -= chunkPtr->numChars;
  3202.     }
  3203.  
  3204.     /*
  3205.      * Call a chunk-specific procedure to find the horizontal range of
  3206.      * the character within the chunk.
  3207.      */
  3208.  
  3209.     (*chunkPtr->bboxProc)(chunkPtr, charCount, dlPtr->y + dlPtr->spaceAbove,
  3210.         dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  3211.         dlPtr->baseline - dlPtr->spaceAbove, &x, &y, &width,
  3212.         &height);
  3213.     delta = x - dInfoPtr->curPixelOffset;
  3214.     oneThird = lineWidth/3;
  3215.     if (delta < 0) {
  3216.     if (delta < -oneThird) {
  3217.         dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
  3218.     } else {
  3219.         dInfoPtr->newCharOffset -= ((-delta) + textPtr->charWidth - 1)
  3220.         / textPtr->charWidth;
  3221.     }
  3222.     } else {
  3223.     delta -= (lineWidth - width);
  3224.     if (delta > 0) {
  3225.         if (delta > oneThird) {
  3226.         dInfoPtr->newCharOffset = (x - lineWidth/2)/textPtr->charWidth;
  3227.         } else {
  3228.         dInfoPtr->newCharOffset += (delta + textPtr->charWidth - 1)
  3229.             / textPtr->charWidth;
  3230.         }
  3231.     } else {
  3232.         return TCL_OK;
  3233.     }
  3234.     }
  3235.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3236.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3237.     dInfoPtr->flags |= REDRAW_PENDING;
  3238.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3239.     }
  3240.     return TCL_OK;
  3241. }
  3242.  
  3243. /*
  3244.  *--------------------------------------------------------------
  3245.  *
  3246.  * TkTextXviewCmd --
  3247.  *
  3248.  *    This procedure is invoked to process the "xview" option for
  3249.  *    the widget command for text widgets. See the user documentation
  3250.  *    for details on what it does.
  3251.  *
  3252.  * Results:
  3253.  *    A standard Tcl result.
  3254.  *
  3255.  * Side effects:
  3256.  *    See the user documentation.
  3257.  *
  3258.  *--------------------------------------------------------------
  3259.  */
  3260.  
  3261. int
  3262. TkTextXviewCmd(textPtr, interp, argc, argv)
  3263.     TkText *textPtr;        /* Information about text widget. */
  3264.     Tcl_Interp *interp;        /* Current interpreter. */
  3265.     int argc;            /* Number of arguments. */
  3266.     char **argv;        /* Argument strings.  Someone else has already
  3267.                  * parsed this command enough to know that
  3268.                  * argv[1] is "xview". */
  3269. {
  3270.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3271.     int type, charsPerPage, count, newOffset;
  3272.     double fraction;
  3273.  
  3274.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3275.     UpdateDisplayInfo(textPtr);
  3276.     }
  3277.  
  3278.     if (argc == 2) {
  3279.     GetXView(interp, textPtr, 0);
  3280.     return TCL_OK;
  3281.     }
  3282.  
  3283.     newOffset = dInfoPtr->newCharOffset;
  3284.     type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  3285.     switch (type) {
  3286.     case TK_SCROLL_ERROR:
  3287.         return TCL_ERROR;
  3288.     case TK_SCROLL_MOVETO:
  3289.         if (fraction > 1.0) {
  3290.         fraction = 1.0;
  3291.         }
  3292.         if (fraction < 0) {
  3293.         fraction = 0;
  3294.         }
  3295.         newOffset = (int) (((fraction * dInfoPtr->maxLength) / textPtr->charWidth)
  3296.             + 0.5);
  3297.         break;
  3298.     case TK_SCROLL_PAGES:
  3299.         charsPerPage = ((dInfoPtr->maxX - dInfoPtr->x) / textPtr->charWidth)
  3300.             - 2;
  3301.         if (charsPerPage < 1) {
  3302.         charsPerPage = 1;
  3303.         }
  3304.         newOffset += charsPerPage*count;
  3305.         break;
  3306.     case TK_SCROLL_UNITS:
  3307.         newOffset += count;
  3308.         break;
  3309.     }
  3310.  
  3311.     dInfoPtr->newCharOffset = newOffset;
  3312.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3313.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3314.     dInfoPtr->flags |= REDRAW_PENDING;
  3315.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3316.     }
  3317.     return TCL_OK;
  3318. }
  3319.  
  3320. /*
  3321.  *----------------------------------------------------------------------
  3322.  *
  3323.  * ScrollByLines --
  3324.  *
  3325.  *    This procedure is called to scroll a text widget up or down
  3326.  *    by a given number of lines.
  3327.  *
  3328.  * Results:
  3329.  *    None.
  3330.  *
  3331.  * Side effects:
  3332.  *    The view in textPtr's window changes to reflect the value
  3333.  *    of "offset".
  3334.  *
  3335.  *----------------------------------------------------------------------
  3336.  */
  3337.  
  3338. static void
  3339. ScrollByLines(textPtr, offset)
  3340.     TkText *textPtr;        /* Widget to scroll. */
  3341.     int offset;            /* Amount by which to scroll, in *screen*
  3342.                  * lines.  Positive means that information
  3343.                  * later in text becomes visible, negative
  3344.                  * means that information earlier in the
  3345.                  * text becomes visible. */
  3346. {
  3347.     int i, charsToCount, lineNum;
  3348.     TkTextIndex new, index;
  3349.     TkTextLine *lastLinePtr;
  3350.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3351.     DLine *dlPtr, *lowestPtr;
  3352.  
  3353.     if (offset < 0) {
  3354.     /*
  3355.      * Must scroll up (to show earlier information in the text).
  3356.      * The code below is similar to that in MeasureUp, except that
  3357.      * it counts lines instead of pixels.
  3358.      */
  3359.  
  3360.     charsToCount = textPtr->topIndex.charIndex + 1;
  3361.     index.tree = textPtr->tree;
  3362.     offset--;            /* Skip line containing topIndex. */
  3363.     for (lineNum = TkBTreeLineIndex(textPtr->topIndex.linePtr);
  3364.         lineNum >= 0; lineNum--) {
  3365.         index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
  3366.         index.charIndex = 0;
  3367.         lowestPtr = NULL;
  3368.         do {
  3369.         dlPtr = LayoutDLine(textPtr, &index);
  3370.         dlPtr->nextPtr = lowestPtr;
  3371.         lowestPtr = dlPtr;
  3372.         TkTextIndexForwChars(&index, dlPtr->count, &index);
  3373.         charsToCount -= dlPtr->count;
  3374.         } while ((charsToCount > 0)
  3375.             && (index.linePtr == dlPtr->index.linePtr));
  3376.  
  3377.         for (dlPtr = lowestPtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  3378.         offset++;
  3379.         if (offset == 0) {
  3380.             textPtr->topIndex = dlPtr->index;
  3381.             break;
  3382.         }
  3383.         }
  3384.     
  3385.         /*
  3386.          * Discard the display lines, then either return or prepare
  3387.          * for the next display line to lay out.
  3388.          */
  3389.     
  3390.         FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  3391.         if (offset >= 0) {
  3392.         goto scheduleUpdate;
  3393.         }
  3394.         charsToCount = INT_MAX;
  3395.     }
  3396.     
  3397.     /*
  3398.      * Ran off the beginning of the text.  Return the first character
  3399.      * in the text.
  3400.      */
  3401.  
  3402.     TkTextMakeIndex(textPtr->tree, 0, 0, &textPtr->topIndex);
  3403.     } else {
  3404.     /*
  3405.      * Scrolling down, to show later information in the text.
  3406.      * Just count lines from the current top of the window.
  3407.      */
  3408.  
  3409.     lastLinePtr = TkBTreeFindLine(textPtr->tree,
  3410.         TkBTreeNumLines(textPtr->tree));
  3411.     for (i = 0; i < offset; i++) {
  3412.         dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
  3413.         dlPtr->nextPtr = NULL;
  3414.         TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count, &new);
  3415.         FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  3416.         if (new.linePtr == lastLinePtr) {
  3417.         break;
  3418.         }
  3419.         textPtr->topIndex = new;
  3420.     }
  3421.     }
  3422.  
  3423.     scheduleUpdate:
  3424.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3425.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3426.     }
  3427.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  3428. }
  3429.  
  3430. /*
  3431.  *--------------------------------------------------------------
  3432.  *
  3433.  * TkTextYviewCmd --
  3434.  *
  3435.  *    This procedure is invoked to process the "yview" option for
  3436.  *    the widget command for text widgets. See the user documentation
  3437.  *    for details on what it does.
  3438.  *
  3439.  * Results:
  3440.  *    A standard Tcl result.
  3441.  *
  3442.  * Side effects:
  3443.  *    See the user documentation.
  3444.  *
  3445.  *--------------------------------------------------------------
  3446.  */
  3447.  
  3448. int
  3449. TkTextYviewCmd(textPtr, interp, argc, argv)
  3450.     TkText *textPtr;        /* Information about text widget. */
  3451.     Tcl_Interp *interp;        /* Current interpreter. */
  3452.     int argc;            /* Number of arguments. */
  3453.     char **argv;        /* Argument strings.  Someone else has already
  3454.                  * parsed this command enough to know that
  3455.                  * argv[1] is "yview". */
  3456. {
  3457.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3458.     int pickPlace, lineNum, type, charsInLine;
  3459.     Tk_FontMetrics fm;
  3460.     int pixels, count;
  3461.     size_t switchLength;
  3462.     double fraction;
  3463.     TkTextIndex index, new;
  3464.     TkTextLine *lastLinePtr;
  3465.     DLine *dlPtr;
  3466.  
  3467.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3468.     UpdateDisplayInfo(textPtr);
  3469.     }
  3470.  
  3471.     if (argc == 2) {
  3472.     GetYView(interp, textPtr, 0);
  3473.     return TCL_OK;
  3474.     }
  3475.  
  3476.     /*
  3477.      * Next, handle the old syntax: "pathName yview ?-pickplace? where"
  3478.      */
  3479.  
  3480.     pickPlace = 0;
  3481.     if (argv[2][0] == '-') {
  3482.     switchLength = strlen(argv[2]);
  3483.     if ((switchLength >= 2)
  3484.         && (strncmp(argv[2], "-pickplace", switchLength) == 0)) {
  3485.         pickPlace = 1;
  3486.         if (argc != 4) {
  3487.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  3488.             argv[0], " yview -pickplace lineNum|index\"",
  3489.             (char *) NULL);
  3490.         return TCL_ERROR;
  3491.         }
  3492.     }
  3493.     }
  3494.     if ((argc == 3) || pickPlace) {
  3495.     if (Tcl_GetInt(interp, argv[2+pickPlace], &lineNum) == TCL_OK) {
  3496.         TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
  3497.         TkTextSetYView(textPtr, &index, 0);
  3498.         return TCL_OK;
  3499.     }
  3500.     
  3501.     /*
  3502.      * The argument must be a regular text index.
  3503.      */
  3504.     
  3505.     Tcl_ResetResult(interp);
  3506.     if (TkTextGetIndex(interp, textPtr, argv[2+pickPlace],
  3507.         &index) != TCL_OK) {
  3508.         return TCL_ERROR;
  3509.     }
  3510.     TkTextSetYView(textPtr, &index, pickPlace);
  3511.     return TCL_OK;
  3512.     }
  3513.  
  3514.     /*
  3515.      * New syntax: dispatch based on argv[2].
  3516.      */
  3517.  
  3518.     type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  3519.     switch (type) {
  3520.     case TK_SCROLL_ERROR:
  3521.         return TCL_ERROR;
  3522.     case TK_SCROLL_MOVETO:
  3523.         if (fraction > 1.0) {
  3524.         fraction = 1.0;
  3525.         }
  3526.         if (fraction < 0) {
  3527.         fraction = 0;
  3528.         }
  3529.         fraction *= TkBTreeNumLines(textPtr->tree);
  3530.         lineNum = (int) fraction;
  3531.         TkTextMakeIndex(textPtr->tree, lineNum, 0, &index);
  3532.         charsInLine = TkBTreeCharsInLine(index.linePtr);
  3533.         index.charIndex = (int)((charsInLine * (fraction-lineNum)) + 0.5);
  3534.         if (index.charIndex >= charsInLine) {
  3535.         TkTextMakeIndex(textPtr->tree, lineNum+1, 0, &index);
  3536.         }
  3537.         TkTextSetYView(textPtr, &index, 0);
  3538.         break;
  3539.     case TK_SCROLL_PAGES:
  3540.         /*
  3541.          * Scroll up or down by screenfuls.  Actually, use the
  3542.          * window height minus two lines, so that there's some
  3543.          * overlap between adjacent pages.
  3544.          */
  3545.  
  3546.         Tk_GetFontMetrics(textPtr->tkfont, &fm);
  3547.         if (count < 0) {
  3548.         pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*(-count)
  3549.             + fm.linespace;
  3550.         MeasureUp(textPtr, &textPtr->topIndex, pixels, &new);
  3551.         if (TkTextIndexCmp(&textPtr->topIndex, &new) == 0) {
  3552.             /*
  3553.              * A page of scrolling ended up being less than one line.
  3554.              * Scroll one line anyway.
  3555.              */
  3556.  
  3557.             count = -1;
  3558.             goto scrollByLines;
  3559.         }
  3560.         textPtr->topIndex = new;
  3561.         } else {
  3562.         /*
  3563.          * Scrolling down by pages.  Layout lines starting at the
  3564.          * top index and count through the desired vertical distance.
  3565.          */
  3566.  
  3567.         pixels = (dInfoPtr->maxY - 2*fm.linespace - dInfoPtr->y)*count;
  3568.         lastLinePtr = TkBTreeFindLine(textPtr->tree,
  3569.             TkBTreeNumLines(textPtr->tree));
  3570.         do {
  3571.             dlPtr = LayoutDLine(textPtr, &textPtr->topIndex);
  3572.             dlPtr->nextPtr = NULL;
  3573.             TkTextIndexForwChars(&textPtr->topIndex, dlPtr->count,
  3574.                 &new);
  3575.             pixels -= dlPtr->height;
  3576.             FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  3577.             if (new.linePtr == lastLinePtr) {
  3578.             break;
  3579.             }
  3580.             textPtr->topIndex = new;
  3581.         } while (pixels > 0);
  3582.         }
  3583.         if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3584.         Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3585.         }
  3586.         dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  3587.         break;
  3588.     case TK_SCROLL_UNITS:
  3589.         scrollByLines:
  3590.         ScrollByLines(textPtr, count);
  3591.         break;
  3592.     }
  3593.     return TCL_OK;
  3594. }
  3595.  
  3596. /*
  3597.  *--------------------------------------------------------------
  3598.  *
  3599.  * TkTextScanCmd --
  3600.  *
  3601.  *    This procedure is invoked to process the "scan" option for
  3602.  *    the widget command for text widgets. See the user documentation
  3603.  *    for details on what it does.
  3604.  *
  3605.  * Results:
  3606.  *    A standard Tcl result.
  3607.  *
  3608.  * Side effects:
  3609.  *    See the user documentation.
  3610.  *
  3611.  *--------------------------------------------------------------
  3612.  */
  3613.  
  3614. int
  3615. TkTextScanCmd(textPtr, interp, argc, argv)
  3616.     register TkText *textPtr;    /* Information about text widget. */
  3617.     Tcl_Interp *interp;        /* Current interpreter. */
  3618.     int argc;            /* Number of arguments. */
  3619.     char **argv;        /* Argument strings.  Someone else has already
  3620.                  * parsed this command enough to know that
  3621.                  * argv[1] is "scan". */
  3622. {
  3623.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3624.     TkTextIndex index;
  3625.     int c, x, y, totalScroll, newChar, maxChar;
  3626.     Tk_FontMetrics fm;
  3627.     size_t length;
  3628.  
  3629.     if (argc != 5) {
  3630.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  3631.         argv[0], " scan mark|dragto x y\"", (char *) NULL);
  3632.     return TCL_ERROR;
  3633.     }
  3634.     if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
  3635.     return TCL_ERROR;
  3636.     }
  3637.     if (Tcl_GetInt(interp, argv[4], &y) != TCL_OK) {
  3638.     return TCL_ERROR;
  3639.     }
  3640.     c = argv[2][0];
  3641.     length = strlen(argv[2]);
  3642.     if ((c == 'd') && (strncmp(argv[2], "dragto", length) == 0)) {
  3643.     /*
  3644.      * Amplify the difference between the current position and the
  3645.      * mark position to compute how much the view should shift, then
  3646.      * update the mark position to correspond to the new view.  If we
  3647.      * run off the edge of the text, reset the mark point so that the
  3648.      * current position continues to correspond to the edge of the
  3649.      * window.  This means that the picture will start dragging as
  3650.      * soon as the mouse reverses direction (without this reset, might
  3651.      * have to slide mouse a long ways back before the picture starts
  3652.      * moving again).
  3653.      */
  3654.  
  3655.     newChar = dInfoPtr->scanMarkChar + (10*(dInfoPtr->scanMarkX - x))
  3656.         / (textPtr->charWidth);
  3657.     maxChar = 1 + (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
  3658.         + textPtr->charWidth - 1)/textPtr->charWidth;
  3659.     if (newChar < 0) {
  3660.         dInfoPtr->scanMarkChar = newChar = 0;
  3661.         dInfoPtr->scanMarkX = x;
  3662.     } else if (newChar > maxChar) {
  3663.         dInfoPtr->scanMarkChar = newChar = maxChar;
  3664.         dInfoPtr->scanMarkX = x;
  3665.     }
  3666.     dInfoPtr->newCharOffset = newChar;
  3667.  
  3668.     Tk_GetFontMetrics(textPtr->tkfont, &fm);
  3669.     totalScroll = (10*(dInfoPtr->scanMarkY - y)) / fm.linespace;
  3670.     if (totalScroll != dInfoPtr->scanTotalScroll) {
  3671.         index = textPtr->topIndex;
  3672.         ScrollByLines(textPtr, totalScroll-dInfoPtr->scanTotalScroll);
  3673.         dInfoPtr->scanTotalScroll = totalScroll;
  3674.         if ((index.linePtr == textPtr->topIndex.linePtr) &&
  3675.             (index.charIndex == textPtr->topIndex.charIndex)) {
  3676.         dInfoPtr->scanTotalScroll = 0;
  3677.         dInfoPtr->scanMarkY = y;
  3678.         }
  3679.     }
  3680.     } else if ((c == 'm') && (strncmp(argv[2], "mark", length) == 0)) {
  3681.     dInfoPtr->scanMarkChar = dInfoPtr->newCharOffset;
  3682.     dInfoPtr->scanMarkX = x;
  3683.     dInfoPtr->scanTotalScroll = 0;
  3684.     dInfoPtr->scanMarkY = y;
  3685.     } else {
  3686.     Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  3687.         "\": must be mark or dragto", (char *) NULL);
  3688.     return TCL_ERROR;
  3689.     }
  3690.     dInfoPtr->flags |= DINFO_OUT_OF_DATE;
  3691.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  3692.     dInfoPtr->flags |= REDRAW_PENDING;
  3693.     Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  3694.     }
  3695.     return TCL_OK;
  3696. }
  3697.  
  3698. /*
  3699.  *----------------------------------------------------------------------
  3700.  *
  3701.  * GetXView --
  3702.  *
  3703.  *    This procedure computes the fractions that indicate what's
  3704.  *    visible in a text window and, optionally, evaluates a
  3705.  *    Tcl script to report them to the text's associated scrollbar.
  3706.  *
  3707.  * Results:
  3708.  *    If report is zero, then interp->result is filled in with
  3709.  *    two real numbers separated by a space, giving the position of
  3710.  *    the left and right edges of the window as fractions from 0 to
  3711.  *    1, where 0 means the left edge of the text and 1 means the right
  3712.  *    edge.  If report is non-zero, then interp->result isn't modified
  3713.  *    directly, but instead a script is evaluated in interp to report
  3714.  *    the new horizontal scroll position to the scrollbar (if the scroll
  3715.  *    position hasn't changed then no script is invoked).
  3716.  *
  3717.  * Side effects:
  3718.  *    None.
  3719.  *
  3720.  *----------------------------------------------------------------------
  3721.  */
  3722.  
  3723. static void
  3724. GetXView(interp, textPtr, report)
  3725.     Tcl_Interp *interp;            /* If "report" is FALSE, string
  3726.                      * describing visible range gets
  3727.                      * stored in interp->result. */
  3728.     TkText *textPtr;            /* Information about text widget. */
  3729.     int report;                /* Non-zero means report info to
  3730.                      * scrollbar if it has changed. */
  3731. {
  3732.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3733.     char buffer[200];
  3734.     double first, last;
  3735.     int code;
  3736.  
  3737.     if (dInfoPtr->maxLength > 0) {
  3738.     first = ((double) dInfoPtr->curPixelOffset)
  3739.         / dInfoPtr->maxLength;
  3740.     last = first + ((double) (dInfoPtr->maxX - dInfoPtr->x))
  3741.         / dInfoPtr->maxLength;
  3742.     if (last > 1.0) {
  3743.         last = 1.0;
  3744.     }
  3745.     } else {
  3746.     first = 0;
  3747.     last = 1.0;
  3748.     }
  3749.     if (!report) {
  3750.     sprintf(interp->result, "%g %g", first, last);
  3751.     return;
  3752.     }
  3753.     if ((first == dInfoPtr->xScrollFirst) && (last == dInfoPtr->xScrollLast)) {
  3754.     return;
  3755.     }
  3756.     dInfoPtr->xScrollFirst = first;
  3757.     dInfoPtr->xScrollLast = last;
  3758.     sprintf(buffer, " %g %g", first, last);
  3759.     code = Tcl_VarEval(interp, textPtr->xScrollCmd,
  3760.         buffer, (char *) NULL);
  3761.     if (code != TCL_OK) {
  3762.     Tcl_AddErrorInfo(interp,
  3763.         "\n    (horizontal scrolling command executed by text)");
  3764.     Tcl_BackgroundError(interp);
  3765.     }
  3766. }
  3767.  
  3768. /*
  3769.  *----------------------------------------------------------------------
  3770.  *
  3771.  * GetYView --
  3772.  *
  3773.  *    This procedure computes the fractions that indicate what's
  3774.  *    visible in a text window and, optionally, evaluates a
  3775.  *    Tcl script to report them to the text's associated scrollbar.
  3776.  *
  3777.  * Results:
  3778.  *    If report is zero, then interp->result is filled in with
  3779.  *    two real numbers separated by a space, giving the position of
  3780.  *    the top and bottom of the window as fractions from 0 to 1, where
  3781.  *    0 means the beginning of the text and 1 means the end.  If
  3782.  *    report is non-zero, then interp->result isn't modified directly,
  3783.  *    but a script is evaluated in interp to report the new scroll
  3784.  *    position to the scrollbar (if the scroll position hasn't changed
  3785.  *    then no script is invoked).
  3786.  *
  3787.  * Side effects:
  3788.  *    None.
  3789.  *
  3790.  *----------------------------------------------------------------------
  3791.  */
  3792.  
  3793. static void
  3794. GetYView(interp, textPtr, report)
  3795.     Tcl_Interp *interp;            /* If "report" is FALSE, string
  3796.                      * describing visible range gets
  3797.                      * stored in interp->result. */
  3798.     TkText *textPtr;            /* Information about text widget. */
  3799.     int report;                /* Non-zero means report info to
  3800.                      * scrollbar if it has changed. */
  3801. {
  3802.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3803.     char buffer[200];
  3804.     double first, last;
  3805.     DLine *dlPtr;
  3806.     int totalLines, code, count;
  3807.  
  3808.     dlPtr = dInfoPtr->dLinePtr;
  3809.     totalLines = TkBTreeNumLines(textPtr->tree);
  3810.     first = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
  3811.         + ((double) dlPtr->index.charIndex)
  3812.         / (TkBTreeCharsInLine(dlPtr->index.linePtr));
  3813.     first /= totalLines;
  3814.     while (1) {
  3815.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  3816.         /*
  3817.          * The last line is only partially visible, so don't
  3818.          * count its characters in what's visible.
  3819.          */
  3820.         count = 0;
  3821.         break;
  3822.     }
  3823.     if (dlPtr->nextPtr == NULL) {
  3824.         count = dlPtr->count;
  3825.         break;
  3826.     }
  3827.     dlPtr = dlPtr->nextPtr;
  3828.     }
  3829.     last = ((double) TkBTreeLineIndex(dlPtr->index.linePtr))
  3830.         + ((double) (dlPtr->index.charIndex + count))
  3831.         / (TkBTreeCharsInLine(dlPtr->index.linePtr));
  3832.     last /= totalLines;
  3833.     if (!report) {
  3834.     sprintf(interp->result, "%g %g", first, last);
  3835.     return;
  3836.     }
  3837.     if ((first == dInfoPtr->yScrollFirst) && (last == dInfoPtr->yScrollLast)) {
  3838.     return;
  3839.     }
  3840.     dInfoPtr->yScrollFirst = first;
  3841.     dInfoPtr->yScrollLast = last;
  3842.     sprintf(buffer, " %g %g", first, last);
  3843.     code = Tcl_VarEval(interp, textPtr->yScrollCmd,
  3844.         buffer, (char *) NULL);
  3845.     if (code != TCL_OK) {
  3846.     Tcl_AddErrorInfo(interp,
  3847.         "\n    (vertical scrolling command executed by text)");
  3848.     Tcl_BackgroundError(interp);
  3849.     }
  3850. }
  3851.  
  3852. /*
  3853.  *----------------------------------------------------------------------
  3854.  *
  3855.  * FindDLine --
  3856.  *
  3857.  *    This procedure is called to find the DLine corresponding to a
  3858.  *    given text index.
  3859.  *
  3860.  * Results:
  3861.  *    The return value is a pointer to the first DLine found in the
  3862.  *    list headed by dlPtr that displays information at or after the
  3863.  *    specified position.  If there is no such line in the list then
  3864.  *    NULL is returned.
  3865.  *
  3866.  * Side effects:
  3867.  *    None.
  3868.  *
  3869.  *----------------------------------------------------------------------
  3870.  */
  3871.  
  3872. static DLine *
  3873. FindDLine(dlPtr, indexPtr)
  3874.     register DLine *dlPtr;    /* Pointer to first in list of DLines
  3875.                  * to search. */
  3876.     TkTextIndex *indexPtr;    /* Index of desired character. */
  3877. {
  3878.     TkTextLine *linePtr;
  3879.  
  3880.     if (dlPtr == NULL) {
  3881.     return NULL;
  3882.     }
  3883.     if (TkBTreeLineIndex(indexPtr->linePtr)
  3884.         < TkBTreeLineIndex(dlPtr->index.linePtr)) {
  3885.     /*
  3886.      * The first display line is already past the desired line.
  3887.      */
  3888.     return dlPtr;
  3889.     }
  3890.  
  3891.     /*
  3892.      * Find the first display line that covers the desired text line.
  3893.      */
  3894.  
  3895.     linePtr = dlPtr->index.linePtr;
  3896.     while (linePtr != indexPtr->linePtr) {
  3897.     while (dlPtr->index.linePtr == linePtr) {
  3898.         dlPtr = dlPtr->nextPtr;
  3899.         if (dlPtr == NULL) {
  3900.         return NULL;
  3901.         }
  3902.     }
  3903.     linePtr = TkBTreeNextLine(linePtr);
  3904.     if (linePtr == NULL) {
  3905.         panic("FindDLine reached end of text");
  3906.     }
  3907.     }
  3908.     if (indexPtr->linePtr != dlPtr->index.linePtr) {
  3909.     return dlPtr;
  3910.     }
  3911.  
  3912.     /*
  3913.      * Now get to the right position within the text line.
  3914.      */
  3915.  
  3916.     while (indexPtr->charIndex >= (dlPtr->index.charIndex + dlPtr->count)) {
  3917.     dlPtr = dlPtr->nextPtr;
  3918.     if ((dlPtr == NULL) || (dlPtr->index.linePtr != indexPtr->linePtr)) {
  3919.         break;
  3920.     }
  3921.     }
  3922.     return dlPtr;
  3923. }
  3924.  
  3925. /*
  3926.  *----------------------------------------------------------------------
  3927.  *
  3928.  * TkTextPixelIndex --
  3929.  *
  3930.  *    Given an (x,y) coordinate on the screen, find the location of
  3931.  *    the character closest to that location.
  3932.  *
  3933.  * Results:
  3934.  *    The index at *indexPtr is modified to refer to the character
  3935.  *    on the display that is closest to (x,y).
  3936.  *
  3937.  * Side effects:
  3938.  *    None.
  3939.  *
  3940.  *----------------------------------------------------------------------
  3941.  */
  3942.  
  3943. void
  3944. TkTextPixelIndex(textPtr, x, y, indexPtr)
  3945.     TkText *textPtr;        /* Widget record for text widget. */
  3946.     int x, y;            /* Pixel coordinates of point in widget's
  3947.                  * window. */
  3948.     TkTextIndex *indexPtr;    /* This index gets filled in with the
  3949.                  * index of the character nearest to (x,y). */
  3950. {
  3951.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  3952.     register DLine *dlPtr;
  3953.     register TkTextDispChunk *chunkPtr;
  3954.  
  3955.     /*
  3956.      * Make sure that all of the layout information about what's
  3957.      * displayed where on the screen is up-to-date.
  3958.      */
  3959.  
  3960.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  3961.     UpdateDisplayInfo(textPtr);
  3962.     }
  3963.  
  3964.     /*
  3965.      * If the coordinates are above the top of the window, then adjust
  3966.      * them to refer to the upper-right corner of the window.  If they're
  3967.      * off to one side or the other, then adjust to the closest side.
  3968.      */
  3969.  
  3970.     if (y < dInfoPtr->y) {
  3971.     y = dInfoPtr->y;
  3972.     x = dInfoPtr->x;
  3973.     }
  3974.     if (x >= dInfoPtr->maxX) {
  3975.     x = dInfoPtr->maxX - 1;
  3976.     }
  3977.     if (x < dInfoPtr->x) {
  3978.     x = dInfoPtr->x;
  3979.     }
  3980.  
  3981.     /*
  3982.      * Find the display line containing the desired y-coordinate.
  3983.      */
  3984.  
  3985.     for (dlPtr = dInfoPtr->dLinePtr; y >= (dlPtr->y + dlPtr->height);
  3986.         dlPtr = dlPtr->nextPtr) {
  3987.     if (dlPtr->nextPtr == NULL) {
  3988.         /*
  3989.          * Y-coordinate is off the bottom of the displayed text.
  3990.          * Use the last character on the last line.
  3991.          */
  3992.  
  3993.         x = dInfoPtr->maxX - 1;
  3994.         break;
  3995.     }
  3996.     }
  3997.  
  3998.     /*
  3999.      * Scan through the line's chunks to find the one that contains
  4000.      * the desired x-coordinate.  Before doing this, translate the
  4001.      * x-coordinate from the coordinate system of the window to the
  4002.      * coordinate system of the line (to take account of x-scrolling).
  4003.      */
  4004.  
  4005.     *indexPtr = dlPtr->index;
  4006.     x = x - dInfoPtr->x + dInfoPtr->curPixelOffset;
  4007.     for (chunkPtr = dlPtr->chunkPtr; x >= (chunkPtr->x + chunkPtr->width);
  4008.         indexPtr->charIndex += chunkPtr->numChars,
  4009.         chunkPtr = chunkPtr->nextPtr) {
  4010.     if (chunkPtr->nextPtr == NULL) {
  4011.         indexPtr->charIndex += chunkPtr->numChars - 1;
  4012.         return;
  4013.     }
  4014.     }
  4015.  
  4016.     /*
  4017.      * If the chunk has more than one character in it, ask it which
  4018.      * character is at the desired location.
  4019.      */
  4020.  
  4021.     if (chunkPtr->numChars > 1) {
  4022.     indexPtr->charIndex += (*chunkPtr->measureProc)(chunkPtr, x);
  4023.     }
  4024. }
  4025.  
  4026. /*
  4027.  *----------------------------------------------------------------------
  4028.  *
  4029.  * TkTextCharBbox --
  4030.  *
  4031.  *    Given an index, find the bounding box of the screen area
  4032.  *    occupied by that character.
  4033.  *
  4034.  * Results:
  4035.  *    Zero is returned if the character is on the screen.  -1
  4036.  *    means the character isn't on the screen.  If the return value
  4037.  *    is 0, then the bounding box of the part of the character that's
  4038.  *    visible on the screen is returned to *xPtr, *yPtr, *widthPtr,
  4039.  *    and *heightPtr.
  4040.  *
  4041.  * Side effects:
  4042.  *    None.
  4043.  *
  4044.  *----------------------------------------------------------------------
  4045.  */
  4046.  
  4047. int
  4048. TkTextCharBbox(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr)
  4049.     TkText *textPtr;        /* Widget record for text widget. */
  4050.     TkTextIndex *indexPtr;    /* Index of character whose bounding
  4051.                  * box is desired. */
  4052.     int *xPtr, *yPtr;        /* Filled with character's upper-left
  4053.                  * coordinate. */
  4054.     int *widthPtr, *heightPtr;    /* Filled in with character's dimensions. */
  4055. {
  4056.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  4057.     DLine *dlPtr;
  4058.     register TkTextDispChunk *chunkPtr;
  4059.     int index;
  4060.  
  4061.     /*
  4062.      * Make sure that all of the screen layout information is up to date.
  4063.      */
  4064.  
  4065.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  4066.     UpdateDisplayInfo(textPtr);
  4067.     }
  4068.  
  4069.     /*
  4070.      * Find the display line containing the desired index.
  4071.      */
  4072.  
  4073.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  4074.     if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
  4075.     return -1;
  4076.     }
  4077.  
  4078.     /*
  4079.      * Find the chunk within the line that contains the desired
  4080.      * index.
  4081.      */
  4082.  
  4083.     index = indexPtr->charIndex - dlPtr->index.charIndex;
  4084.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  4085.     if (chunkPtr == NULL) {
  4086.         return -1;
  4087.     }
  4088.     if (index < chunkPtr->numChars) {
  4089.         break;
  4090.     }
  4091.     index -= chunkPtr->numChars;
  4092.     }
  4093.  
  4094.     /*
  4095.      * Call a chunk-specific procedure to find the horizontal range of
  4096.      * the character within the chunk, then fill in the vertical range.
  4097.      * The x-coordinate returned by bboxProc is a coordinate within a
  4098.      * line, not a coordinate on the screen.  Translate it to reflect
  4099.      * horizontal scrolling.
  4100.      */
  4101.  
  4102.     (*chunkPtr->bboxProc)(chunkPtr, index, dlPtr->y + dlPtr->spaceAbove,
  4103.         dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  4104.         dlPtr->baseline - dlPtr->spaceAbove, xPtr, yPtr, widthPtr,
  4105.         heightPtr);
  4106.     *xPtr = *xPtr + dInfoPtr->x - dInfoPtr->curPixelOffset;
  4107.     if ((index == (chunkPtr->numChars-1)) && (chunkPtr->nextPtr == NULL)) {
  4108.     /*
  4109.      * Last character in display line.  Give it all the space up to
  4110.      * the line.
  4111.      */
  4112.  
  4113.     if (*xPtr > dInfoPtr->maxX) {
  4114.         *xPtr = dInfoPtr->maxX;
  4115.     }
  4116.     *widthPtr = dInfoPtr->maxX - *xPtr;
  4117.     }
  4118.     if ((*xPtr + *widthPtr) <= dInfoPtr->x) {
  4119.     return -1;
  4120.     }
  4121.     if ((*xPtr + *widthPtr) > dInfoPtr->maxX) {
  4122.     *widthPtr = dInfoPtr->maxX - *xPtr;
  4123.     if (*widthPtr <= 0) {
  4124.         return -1;
  4125.     }
  4126.     }
  4127.     if ((*yPtr + *heightPtr) > dInfoPtr->maxY) {
  4128.     *heightPtr = dInfoPtr->maxY - *yPtr;
  4129.     if (*heightPtr <= 0) {
  4130.         return -1;
  4131.     }
  4132.     }
  4133.     return 0;
  4134. }
  4135.  
  4136. /*
  4137.  *----------------------------------------------------------------------
  4138.  *
  4139.  * TkTextDLineInfo --
  4140.  *
  4141.  *    Given an index, return information about the display line
  4142.  *    containing that character.
  4143.  *
  4144.  * Results:
  4145.  *    Zero is returned if the character is on the screen.  -1
  4146.  *    means the character isn't on the screen.  If the return value
  4147.  *    is 0, then information is returned in the variables pointed
  4148.  *    to by xPtr, yPtr, widthPtr, heightPtr, and basePtr.
  4149.  *
  4150.  * Side effects:
  4151.  *    None.
  4152.  *
  4153.  *----------------------------------------------------------------------
  4154.  */
  4155.  
  4156. int
  4157. TkTextDLineInfo(textPtr, indexPtr, xPtr, yPtr, widthPtr, heightPtr, basePtr)
  4158.     TkText *textPtr;        /* Widget record for text widget. */
  4159.     TkTextIndex *indexPtr;    /* Index of character whose bounding
  4160.                  * box is desired. */
  4161.     int *xPtr, *yPtr;        /* Filled with line's upper-left
  4162.                  * coordinate. */
  4163.     int *widthPtr, *heightPtr;    /* Filled in with line's dimensions. */
  4164.     int *basePtr;        /* Filled in with the baseline position,
  4165.                  * measured as an offset down from *yPtr. */
  4166. {
  4167.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  4168.     DLine *dlPtr;
  4169.  
  4170.     /*
  4171.      * Make sure that all of the screen layout information is up to date.
  4172.      */
  4173.  
  4174.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  4175.     UpdateDisplayInfo(textPtr);
  4176.     }
  4177.  
  4178.     /*
  4179.      * Find the display line containing the desired index.
  4180.      */
  4181.  
  4182.     dlPtr = FindDLine(dInfoPtr->dLinePtr, indexPtr);
  4183.     if ((dlPtr == NULL) || (TkTextIndexCmp(&dlPtr->index, indexPtr) > 0)) {
  4184.     return -1;
  4185.     }
  4186.  
  4187.     *xPtr = dInfoPtr->x - dInfoPtr->curPixelOffset + dlPtr->chunkPtr->x;
  4188.     *widthPtr = dlPtr->length - dlPtr->chunkPtr->x;
  4189.     *yPtr = dlPtr->y;
  4190.     if ((dlPtr->y + dlPtr->height) > dInfoPtr->maxY) {
  4191.     *heightPtr = dInfoPtr->maxY - dlPtr->y;
  4192.     } else {
  4193.     *heightPtr = dlPtr->height;
  4194.     }
  4195.     *basePtr = dlPtr->baseline;
  4196.     return 0;
  4197. }
  4198.  
  4199. /*
  4200.  *--------------------------------------------------------------
  4201.  *
  4202.  * TkTextCharLayoutProc --
  4203.  *
  4204.  *    This procedure is the "layoutProc" for character segments.
  4205.  *
  4206.  * Results:
  4207.  *    If there is something to display for the chunk then a
  4208.  *    non-zero value is returned and the fields of chunkPtr
  4209.  *    will be filled in (see the declaration of TkTextDispChunk
  4210.  *    in tkText.h for details).  If zero is returned it means
  4211.  *    that no characters from this chunk fit in the window.
  4212.  *    If -1 is returned it means that this segment just doesn't
  4213.  *    need to be displayed (never happens for text).
  4214.  *
  4215.  * Side effects:
  4216.  *    Memory is allocated to hold additional information about
  4217.  *    the chunk.
  4218.  *
  4219.  *--------------------------------------------------------------
  4220.  */
  4221.  
  4222. int
  4223. TkTextCharLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
  4224.     noCharsYet, wrapMode, chunkPtr)
  4225.     TkText *textPtr;        /* Text widget being layed out. */
  4226.     TkTextIndex *indexPtr;    /* Index of first character to lay out
  4227.                  * (corresponds to segPtr and offset). */
  4228.     TkTextSegment *segPtr;    /* Segment being layed out. */
  4229.     int offset;            /* Offset within segment of first character
  4230.                  * to consider. */
  4231.     int maxX;            /* Chunk must not occupy pixels at this
  4232.                  * position or higher. */
  4233.     int maxChars;        /* Chunk must not include more than this
  4234.                  * many characters. */
  4235.     int noCharsYet;        /* Non-zero means no characters have been
  4236.                  * assigned to this display line yet. */
  4237.     Tk_Uid wrapMode;        /* How to handle line wrapping: tkTextCharUid,
  4238.                  * tkTextNoneUid, or tkTextWordUid. */
  4239.     register TkTextDispChunk *chunkPtr;
  4240.                 /* Structure to fill in with information
  4241.                  * about this chunk.  The x field has already
  4242.                  * been set by the caller. */
  4243. {
  4244.     Tk_Font tkfont;
  4245.     int nextX, charsThatFit, count;
  4246.     CharInfo *ciPtr;
  4247.     char *p;
  4248.     TkTextSegment *nextPtr;
  4249.     Tk_FontMetrics fm;
  4250.  
  4251.     /*
  4252.      * Figure out how many characters will fit in the space we've got.
  4253.      * Include the next character, even though it won't fit completely,
  4254.      * if any of the following is true:
  4255.      *   (a) the chunk contains no characters and the display line contains
  4256.      *         no characters yet (i.e. the line isn't wide enough to hold
  4257.      *         even a single character).
  4258.      *   (b) at least one pixel of the character is visible, we haven't
  4259.      *         already exceeded the character limit, and the next character
  4260.      *         is a white space character.
  4261.      */
  4262.  
  4263.     p = segPtr->body.chars + offset;
  4264.     tkfont = chunkPtr->stylePtr->sValuePtr->tkfont;
  4265.     charsThatFit = MeasureChars(tkfont, p, maxChars, chunkPtr->x, maxX, 0,
  4266.         &nextX);
  4267.     if (charsThatFit < maxChars) {
  4268.     if ((charsThatFit == 0) && noCharsYet) {
  4269.         charsThatFit = 1;
  4270.         MeasureChars(tkfont, p, 1, chunkPtr->x, INT_MAX, 0, &nextX);
  4271.     }
  4272.     if ((nextX < maxX) && ((p[charsThatFit] == ' ')
  4273.         || (p[charsThatFit] == '\t'))) {
  4274.         /*
  4275.          * Space characters are funny, in that they are considered
  4276.          * to fit if there is at least one pixel of space left on the
  4277.          * line.  Just give the space character whatever space is left.
  4278.          */
  4279.  
  4280.         nextX = maxX;
  4281.         charsThatFit++;
  4282.     }
  4283.     if (p[charsThatFit] == '\n') {
  4284.         /*
  4285.          * A newline character takes up no space, so if the previous
  4286.          * character fits then so does the newline.
  4287.          */
  4288.  
  4289.         charsThatFit++;
  4290.     }
  4291.     if (charsThatFit == 0) {
  4292.         return 0;
  4293.     }
  4294.     }
  4295.     
  4296.     Tk_GetFontMetrics(tkfont, &fm);
  4297.  
  4298.     /*
  4299.      * Fill in the chunk structure and allocate and initialize a
  4300.      * CharInfo structure.  If the last character is a newline
  4301.      * then don't bother to display it.
  4302.      */
  4303.  
  4304.     chunkPtr->displayProc = CharDisplayProc;
  4305.     chunkPtr->undisplayProc = CharUndisplayProc;
  4306.     chunkPtr->measureProc = CharMeasureProc;
  4307.     chunkPtr->bboxProc = CharBboxProc;
  4308.     chunkPtr->numChars = charsThatFit;
  4309.     chunkPtr->minAscent = fm.ascent + chunkPtr->stylePtr->sValuePtr->offset;
  4310.     chunkPtr->minDescent = fm.descent - chunkPtr->stylePtr->sValuePtr->offset;
  4311.     chunkPtr->minHeight = 0;
  4312.     chunkPtr->width = nextX - chunkPtr->x;
  4313.     chunkPtr->breakIndex = -1;
  4314.     ciPtr = (CharInfo *) ckalloc((unsigned)
  4315.         (sizeof(CharInfo) - 3 + charsThatFit));
  4316.     chunkPtr->clientData = (ClientData) ciPtr;
  4317.     ciPtr->numChars = charsThatFit;
  4318.     strncpy(ciPtr->chars, p, (size_t) charsThatFit);
  4319.     if (p[charsThatFit-1] == '\n') {
  4320.     ciPtr->numChars--;
  4321.     }
  4322.  
  4323.     /*
  4324.      * Compute a break location.  If we're in word wrap mode, a
  4325.      * break can occur after any space character, or at the end of
  4326.      * the chunk if the next segment (ignoring those with zero size)
  4327.      * is not a character segment.
  4328.      */
  4329.  
  4330.     if (wrapMode != tkTextWordUid) {
  4331.     chunkPtr->breakIndex = chunkPtr->numChars;
  4332.     } else {
  4333.     for (count = charsThatFit, p += charsThatFit-1; count > 0;
  4334.         count--, p--) {
  4335.         if (isspace(UCHAR(*p))) {
  4336.         chunkPtr->breakIndex = count;
  4337.         break;
  4338.         }
  4339.     }
  4340.     if ((charsThatFit+offset) == segPtr->size) {
  4341.         for (nextPtr = segPtr->nextPtr; nextPtr != NULL;
  4342.             nextPtr = nextPtr->nextPtr) {
  4343.         if (nextPtr->size != 0) {
  4344.             if (nextPtr->typePtr != &tkTextCharType) {
  4345.             chunkPtr->breakIndex = chunkPtr->numChars;
  4346.             }
  4347.             break;
  4348.         }
  4349.         }
  4350.     }
  4351.     }
  4352.     return 1;
  4353. }
  4354.  
  4355. /*
  4356.  *--------------------------------------------------------------
  4357.  *
  4358.  * CharDisplayProc --
  4359.  *
  4360.  *    This procedure is called to display a character chunk on
  4361.  *    the screen or in an off-screen pixmap.
  4362.  *
  4363.  * Results:
  4364.  *    None.
  4365.  *
  4366.  * Side effects:
  4367.  *    Graphics are drawn.
  4368.  *
  4369.  *--------------------------------------------------------------
  4370.  */
  4371.  
  4372. static void
  4373. CharDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
  4374.     TkTextDispChunk *chunkPtr;        /* Chunk that is to be drawn. */
  4375.     int x;                /* X-position in dst at which to
  4376.                      * draw this chunk (may differ from
  4377.                      * the x-position in the chunk because
  4378.                      * of scrolling). */
  4379.     int y;                /* Y-position at which to draw this
  4380.                      * chunk in dst. */
  4381.     int height;                /* Total height of line. */
  4382.     int baseline;            /* Offset of baseline from y. */
  4383.     Display *display;            /* Display to use for drawing. */
  4384.     Drawable dst;            /* Pixmap or window in which to draw
  4385.                      * chunk. */
  4386.     int screenY;            /* Y-coordinate in text window that
  4387.                      * corresponds to y. */
  4388. {
  4389.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4390.     TextStyle *stylePtr;
  4391.     StyleValues *sValuePtr;
  4392.     int offsetChars, offsetX;
  4393.  
  4394.     if ((x + chunkPtr->width) <= 0) {
  4395.     /*
  4396.      * The chunk is off-screen.
  4397.      */
  4398.  
  4399.     return;
  4400.     }
  4401.  
  4402.     stylePtr = chunkPtr->stylePtr;
  4403.     sValuePtr = stylePtr->sValuePtr;
  4404.  
  4405.     /*
  4406.      * If the text sticks out way to the left of the window, skip
  4407.      * over the characters that aren't in the visible part of the
  4408.      * window.  This is essential if x is very negative (such as
  4409.      * less than 32K);  otherwise overflow problems will occur
  4410.      * in servers that use 16-bit arithmetic, like X.
  4411.      */
  4412.  
  4413.     offsetX = x;
  4414.     offsetChars = 0;
  4415.     if (x < 0) {
  4416.     offsetChars = MeasureChars(sValuePtr->tkfont, ciPtr->chars,
  4417.         ciPtr->numChars, x, 0, x - chunkPtr->x, &offsetX);
  4418.     }
  4419.  
  4420.     /*
  4421.      * Draw the text, underline, and overstrike for this chunk.
  4422.      */
  4423.  
  4424.     if (ciPtr->numChars > offsetChars) {
  4425.     int numChars = ciPtr->numChars - offsetChars;
  4426.     char *string = ciPtr->chars + offsetChars;
  4427.  
  4428.     if ((numChars > 0) && (string[numChars - 1] == '\t')) {
  4429.         numChars--;
  4430.     }
  4431.     Tk_DrawChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont, string,
  4432.         numChars, offsetX, y + baseline - sValuePtr->offset);
  4433.     if (sValuePtr->underline) {
  4434.         Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
  4435.             ciPtr->chars + offsetChars, offsetX,
  4436.             y + baseline - sValuePtr->offset,
  4437.             0, numChars);
  4438.  
  4439.     }
  4440.     if (sValuePtr->overstrike) {
  4441.         Tk_FontMetrics fm;
  4442.         
  4443.         Tk_GetFontMetrics(sValuePtr->tkfont, &fm);
  4444.         Tk_UnderlineChars(display, dst, stylePtr->fgGC, sValuePtr->tkfont,
  4445.             ciPtr->chars + offsetChars, offsetX,
  4446.             y + baseline - sValuePtr->offset
  4447.                 - fm.descent - (fm.ascent * 3) / 10,
  4448.             0, numChars);
  4449.     }
  4450.     }
  4451. }
  4452.  
  4453. /*
  4454.  *--------------------------------------------------------------
  4455.  *
  4456.  * CharUndisplayProc --
  4457.  *
  4458.  *    This procedure is called when a character chunk is no
  4459.  *    longer going to be displayed.  It frees up resources
  4460.  *    that were allocated to display the chunk.
  4461.  *
  4462.  * Results:
  4463.  *    None.
  4464.  *
  4465.  * Side effects:
  4466.  *    Memory and other resources get freed.
  4467.  *
  4468.  *--------------------------------------------------------------
  4469.  */
  4470.  
  4471. static void
  4472. CharUndisplayProc(textPtr, chunkPtr)
  4473.     TkText *textPtr;            /* Overall information about text
  4474.                      * widget. */
  4475.     TkTextDispChunk *chunkPtr;        /* Chunk that is about to be freed. */
  4476. {
  4477.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4478.  
  4479.     ckfree((char *) ciPtr);
  4480. }
  4481.  
  4482. /*
  4483.  *--------------------------------------------------------------
  4484.  *
  4485.  * CharMeasureProc --
  4486.  *
  4487.  *    This procedure is called to determine which character in
  4488.  *    a character chunk lies over a given x-coordinate.
  4489.  *
  4490.  * Results:
  4491.  *    The return value is the index *within the chunk* of the
  4492.  *    character that covers the position given by "x".
  4493.  *
  4494.  * Side effects:
  4495.  *    None.
  4496.  *
  4497.  *--------------------------------------------------------------
  4498.  */
  4499.  
  4500. static int
  4501. CharMeasureProc(chunkPtr, x)
  4502.     TkTextDispChunk *chunkPtr;        /* Chunk containing desired coord. */
  4503.     int x;                /* X-coordinate, in same coordinate
  4504.                      * system as chunkPtr->x. */
  4505. {
  4506.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4507.     int endX;
  4508.  
  4509.     return MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars,
  4510.         chunkPtr->numChars-1, chunkPtr->x, x, 0, &endX);
  4511. }
  4512.  
  4513. /*
  4514.  *--------------------------------------------------------------
  4515.  *
  4516.  * CharBboxProc --
  4517.  *
  4518.  *    This procedure is called to compute the bounding box of
  4519.  *    the area occupied by a single character.
  4520.  *
  4521.  * Results:
  4522.  *    There is no return value.  *xPtr and *yPtr are filled in
  4523.  *    with the coordinates of the upper left corner of the
  4524.  *    character, and *widthPtr and *heightPtr are filled in with
  4525.  *    the dimensions of the character in pixels.  Note:  not all
  4526.  *    of the returned bbox is necessarily visible on the screen
  4527.  *    (the rightmost part might be off-screen to the right,
  4528.  *    and the bottommost part might be off-screen to the bottom).
  4529.  *
  4530.  * Side effects:
  4531.  *    None.
  4532.  *
  4533.  *--------------------------------------------------------------
  4534.  */
  4535.  
  4536. static void
  4537. CharBboxProc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
  4538.     widthPtr, heightPtr)
  4539.     TkTextDispChunk *chunkPtr;        /* Chunk containing desired char. */
  4540.     int index;                /* Index of desired character within
  4541.                      * the chunk. */
  4542.     int y;                /* Topmost pixel in area allocated
  4543.                      * for this line. */
  4544.     int lineHeight;            /* Height of line, in pixels. */
  4545.     int baseline;            /* Location of line's baseline, in
  4546.                      * pixels measured down from y. */
  4547.     int *xPtr, *yPtr;            /* Gets filled in with coords of
  4548.                      * character's upper-left pixel. 
  4549.                      * X-coord is in same coordinate
  4550.                      * system as chunkPtr->x. */
  4551.     int *widthPtr;            /* Gets filled in with width of
  4552.                      * character, in pixels. */
  4553.     int *heightPtr;            /* Gets filled in with height of
  4554.                      * character, in pixels. */
  4555. {
  4556.     CharInfo *ciPtr = (CharInfo *) chunkPtr->clientData;
  4557.     int maxX;
  4558.  
  4559.     maxX = chunkPtr->width + chunkPtr->x;
  4560.     MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, ciPtr->chars, index,
  4561.         chunkPtr->x, 1000000, 0, xPtr);
  4562.  
  4563.     if (index == ciPtr->numChars) {
  4564.     /*
  4565.      * This situation only happens if the last character in a line
  4566.      * is a space character, in which case it absorbs all of the
  4567.      * extra space in the line (see TkTextCharLayoutProc).
  4568.      */
  4569.  
  4570.     *widthPtr = maxX - *xPtr;
  4571.     } else if ((ciPtr->chars[index] == '\t')
  4572.         && (index == (ciPtr->numChars-1))) {
  4573.     /*
  4574.      * The desired character is a tab character that terminates a
  4575.      * chunk;  give it all the space left in the chunk.
  4576.      */
  4577.  
  4578.     *widthPtr = maxX - *xPtr;
  4579.     } else {
  4580.     MeasureChars(chunkPtr->stylePtr->sValuePtr->tkfont, 
  4581.         ciPtr->chars + index, 1, *xPtr, 1000000, 0, widthPtr);
  4582.     if (*widthPtr > maxX) {
  4583.         *widthPtr = maxX - *xPtr;
  4584.     } else {
  4585.         *widthPtr -= *xPtr;
  4586.     }
  4587.     }
  4588.     *yPtr = y + baseline - chunkPtr->minAscent;
  4589.     *heightPtr = chunkPtr->minAscent + chunkPtr->minDescent;
  4590. }
  4591.  
  4592. /*
  4593.  *----------------------------------------------------------------------
  4594.  *
  4595.  * AdjustForTab --
  4596.  *
  4597.  *    This procedure is called to move a series of chunks right
  4598.  *    in order to align them with a tab stop.
  4599.  *
  4600.  * Results:
  4601.  *    None.
  4602.  *
  4603.  * Side effects:
  4604.  *    The width of chunkPtr gets adjusted so that it absorbs the
  4605.  *    extra space due to the tab.  The x locations in all the chunks
  4606.  *    after chunkPtr are adjusted rightward to align with the tab
  4607.  *    stop given by tabArrayPtr and index.
  4608.  *
  4609.  *----------------------------------------------------------------------
  4610.  */
  4611.  
  4612. static void
  4613. AdjustForTab(textPtr, tabArrayPtr, index, chunkPtr)
  4614.     TkText *textPtr;            /* Information about the text widget as
  4615.                      * a whole. */
  4616.     TkTextTabArray *tabArrayPtr;    /* Information about the tab stops
  4617.                      * that apply to this line.  May be
  4618.                      * NULL to indicate default tabbing
  4619.                      * (every 8 chars). */
  4620.     int index;                /* Index of current tab stop. */
  4621.     TkTextDispChunk *chunkPtr;        /* Chunk whose last character is
  4622.                      * the tab;  the following chunks
  4623.                      * contain information to be shifted
  4624.                      * right. */
  4625.  
  4626. {
  4627.     int x, desired, delta, width, decimal, i, gotDigit;
  4628.     TkTextDispChunk *chunkPtr2, *decimalChunkPtr;
  4629.     CharInfo *ciPtr;
  4630.     int tabX, prev, spaceWidth;
  4631.     char *p;
  4632.     TkTextTabAlign alignment;
  4633.  
  4634.     if (chunkPtr->nextPtr == NULL) {
  4635.     /*
  4636.      * Nothing after the actual tab;  just return.
  4637.      */
  4638.  
  4639.     return;
  4640.     }
  4641.  
  4642.     /*
  4643.      * If no tab information has been given, do the usual thing:
  4644.      * round up to the next boundary of 8 average-sized characters.
  4645.      */
  4646.  
  4647.     x = chunkPtr->nextPtr->x;
  4648.     if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
  4649.     /*
  4650.      * No tab information has been given, so use the default
  4651.      * interpretation of tabs.
  4652.      */
  4653.  
  4654.     desired = NextTabStop(textPtr->tkfont, x, 0);
  4655.     goto update;
  4656.     }
  4657.  
  4658.     if (index < tabArrayPtr->numTabs) {
  4659.     alignment = tabArrayPtr->tabs[index].alignment;
  4660.     tabX = tabArrayPtr->tabs[index].location;
  4661.     } else {
  4662.     /*
  4663.      * Ran out of tab stops;  compute a tab position by extrapolating
  4664.      * from the last two tab positions.
  4665.      */
  4666.  
  4667.     if (tabArrayPtr->numTabs > 1) {
  4668.         prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
  4669.     } else {
  4670.         prev = 0;
  4671.     }
  4672.     alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
  4673.     tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
  4674.         + (index + 1 - tabArrayPtr->numTabs)
  4675.         * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
  4676.     }
  4677.  
  4678.     if (alignment == LEFT) {
  4679.     desired = tabX;
  4680.     goto update;
  4681.     }
  4682.  
  4683.     if ((alignment == CENTER) || (alignment == RIGHT)) {
  4684.     /*
  4685.      * Compute the width of all the information in the tab group,
  4686.      * then use it to pick a desired location.
  4687.      */
  4688.  
  4689.     width = 0;
  4690.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4691.         chunkPtr2 = chunkPtr2->nextPtr) {
  4692.         width += chunkPtr2->width;
  4693.     }
  4694.     if (alignment == CENTER) {
  4695.         desired = tabX - width/2;
  4696.     } else {
  4697.         desired = tabX - width;
  4698.     }
  4699.     goto update;
  4700.     }
  4701.  
  4702.     /*
  4703.      * Must be numeric alignment.  Search through the text to be
  4704.      * tabbed, looking for the last , or . before the first character
  4705.      * that isn't a number, comma, period, or sign.
  4706.      */
  4707.  
  4708.     decimalChunkPtr = NULL;
  4709.     decimal = gotDigit = 0;
  4710.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4711.         chunkPtr2 = chunkPtr2->nextPtr) {
  4712.     if (chunkPtr2->displayProc != CharDisplayProc) {
  4713.         continue;
  4714.     }
  4715.     ciPtr = (CharInfo *) chunkPtr2->clientData;
  4716.     for (p = ciPtr->chars, i = 0; i < ciPtr->numChars; p++, i++) {
  4717.         if (isdigit(UCHAR(*p))) {
  4718.         gotDigit = 1;
  4719.         } else if ((*p == '.') || (*p == ',')) {
  4720.         decimal = p-ciPtr->chars;
  4721.         decimalChunkPtr = chunkPtr2;
  4722.         } else if (gotDigit) {
  4723.         if (decimalChunkPtr == NULL) {
  4724.             decimal = p-ciPtr->chars;
  4725.             decimalChunkPtr = chunkPtr2;
  4726.         }
  4727.         goto endOfNumber;
  4728.         }
  4729.     }
  4730.     }
  4731.     endOfNumber:
  4732.     if (decimalChunkPtr != NULL) {
  4733.     int curX;
  4734.  
  4735.     ciPtr = (CharInfo *) decimalChunkPtr->clientData;
  4736.     MeasureChars(decimalChunkPtr->stylePtr->sValuePtr->tkfont,
  4737.         ciPtr->chars, decimal, decimalChunkPtr->x, 1000000, 0, &curX);
  4738.     desired = tabX - (curX - x);
  4739.     goto update;
  4740.     } else {
  4741.     /*
  4742.      * There wasn't a decimal point.  Right justify the text.
  4743.      */
  4744.     
  4745.     width = 0;
  4746.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4747.         chunkPtr2 = chunkPtr2->nextPtr) {
  4748.         width += chunkPtr2->width;
  4749.     }
  4750.     desired = tabX - width;
  4751.     }
  4752.  
  4753.     /*
  4754.      * Shift all of the chunks to the right so that the left edge is
  4755.      * at the desired location, then expand the chunk containing the
  4756.      * tab.  Be sure that the tab occupies at least the width of a
  4757.      * space character.
  4758.      */
  4759.  
  4760.     update:
  4761.     delta = desired - x;
  4762.     MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
  4763.     if (delta < spaceWidth) {
  4764.     delta = spaceWidth;
  4765.     }
  4766.     for (chunkPtr2 = chunkPtr->nextPtr; chunkPtr2 != NULL;
  4767.         chunkPtr2 = chunkPtr2->nextPtr) {
  4768.     chunkPtr2->x += delta;
  4769.     }
  4770.     chunkPtr->width += delta;
  4771. }
  4772.  
  4773. /*
  4774.  *----------------------------------------------------------------------
  4775.  *
  4776.  * SizeOfTab --
  4777.  *
  4778.  *    This returns an estimate of the amount of white space that will
  4779.  *    be consumed by a tab.
  4780.  *
  4781.  * Results:
  4782.  *    The return value is the minimum number of pixels that will
  4783.  *    be occupied by the index'th tab of tabArrayPtr, assuming that
  4784.  *    the current position on the line is x and the end of the
  4785.  *    line is maxX.  For numeric tabs, this is a conservative
  4786.  *    estimate.  The return value is always >= 0.
  4787.  *
  4788.  * Side effects:
  4789.  *    None.
  4790.  *
  4791.  *----------------------------------------------------------------------
  4792.  */
  4793.  
  4794. static int
  4795. SizeOfTab(textPtr, tabArrayPtr, index, x, maxX)
  4796.     TkText *textPtr;            /* Information about the text widget as
  4797.                      * a whole. */
  4798.     TkTextTabArray *tabArrayPtr;    /* Information about the tab stops
  4799.                      * that apply to this line.  NULL
  4800.                      * means use default tabbing (every
  4801.                      * 8 chars.) */
  4802.     int index;                /* Index of current tab stop. */
  4803.     int x;                /* Current x-location in line. Only
  4804.                      * used if tabArrayPtr == NULL. */
  4805.     int maxX;                /* X-location of pixel just past the
  4806.                      * right edge of the line. */
  4807. {
  4808.     int tabX, prev, result, spaceWidth;
  4809.     TkTextTabAlign alignment;
  4810.  
  4811.     if ((tabArrayPtr == NULL) || (tabArrayPtr->numTabs == 0)) {
  4812.     tabX = NextTabStop(textPtr->tkfont, x, 0);
  4813.     return tabX - x;
  4814.     }
  4815.     if (index < tabArrayPtr->numTabs) {
  4816.     tabX = tabArrayPtr->tabs[index].location;
  4817.     alignment = tabArrayPtr->tabs[index].alignment;
  4818.     } else {
  4819.     /*
  4820.      * Ran out of tab stops;  compute a tab position by extrapolating
  4821.      * from the last two tab positions.
  4822.      */
  4823.  
  4824.     if (tabArrayPtr->numTabs > 1) {
  4825.         prev = tabArrayPtr->tabs[tabArrayPtr->numTabs-2].location;
  4826.     } else {
  4827.         prev = 0;
  4828.     }
  4829.     tabX = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location
  4830.         + (index + 1 - tabArrayPtr->numTabs)
  4831.         * (tabArrayPtr->tabs[tabArrayPtr->numTabs-1].location - prev);
  4832.     alignment = tabArrayPtr->tabs[tabArrayPtr->numTabs-1].alignment;
  4833.     }
  4834.     if (alignment == CENTER) {
  4835.     /*
  4836.      * Be very careful in the arithmetic below, because maxX may
  4837.      * be the largest positive number:  watch out for integer
  4838.      * overflow.
  4839.      */
  4840.  
  4841.     if ((maxX-tabX) < (tabX - x)) {
  4842.         result = (maxX - x) - 2*(maxX - tabX);
  4843.     } else {
  4844.         result = 0;
  4845.     }
  4846.     goto done;
  4847.     }
  4848.     if (alignment == RIGHT) {
  4849.     result = 0;
  4850.     goto done;
  4851.     }
  4852.  
  4853.     /*
  4854.      * Note: this treats NUMERIC alignment the same as LEFT
  4855.      * alignment, which is somewhat conservative.  However, it's
  4856.      * pretty tricky at this point to figure out exactly where
  4857.      * the damn decimal point will be.
  4858.      */
  4859.  
  4860.     if (tabX > x) {
  4861.     result = tabX - x;
  4862.     } else {
  4863.     result = 0;
  4864.     }
  4865.  
  4866.     done:
  4867.     MeasureChars(textPtr->tkfont, " ", 1, 0, INT_MAX, 0, &spaceWidth);
  4868.     if (result < spaceWidth) {
  4869.     result = spaceWidth;
  4870.     }
  4871.     return result;
  4872. }
  4873.  
  4874. /*
  4875.  *---------------------------------------------------------------------------
  4876.  *
  4877.  * NextTabStop --
  4878.  *
  4879.  *    Given the current position, determine where the next default
  4880.  *    tab stop would be located.  This procedure is called when the
  4881.  *    current chunk in the text has no tabs defined and so the default
  4882.  *    tab spacing for the font should be used.
  4883.  *
  4884.  * Results:
  4885.  *    The location in pixels of the next tab stop.
  4886.  *
  4887.  * Side effects:
  4888.  *    None.
  4889.  *
  4890.  *---------------------------------------------------------------------------
  4891.  */
  4892.  
  4893. static int
  4894. NextTabStop(tkfont, x, tabOrigin)
  4895.     Tk_Font tkfont;        /* Font in which chunk that contains tab
  4896.                  * stop will be drawn. */
  4897.     int x;            /* X-position in pixels where last
  4898.                  * character was drawn.  The next tab stop
  4899.                  * occurs somewhere after this location. */
  4900.     int tabOrigin;        /* The origin for tab stops.  May be
  4901.                  * non-zero if text has been scrolled. */
  4902. {
  4903.     int tabWidth, rem;
  4904.     
  4905.     tabWidth = Tk_TextWidth(tkfont, "0", 1) * 8;
  4906.     if (tabWidth == 0) {
  4907.     tabWidth = 1;
  4908.     }
  4909.  
  4910.     x += tabWidth;
  4911.     rem = (x - tabOrigin) % tabWidth;
  4912.     if (rem < 0) {
  4913.     rem += tabWidth;
  4914.     }
  4915.     x -= rem;
  4916.     return x;
  4917. }
  4918.  
  4919. /*
  4920.  *---------------------------------------------------------------------------
  4921.  *
  4922.  *  MeasureChars --
  4923.  *
  4924.  *    Determine the number of characters from the string that will fit
  4925.  *    in the given horizontal span.  The measurement is done under the
  4926.  *    assumption that Tk_DisplayChars will be used to actually display
  4927.  *    the characters.
  4928.  *
  4929.  *    If tabs are encountered in the string, they will be expanded
  4930.  *    to the next tab stop, unless the TK_IGNORE_TABS flag is specified.
  4931.  *
  4932.  *    If a newline is encountered in the string, the line will be
  4933.  *    broken at that point, unless the TK_NEWSLINES_NOT_SPECIAL flag
  4934.  *    is specified.  
  4935.  *
  4936.  * Results:
  4937.  *    The return value is the number of characters from source
  4938.  *    that fit in the span given by startX and maxX.  *nextXPtr
  4939.  *    is filled in with the x-coordinate at which the first
  4940.  *    character that didn't fit would be drawn, if it were to
  4941.  *    be drawn.
  4942.  *
  4943.  * Side effects:
  4944.  *    None.
  4945.  *
  4946.  *--------------------------------------------------------------
  4947.  */
  4948.  
  4949. static int
  4950. MeasureChars(tkfont, source, maxChars, startX, maxX, tabOrigin, nextXPtr)
  4951.     Tk_Font tkfont;        /* Font in which to draw characters. */
  4952.     CONST char *source;        /* Characters to be displayed.  Need not
  4953.                  * be NULL-terminated. */
  4954.     int maxChars;        /* Maximum # of characters to consider from
  4955.                  * source. */
  4956.     int startX;            /* X-position at which first character will
  4957.                  * be drawn. */
  4958.     int maxX;            /* Don't consider any character that would
  4959.                  * cross this x-position. */
  4960.     int tabOrigin;        /* X-location that serves as "origin" for
  4961.                  * tab stops. */
  4962.     int *nextXPtr;        /* Return x-position of terminating
  4963.                  * character here. */
  4964. {
  4965.     int curX, width, ch;
  4966.     CONST char *special, *end, *start;
  4967.  
  4968.     ch = 0;            /* lint. */
  4969.     curX = startX;
  4970.     special = source;
  4971.     end = source + maxChars;
  4972.     for (start = source; start < end; ) {
  4973.     if (start >= special) {
  4974.         /*
  4975.          * Find the next special character in the string.
  4976.          */
  4977.  
  4978.         for (special = start; special < end; special++) {
  4979.         ch = *special;
  4980.         if ((ch == '\t') || (ch == '\n')) {
  4981.             break;
  4982.         }
  4983.         }
  4984.     }
  4985.  
  4986.     /*
  4987.      * Special points at the next special character (or the end of the
  4988.      * string).  Process characters between start and special.
  4989.      */
  4990.  
  4991.     if (curX >= maxX) {
  4992.         break;
  4993.     }
  4994.     start += Tk_MeasureChars(tkfont, start, special - start, maxX - curX,
  4995.         0, &width);
  4996.     curX += width;
  4997.     if (start < special) {
  4998.         /*
  4999.          * No more chars fit in line.
  5000.          */
  5001.  
  5002.         break;
  5003.     }
  5004.     if (special < end) {
  5005.         if (ch == '\t') {
  5006.         start++;
  5007.         } else {
  5008.         break;
  5009.         }
  5010.     }
  5011.     }
  5012.  
  5013.     *nextXPtr = curX;
  5014.     return start - source;
  5015. }
  5016.