home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkTextDisp.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  147KB  |  4,828 lines

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