home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkTextDisp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  62.3 KB  |  2,084 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-1993 The Regents of the University of California.
  9.  * All rights reserved.
  10.  *
  11.  * Permission is hereby granted, without written agreement and without
  12.  * license or royalty fees, to use, copy, modify, and distribute this
  13.  * software and its documentation for any purpose, provided that the
  14.  * above copyright notice and the following two paragraphs appear in
  15.  * all copies of this software.
  16.  * 
  17.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  18.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  19.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  20.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  21.  *
  22.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  23.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  24.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  25.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  26.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  27.  */
  28.  
  29. #ifndef lint
  30. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkTextDisp.c,v 1.32 93/06/16 17:16:32 ouster Exp $ SPRITE (Berkeley)";
  31. #endif
  32.  
  33. #include "tkConfig.h"
  34. #include "tkInt.h"
  35. #include "tkText.h"
  36.  
  37. /*
  38.  * The following structure describes how to display a range of characters.
  39.  * The information is generated by scanning all of the tags associated
  40.  * with the characters and combining that with default information for
  41.  * the overall widget.  These structures form the hash keys for
  42.  * dInfoPtr->styleTable.
  43.  */
  44.  
  45. typedef struct StyleValues {
  46.     Tk_3DBorder border;        /* Used for drawing background under text.
  47.                  * NULL means use widget background. */
  48.     int borderWidth;        /* Width of 3-D border for background. */
  49.     int relief;            /* 3-D relief for background. */
  50.     Pixmap bgStipple;        /* Stipple bitmap for background.  None
  51.                  * means draw solid. */
  52.     XColor *fgColor;        /* Foreground color for text. */
  53.     XFontStruct *fontPtr;    /* Font for displaying text. */
  54.     Pixmap fgStipple;        /* Stipple bitmap for text and other
  55.                  * foreground stuff.   None means draw
  56.                  * solid.*/
  57.     int underline;        /* Non-zero means draw underline underneath
  58.                  * text. */
  59. } StyleValues;
  60.  
  61. /*
  62.  * The following structure extends the StyleValues structure above with
  63.  * graphics contexts used to actually draw the characters.  The entries
  64.  * in dInfoPtr->styleTable point to structures of this type.
  65.  */
  66.  
  67. typedef struct Style {
  68.     int refCount;        /* Number of times this structure is
  69.                  * referenced in Chunks. */
  70.     GC bgGC;            /* Graphics context for background.  None
  71.                  * unless background is stippled. */
  72.     GC fgGC;            /* Graphics context for foreground. */
  73.     StyleValues *sValuePtr;    /* Raw information from which GCs were
  74.                  * derived. */
  75.     Tcl_HashEntry *hPtr;    /* Pointer to entry in styleTable.  Used
  76.                  * to delete entry. */
  77. } Style;
  78.  
  79. /*
  80.  * The following structure describes a range of characters, all on the
  81.  * same line of the display (which also means the same line of the text
  82.  * widget) and all having the same display attributes.
  83.  */
  84.  
  85. typedef struct Chunk {
  86.     char *text;            /* Characters to display. */
  87.     int numChars;        /* Number of characters to display. */
  88.     Style *stylePtr;        /* Style information used to display
  89.                  * characters. */
  90.     int x;            /* X-coordinate of pixel at which to display
  91.                  * the characters. */
  92.     struct Chunk *nextPtr;    /* Next in list of all chunks displayed on the
  93.                  * same display line. */
  94. } Chunk;
  95.  
  96. /*
  97.  * The following structure describes one line of the display, which may
  98.  * be either part or all of one line of the text.
  99.  */
  100.  
  101. typedef struct DLine {
  102.     TkTextLine *linePtr;    /* Pointer to structure in B-tree that
  103.                  * contains characters displayed in this
  104.                  * line. */
  105.     int y;            /* Y-position at which line is supposed to
  106.                  * be drawn (topmost pixel of rectangular
  107.                  * area occupied by line). */
  108.     int oldY;            /* Y-position at which line currently
  109.                  * appears on display.  -1 means line isn't
  110.                  * currently visible on display.  This is
  111.                  * used to move lines by scrolling rather
  112.                  * than re-drawing. */
  113.     int height;            /* Height of line, in pixels. */
  114.     int baseline;        /* Offset of text baseline from y. */
  115.     Chunk *chunkPtr;        /* Pointer to first chunk in list of all
  116.                  * of those that are displayed on this
  117.                  * line of the screen. */
  118.     struct DLine *nextPtr;    /* Next in list of all display lines for
  119.                  * this window.   The list is sorted in
  120.                  * order from top to bottom.  Note:  the
  121.                  * next DLine doesn't always correspond
  122.                  * to the next line of text:  (a) can have
  123.                  * multiple DLines for one text line, and
  124.                  * (b) can have gaps where DLine's have been
  125.                  * deleted because they're out of date. */
  126. } DLine;
  127.  
  128. /*
  129.  * Overall display information for a text widget:
  130.  */
  131.  
  132. typedef struct DInfo {
  133.     Tcl_HashTable styleTable;    /* Hash table that maps from StyleValues to
  134.                  * Styles for this widget. */
  135.     DLine *dLinePtr;        /* First in list of all display lines for
  136.                  * this widget, in order from top to bottom. */
  137.     GC copyGC;            /* Graphics context for copying from off-
  138.                  * screen pixmaps onto screen. */
  139.     GC scrollGC;        /* Graphics context for copying from one place
  140.                  * in the window to another (scrolling):
  141.                  * differs from copyGC in that we need to get
  142.                  * GraphicsExpose events. */
  143.     int x;            /* First x-coordinate that may be used for
  144.                  * actually displaying line information.
  145.                  * Leaves space for border, etc. */
  146.     int y;            /* First y-coordinate that may be used for
  147.                  * actually displaying line information.
  148.                  * Leaves space for border, etc. */
  149.     int maxX;            /* First x-coordinate to right of available
  150.                  * space for displaying lines. */
  151.     int maxY;            /* First y-coordinate below available
  152.                  * space for displaying lines. */
  153.     int topOfEof;        /* Top-most pixel (lowest y-value) that has
  154.                  * been drawn in the appropriate fashion for
  155.                  * the portion of the window after the last
  156.                  * line of the text.  This field is used to
  157.                  * figure out when to redraw part or all of
  158.                  * the eof field. */
  159.     int flags;            /* Various flag values:  see below for
  160.                  * definitions. */
  161. } DInfo;
  162.  
  163. /*
  164.  * Flag values for DInfo structures:
  165.  *
  166.  * DINFO_OUT_OF_DATE:        Non-zero means that the DLine structures
  167.  *                for this window are partially or completely
  168.  *                out of date and need to be recomputed.
  169.  * REDRAW_PENDING:        Means that a when-idle handler has been
  170.  *                scheduled to update the display.
  171.  * REDRAW_BORDERS:        Means window border or pad area has
  172.  *                potentially been damaged and must be redrawn.
  173.  * REPICK_NEEDED:        1 means that the widget has been modified
  174.  *                in a way that could change the current
  175.  *                character (a different character might be
  176.  *                under the mouse cursor now).  Need to
  177.  *                recompute the current character before
  178.  *                the next redisplay.
  179.  */
  180.  
  181. #define DINFO_OUT_OF_DATE    1
  182. #define REDRAW_PENDING        2
  183. #define REDRAW_BORDERS        4
  184. #define REPICK_NEEDED        8
  185.  
  186. /*
  187.  * Structures of the type defined below are used to keep track of
  188.  * tags while scanning through the text to create DLine structures.
  189.  */
  190.  
  191. typedef struct TagInfo {
  192.     int numTags;        /* Number of tags currently active (the first
  193.                  * entries at *tagPtr). */
  194.     int arraySize;        /* Total number of entries at *tagPtr.  We
  195.                  * over-allocate the array to avoid continual
  196.                  * reallocations. */
  197.     TkTextTag **tagPtrs;    /* Pointer to array of pointers to active tags.
  198.                  * Array has space for arraySize tags, and
  199.                  * the first numTags are slots identify the
  200.                  * active tags. Malloc'ed (but may be NULL). */
  201.     TkTextSearch search;    /* Used to scan for tag transitions.  Current
  202.                  * state identifies next tag transition. */
  203. } TagInfo;
  204.  
  205. /*
  206.  * The following counters keep statistics about redisplay that can be
  207.  * checked to see how clever this code is at reducing redisplays.
  208.  */
  209.  
  210. static int numRedisplays;    /* Number of calls to DisplayText. */
  211. static int linesRedrawn;    /* Number of calls to DisplayDLine. */
  212. static int numCopies;        /* Number of calls to XCopyArea to copy part
  213.                  * of the screen. */
  214. static int damagedCopies;    /* Number of times that XCopyAreas didn't
  215.                  * completely work because some of the source
  216.                  * information was damaged. */
  217.  
  218. /*
  219.  * Forward declarations for procedures defined later in this file:
  220.  */
  221.  
  222. static void        ComputeStyleValues _ANSI_ARGS_((TkText *textPtr,
  223.                 int numTags, TkTextTag **tagPtr,
  224.                 StyleValues *sValuePtr));
  225. static void        DisplayDLine _ANSI_ARGS_((TkText *textPtr,
  226.                 DLine *dlPtr, Pixmap pixmap));
  227. static void        DisplayText _ANSI_ARGS_((ClientData clientData));
  228. static DLine *        FindDLine _ANSI_ARGS_((DLine *dlPtr, int line));
  229. static void        FreeDLines _ANSI_ARGS_((TkText *textPtr,
  230.                 DLine *firstPtr, DLine *lastPtr, int unlink));
  231. static void        FreeStyle _ANSI_ARGS_((TkText *textPtr,
  232.                 Style *stylePtr));
  233. static Style *        GetStyle _ANSI_ARGS_((TkText *textPtr,
  234.                 StyleValues *sValuePtr));
  235. static DLine *        LayoutLine _ANSI_ARGS_((TkText *textPtr, int line,
  236.                 TkTextLine *linePtr, TagInfo *tInfoPtr));
  237. static void        ToggleTag _ANSI_ARGS_((TagInfo *tInfoPtr,
  238.                 TkTextTag *tagPtr));
  239. static void        UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
  240.  
  241. /*
  242.  *----------------------------------------------------------------------
  243.  *
  244.  * TkTextCreateDInfo --
  245.  *
  246.  *    This procedure is called when a new text widget is created.
  247.  *    Its job is to set up display-related information for the widget.
  248.  *
  249.  * Results:
  250.  *    None.
  251.  *
  252.  * Side effects:
  253.  *    A DInfo data structure is allocated and initialized and attached
  254.  *    to textPtr.
  255.  *
  256.  *----------------------------------------------------------------------
  257.  */
  258.  
  259. void
  260. TkTextCreateDInfo(textPtr)
  261.     TkText *textPtr;        /* Overall information for text widget. */
  262. {
  263.     register DInfo *dInfoPtr;
  264.     XGCValues gcValues;
  265.  
  266.     dInfoPtr = (DInfo *) ckalloc(sizeof(DInfo));
  267.     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
  268.     dInfoPtr->dLinePtr = NULL;
  269.     gcValues.graphics_exposures = False;
  270.     dInfoPtr->copyGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures, &gcValues);
  271.     gcValues.graphics_exposures = True;
  272.     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
  273.         &gcValues);
  274.     dInfoPtr->topOfEof = 0;
  275.     dInfoPtr->flags = DINFO_OUT_OF_DATE;
  276.     textPtr->dInfoPtr = dInfoPtr;
  277. }
  278.  
  279. /*
  280.  *----------------------------------------------------------------------
  281.  *
  282.  * TkTextFreeDInfo --
  283.  *
  284.  *    This procedure is called to free up all of the private display
  285.  *    information kept by this file for a text widget.
  286.  *
  287.  * Results:
  288.  *    None.
  289.  *
  290.  * Side effects:
  291.  *    Lots of resources get freed.
  292.  *
  293.  *----------------------------------------------------------------------
  294.  */
  295.  
  296. void
  297. TkTextFreeDInfo(textPtr)
  298.     TkText *textPtr;        /* Overall information for text widget. */
  299. {
  300.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  301.  
  302.     /*
  303.      * Be careful to free up styleTable *after* freeing up all the
  304.      * DLines, so that the hash table is still intact to free up the
  305.      * style-related information from the lines.  Once the lines are
  306.      * all free then styleTable will be empty.
  307.      */
  308.  
  309.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  310.     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
  311.     Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  312.     Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
  313.     if (dInfoPtr->flags & REDRAW_PENDING) {
  314.         Tk_CancelIdleCall(DisplayText, (ClientData) textPtr);
  315.     }
  316.     ckfree((char *) dInfoPtr);
  317. }
  318.  
  319. /*
  320.  *----------------------------------------------------------------------
  321.  *
  322.  * GetStyle --
  323.  *
  324.  *    This procedure creates graphics contexts needed to display
  325.  *    text in a particular style, determined by "sValuePtr".  It
  326.  *    attempts to share style information as much as possible.
  327.  *
  328.  * Results:
  329.  *    The return value is a pointer to a Style structure that
  330.  *    corresponds to *sValuePtr.
  331.  *
  332.  * Side effects:
  333.  *    A new entry may be created in the style table for the widget.
  334.  *
  335.  *----------------------------------------------------------------------
  336.  */
  337.  
  338. static Style *
  339. GetStyle(textPtr, sValuePtr)
  340.     TkText *textPtr;        /* Overall information about text widget. */
  341.     StyleValues *sValuePtr;    /* Information about desired style. */
  342. {
  343.     Style *stylePtr;
  344.     Tcl_HashEntry *hPtr;
  345.     int new;
  346.     XGCValues gcValues;
  347.     unsigned long mask;
  348.  
  349.     /*
  350.      * Use an existing style if there's one around that matches.
  351.      */
  352.  
  353.     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
  354.         (char *) sValuePtr, &new);
  355.     if (!new) {
  356.     stylePtr = (Style *) Tcl_GetHashValue(hPtr);
  357.     stylePtr->refCount++;
  358.     return stylePtr;
  359.     }
  360.  
  361.     /*
  362.      * No existing style matched.  Make a new one.
  363.      */
  364.  
  365.     stylePtr = (Style *) ckalloc(sizeof(Style));
  366.     stylePtr->refCount = 1;
  367.     if ((sValuePtr->border != NULL) && (sValuePtr->bgStipple != None)) {
  368.     gcValues.foreground = Tk_3DBorderColor(sValuePtr->border)->pixel;
  369.     gcValues.stipple = sValuePtr->bgStipple;
  370.     gcValues.fill_style = FillStippled;
  371.     stylePtr->bgGC = Tk_GetGC(textPtr->tkwin,
  372.         GCForeground|GCStipple|GCFillStyle, &gcValues);
  373.     } else {
  374.     stylePtr->bgGC = None;
  375.     }
  376.     mask = GCForeground|GCFont;
  377.     gcValues.foreground = sValuePtr->fgColor->pixel;
  378.     gcValues.font = sValuePtr->fontPtr->fid;
  379.     if (sValuePtr->fgStipple != None) {
  380.     gcValues.stipple = sValuePtr->fgStipple;
  381.     gcValues.fill_style = FillStippled;
  382.     mask |= GCStipple|GCFillStyle;
  383.     }
  384.     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  385.     stylePtr->sValuePtr = (StyleValues *)
  386.         Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
  387.     stylePtr->hPtr = hPtr;
  388.     Tcl_SetHashValue(hPtr, stylePtr);
  389.     return stylePtr;
  390. }
  391.  
  392. /*
  393.  *----------------------------------------------------------------------
  394.  *
  395.  * FreeStyle --
  396.  *
  397.  *    This procedure is called when a Style structure is no longer
  398.  *    needed.  It decrements the reference count and frees up the
  399.  *    space for the style structure if the reference count is 0.
  400.  *
  401.  * Results:
  402.  *    None.
  403.  *
  404.  * Side effects:
  405.  *    The storage and other resources associated with the style
  406.  *    are freed up if no-one's still using it.
  407.  *
  408.  *----------------------------------------------------------------------
  409.  */
  410.  
  411. static void
  412. FreeStyle(textPtr, stylePtr)
  413.     TkText *textPtr;        /* Information about overall widget. */
  414.     register Style *stylePtr;    /* Information about style to be freed. */
  415.  
  416. {
  417.     stylePtr->refCount--;
  418.     if (stylePtr->refCount == 0) {
  419.     if (stylePtr->bgGC != None) {
  420.         Tk_FreeGC(textPtr->display, stylePtr->bgGC);
  421.     }
  422.     Tk_FreeGC(textPtr->display, stylePtr->fgGC);
  423.     Tcl_DeleteHashEntry(stylePtr->hPtr);
  424.     ckfree((char *) stylePtr);
  425.     }
  426. }
  427.  
  428. /*
  429.  *----------------------------------------------------------------------
  430.  *
  431.  * ComputeStyleValues --
  432.  *
  433.  *    Given a list of tags that apply at a particular point, compute
  434.  *    the StyleValues that correspond to that set of tags.
  435.  *
  436.  * Results:
  437.  *    All of the fields of *sValuePtr get filled in to hold the
  438.  *    appropriate display information for the given set of tags
  439.  *    in the given widget.
  440.  *
  441.  * Side effects:
  442.  *    None.
  443.  *
  444.  *----------------------------------------------------------------------
  445.  */
  446.  
  447. static void
  448. ComputeStyleValues(textPtr, numTags, tagPtrPtr, sValuePtr)
  449.     TkText *textPtr;            /* Overall information for widget. */
  450.     int numTags;            /* Number of tags at *tagPtr. */
  451.     register TkTextTag **tagPtrPtr;    /* Pointer to array of tag pointers. */
  452.     register StyleValues *sValuePtr;    /* Pointer to structure to fill in. */
  453. {
  454.     register TkTextTag *tagPtr;
  455.  
  456.     /*
  457.      * The variables below keep track of the highest-priority specification
  458.      * that has occurred for each of the various fields of the StyleValues.
  459.      */
  460.  
  461.     int borderPrio, bgStipplePrio;
  462.     int fgPrio, fontPrio, fgStipplePrio;
  463.  
  464.     borderPrio = bgStipplePrio = -1;
  465.     fgPrio = fontPrio = fgStipplePrio = -1;
  466.     memset((VOID *) sValuePtr, 0, sizeof(StyleValues));
  467.     sValuePtr->fgColor = textPtr->fgColor;
  468.     sValuePtr->fontPtr = textPtr->fontPtr;
  469.  
  470.     /*
  471.      * Scan through all of the tags, updating the StyleValues to hold
  472.      * the highest-priority information.
  473.      */
  474.  
  475.     for ( ; numTags > 0; tagPtrPtr++, numTags--) {
  476.     tagPtr = *tagPtrPtr;
  477.     if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
  478.         sValuePtr->border = tagPtr->border;
  479.         sValuePtr->borderWidth = tagPtr->borderWidth;
  480.         sValuePtr->relief = tagPtr->relief;
  481.         borderPrio = tagPtr->priority;
  482.     }
  483.     if ((tagPtr->bgStipple != None)
  484.         && (tagPtr->priority > bgStipplePrio)) {
  485.         sValuePtr->bgStipple = tagPtr->bgStipple;
  486.         bgStipplePrio = tagPtr->priority;
  487.     }
  488.     if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
  489.         sValuePtr->fgColor = tagPtr->fgColor;
  490.         fgPrio = tagPtr->priority;
  491.     }
  492.     if ((tagPtr->fontPtr != None) && (tagPtr->priority > fontPrio)) {
  493.         sValuePtr->fontPtr = tagPtr->fontPtr;
  494.         fontPrio = tagPtr->priority;
  495.     }
  496.     if ((tagPtr->fgStipple != None)
  497.         && (tagPtr->priority > fgStipplePrio)) {
  498.         sValuePtr->fgStipple = tagPtr->fgStipple;
  499.         fgStipplePrio = tagPtr->priority;
  500.     }
  501.     if (tagPtr->underline) {
  502.         sValuePtr->underline = 1;
  503.     }
  504.     }
  505. }
  506.  
  507. /*
  508.  *----------------------------------------------------------------------
  509.  *
  510.  * LayoutLine --
  511.  *
  512.  *    This procedure generates a linked list of one or more DLine
  513.  *    structures, which describe how to display everything in one
  514.  *    line of the text.
  515.  *
  516.  * Results:
  517.  *    The return value is a pointer to one or more DLine structures
  518.  *    linked into a linked list.  The structures are completely filled
  519.  *    in except for the y field, which the caller must supply.  Also,
  520.  *    the information at *tInfoPtr gets updated to refer to the state
  521.  *    just after the last character of the line.
  522.  *
  523.  * Side effects:
  524.  *    None.
  525.  *
  526.  *----------------------------------------------------------------------
  527.  */
  528.  
  529. static DLine *
  530. LayoutLine(textPtr, line, linePtr, tInfoPtr)
  531.     TkText *textPtr;        /* Overall information about text widget. */
  532.     int line;            /* Index of line to layout. */
  533.     TkTextLine *linePtr;    /* Line to layout (corresponds to line). */
  534.     TagInfo *tInfoPtr;        /* Information to help keep track of tags.
  535.                  * Caller must have initialized to correspond
  536.                  * to state just before start of line. */
  537. {
  538.     DLine *firstLinePtr;
  539.     DLine *lastLinePtr = NULL;    /* Initializations needed only to stop */
  540.     Chunk *lastChunkPtr = NULL;    /* compiler warnings. */
  541.     register DLine *dlPtr;
  542.     register Chunk *chunkPtr;
  543.     StyleValues styleValues;
  544.     int ch, charsThatFit, ascent, descent, x, maxX;
  545.  
  546.     firstLinePtr = NULL;
  547.  
  548.     /*
  549.      * Each iteration of the loop below creates one DLine structure.
  550.      */
  551.  
  552.     ch = 0;
  553.     while (1) {
  554.  
  555.     /*
  556.      * Create and initialize a new DLine structure.
  557.      */
  558.  
  559.     dlPtr = (DLine *) ckalloc(sizeof(DLine));
  560.     dlPtr->linePtr = linePtr;
  561.     dlPtr->y = 0;
  562.     dlPtr->oldY = -1;
  563.     dlPtr->chunkPtr = NULL;
  564.     dlPtr->nextPtr = NULL;
  565.     if (firstLinePtr == NULL) {
  566.         firstLinePtr = dlPtr;
  567.     } else {
  568.         lastLinePtr->nextPtr = dlPtr;
  569.     }
  570.     lastLinePtr = dlPtr;
  571.  
  572.     /*
  573.      * Each iteration of the loop below creates one Chunk for the
  574.      * new display line.  Be sure always to create at least one chunk.
  575.      */
  576.  
  577.     x = textPtr->dInfoPtr->x;
  578.     maxX = textPtr->dInfoPtr->maxX;
  579.     ascent = descent = 0;
  580.     do {
  581.         chunkPtr = (Chunk *) ckalloc(sizeof(Chunk));
  582.         chunkPtr->numChars = linePtr->numBytes - ch;
  583.         chunkPtr->text = linePtr->bytes + ch;
  584.         chunkPtr->x = x;
  585.         chunkPtr->nextPtr = NULL;
  586.         if (dlPtr->chunkPtr == NULL) {
  587.         dlPtr->chunkPtr = chunkPtr;
  588.         } else {
  589.         lastChunkPtr->nextPtr = chunkPtr;
  590.         }
  591.         lastChunkPtr = chunkPtr;
  592.  
  593.         /*
  594.          * Update the tag array to include any tag transitions up
  595.          * through the current position, then find the next position
  596.          * with a transition on a tag that impacts the way things are
  597.          * displayed.
  598.          */
  599.  
  600.         while (1) {
  601.         int affectsDisplay;
  602.         TkTextTag *tagPtr;
  603.  
  604.         if ((tInfoPtr->search.linePtr == NULL)
  605.             || (tInfoPtr->search.line1 > line)) {
  606.             break;
  607.         }
  608.         tagPtr = tInfoPtr->search.tagPtr;
  609.         affectsDisplay = TK_TAG_AFFECTS_DISPLAY(tagPtr);
  610.         if ((tInfoPtr->search.line1 < line)
  611.             || (tInfoPtr->search.ch1 <= ch)) {
  612.             if (affectsDisplay) {
  613.             ToggleTag(tInfoPtr, tagPtr);
  614.             }
  615.         } else {
  616.             if (affectsDisplay) {
  617.             chunkPtr->numChars = tInfoPtr->search.ch1 - ch;
  618.             break;
  619.             }
  620.         }
  621.         (void) TkBTreeNextTag(&tInfoPtr->search);
  622.         }
  623.  
  624.         /*
  625.          * Create style information for this chunk.
  626.          */
  627.  
  628.         ComputeStyleValues(textPtr, tInfoPtr->numTags, tInfoPtr->tagPtrs,
  629.             &styleValues);
  630.         chunkPtr->stylePtr = GetStyle(textPtr, &styleValues);
  631.  
  632.         /*
  633.          * See how many characters will fit on the line.  If they don't
  634.          * all fit, then a number of compensations may have to be made.
  635.          *
  636.          * 1. Make sure that at least one character is displayed on
  637.          *    each line.
  638.          * 2. In wrap mode "none", allow a partial character to be
  639.          *    displayed at the end of an incomplete line.
  640.          * 3. In wrap mode "word", search back to find the last space
  641.          *    character, and terminate the line just after that space
  642.          *    character.  This involves a couple of extra complexities:
  643.          *        - the last space may be several chunks back;  in this
  644.          *        case, delete all the chunks that are after the
  645.          *        space.
  646.          *          - if no words fit at all, then use character-wrap for
  647.          *        this DLine.
  648.          *        - have to reinitialize the tag search information, since
  649.          *        we may back up over tag toggles (they'll need to be
  650.          *        reconsidered on the next DLine).
  651.          */
  652.  
  653.         charsThatFit = TkMeasureChars(styleValues.fontPtr,
  654.             chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
  655.             maxX, 0, &x);
  656.         if ((charsThatFit < chunkPtr->numChars) || (x >= maxX)) {
  657.         x = maxX;
  658.         chunkPtr->numChars = charsThatFit;
  659.         ch += charsThatFit;
  660.         if (ch < (linePtr->numBytes - 1)) {
  661.             if ((charsThatFit == 0) && (chunkPtr == dlPtr->chunkPtr)) {
  662.             chunkPtr->numChars = 1;
  663.             ch++;
  664.             } else if (textPtr->wrapMode == tkTextWordUid) {
  665.             if (isspace(chunkPtr->text[charsThatFit])) {
  666.                 ch += 1;    /* Include space on this line. */
  667.             } else {
  668.                 register Chunk *chunkPtr2;
  669.                 register char *p;
  670.                 Chunk *spaceChunkPtr;
  671.                 int count, space;
  672.  
  673.                 spaceChunkPtr = NULL;
  674.                 space = 0;
  675.                 for (chunkPtr2 = dlPtr->chunkPtr;
  676.                     chunkPtr2 != NULL;
  677.                     chunkPtr2 = chunkPtr2->nextPtr) {
  678.                 for (count = chunkPtr2->numChars - 1,
  679.                     p = chunkPtr2->text + count;
  680.                     count >= 0; count--, p--) {
  681.                     if (isspace(*p)) {
  682.                     spaceChunkPtr = chunkPtr2;
  683.                     space = count;
  684.                     break;
  685.                     }
  686.                 }
  687.                 }
  688.                 if (spaceChunkPtr != NULL) {
  689.                 spaceChunkPtr->numChars = space;
  690.                 ch = (spaceChunkPtr->text + space + 1)
  691.                     - linePtr->bytes;
  692.                 if (chunkPtr != spaceChunkPtr) {
  693.                     chunkPtr = spaceChunkPtr;
  694.                     if (tInfoPtr->tagPtrs != NULL) {
  695.                     ckfree((char *) tInfoPtr->tagPtrs);
  696.                     }
  697.                     tInfoPtr->tagPtrs = TkBTreeGetTags(
  698.                         textPtr->tree, dlPtr->linePtr, ch,
  699.                         &tInfoPtr->numTags);
  700.                     TkBTreeStartSearch(textPtr->tree, line,
  701.                         ch+1,
  702.                         TkBTreeNumLines(textPtr->tree), 0,
  703.                         (TkTextTag *) NULL,
  704.                         &tInfoPtr->search);
  705.                     (void) TkBTreeNextTag(&tInfoPtr->search);
  706.                     tInfoPtr->arraySize = tInfoPtr->numTags;
  707.                     while (chunkPtr->nextPtr != NULL) {
  708.                     chunkPtr2 = chunkPtr->nextPtr;
  709.                     chunkPtr->nextPtr = chunkPtr2->nextPtr;
  710.                     FreeStyle(textPtr, chunkPtr2->stylePtr);
  711.                     ckfree((char *) chunkPtr2);
  712.                     }
  713.                 }
  714.                 }
  715.             }
  716.             } else if (textPtr->wrapMode == tkTextNoneUid) {
  717.             chunkPtr->numChars++;
  718.             ch++;
  719.             }
  720.         }
  721.         } else {
  722.         ch += chunkPtr->numChars;
  723.         }
  724.  
  725.         /*
  726.          * Update height information for use later in computing
  727.          * line's overall height and baseline.
  728.          */
  729.  
  730.         if (styleValues.fontPtr->ascent > ascent) {
  731.         ascent = styleValues.fontPtr->ascent;
  732.         }
  733.         if (styleValues.fontPtr->descent > descent) {
  734.         descent = styleValues.fontPtr->descent;
  735.         }
  736.     } while (x < maxX);
  737.  
  738.     dlPtr->height = ascent + descent;
  739.     dlPtr->baseline = ascent;
  740.  
  741.     /*
  742.      * Quit when every character but the last character (the newline)
  743.      * has been accounted for.  Also quit if the wrap mode is "none":
  744.      * this ignores all the characters that don't fit on the first
  745.      * line.
  746.      */
  747.  
  748.     if ((ch >= (linePtr->numBytes-1))
  749.         || (textPtr->wrapMode == tkTextNoneUid)) {
  750.         break;
  751.     }
  752.     }
  753.     return firstLinePtr;
  754. }
  755.  
  756. /*
  757.  *----------------------------------------------------------------------
  758.  *
  759.  * ToggleTag --
  760.  *
  761.  *    Update information about tags to reflect a transition on a
  762.  *    particular tag.
  763.  *
  764.  * Results:
  765.  *    The array at *tInfoPtr is modified to include tagPtr if it
  766.  *    didn't already or to exclude it if it used to include it.
  767.  *    The array will be reallocated to a larger size if needed.
  768.  *
  769.  * Side effects:
  770.  *    None.
  771.  *
  772.  *----------------------------------------------------------------------
  773.  */
  774.  
  775. static void
  776. ToggleTag(tInfoPtr, tagPtr)
  777.     register TagInfo *tInfoPtr;        /* Tag information to be updated. */
  778.     TkTextTag *tagPtr;            /* Tag to be toggled into or out of
  779.                      * *tInfoPtr. */
  780. {
  781.     register TkTextTag **tagPtrPtr;
  782.     int i;
  783.  
  784.     for (i = tInfoPtr->numTags, tagPtrPtr = tInfoPtr->tagPtrs;
  785.         i > 0; i--, tagPtrPtr++) {
  786.     if (*tagPtrPtr == tagPtr) {
  787.         tInfoPtr->numTags--;
  788.         *tagPtrPtr = tInfoPtr->tagPtrs[tInfoPtr->numTags];
  789.         return;
  790.     }
  791.     }
  792.  
  793.     /*
  794.      * Tag not currently in array.  Grow the array if necessary, then
  795.      * add the tag to it.
  796.      */
  797.  
  798.     if (tInfoPtr->numTags == tInfoPtr->arraySize) {
  799.     TkTextTag **newPtrs;
  800.  
  801.     newPtrs = (TkTextTag **) ckalloc((unsigned)
  802.         ((tInfoPtr->arraySize+10) * sizeof(TkTextTag *)));
  803.     if (tInfoPtr->tagPtrs != NULL) {
  804.         memcpy((VOID *) newPtrs, (VOID *) tInfoPtr->tagPtrs,
  805.             tInfoPtr->arraySize * sizeof(TkTextTag *));
  806.         ckfree((char *) tInfoPtr->tagPtrs);
  807.     }
  808.     tInfoPtr->tagPtrs = newPtrs;
  809.     tInfoPtr->arraySize += 10;
  810.     }
  811.     tInfoPtr->tagPtrs[tInfoPtr->numTags] = tagPtr;
  812.     tInfoPtr->numTags++;
  813. }
  814.  
  815. /*
  816.  *----------------------------------------------------------------------
  817.  *
  818.  * UpdateDisplayInfo --
  819.  *
  820.  *    This procedure is invoked to recompute some or all of the
  821.  *    DLine structures for a text widget.  At the time it is called
  822.  *    the DLine structures still left in the widget are guaranteed
  823.  *    to be correct (except for their y-coordinates), but there may
  824.  *    be missing structures (the DLine structures get removed as
  825.  *    soon as they are potentially out-of-date).
  826.  *
  827.  * Results:
  828.  *    None.
  829.  *
  830.  * Side effects:
  831.  *    Upon return, the DLine information for textPtr correctly reflects
  832.  *    the positions where characters will be displayed.  However, this
  833.  *    procedure doesn't actually bring the display up-to-date.
  834.  *
  835.  *----------------------------------------------------------------------
  836.  */
  837.  
  838. static void
  839. UpdateDisplayInfo(textPtr)
  840.     TkText *textPtr;        /* Text widget to update. */
  841. {
  842.     register DInfo *dInfoPtr = textPtr->dInfoPtr;
  843.     register DLine *dlPtr, *prevPtr, *dlPtr2;
  844.     TkTextLine *linePtr;
  845.     TagInfo tagInfo;
  846.     int line, y, maxY;
  847.  
  848.     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
  849.     return;
  850.     }
  851.     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
  852.  
  853.     linePtr = textPtr->topLinePtr;
  854.     dlPtr = dInfoPtr->dLinePtr;
  855.     tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
  856.         &tagInfo.numTags);
  857.     tagInfo.arraySize = tagInfo.numTags;
  858.  
  859.     /*
  860.      * Tricky point:  initialize the tag search just *after* the first
  861.      * character in the line, since the tagInfo structure already has all
  862.      * the tags for the first character.
  863.      */
  864.  
  865.     line = TkBTreeLineIndex(linePtr);
  866.     TkBTreeStartSearch(textPtr->tree, line, 1, TkBTreeNumLines(textPtr->tree),
  867.         0, (TkTextTag *) NULL, &tagInfo.search);
  868.     TkBTreeNextTag(&tagInfo.search);
  869.     prevPtr = NULL;
  870.     y = dInfoPtr->y;
  871.     maxY = dInfoPtr->maxY;
  872.     while (linePtr != NULL) {
  873.     register DLine *newPtr;
  874.     /*
  875.      * See if the next DLine matches the next line we want to
  876.      * appear on the screen.  If so then we can just use its
  877.      * information.  If not then create new DLine structures
  878.      * for the desired line and insert them into the list.
  879.      */
  880.  
  881.     if ((dlPtr == NULL) || (dlPtr->linePtr != linePtr)) {
  882.         newPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
  883.         if (prevPtr == NULL) {
  884.         dInfoPtr->dLinePtr = newPtr;
  885.         } else {
  886.         prevPtr->nextPtr = newPtr;
  887.         }
  888.         for (dlPtr2 = newPtr; dlPtr2->nextPtr != NULL;
  889.             dlPtr2 = dlPtr2->nextPtr) {
  890.         /* Empty loop body. */
  891.         }
  892.         dlPtr2->nextPtr = dlPtr;
  893.         dlPtr = newPtr;
  894.     }
  895.  
  896.     /*
  897.      * Skip to the next line, and update the y-position while
  898.      * skipping.
  899.      */
  900.  
  901.     do {
  902.         dlPtr->y = y;
  903.         y += dlPtr->height;
  904.         prevPtr = dlPtr;
  905.         dlPtr = dlPtr->nextPtr;
  906.     } while ((dlPtr != NULL) && (dlPtr->linePtr == linePtr));
  907.     linePtr = TkBTreeNextLine(linePtr);
  908.     line++;
  909.  
  910.     /*
  911.      * It's important to have the following check here rather than in
  912.      * the while statement for the loop, so that there's always at least
  913.      * one DLine generated, regardless of how small the window is.  This
  914.      * keeps a lot of other code from breaking.
  915.      */
  916.  
  917.     if (y >= maxY) {
  918.         break;
  919.     }
  920.     }
  921.  
  922.     /*
  923.      * Delete any DLine structures that don't fit on the screen and free
  924.      * up the tag array.
  925.      */
  926.  
  927.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
  928.     if (tagInfo.tagPtrs != NULL) {
  929.     ckfree((char *) tagInfo.tagPtrs);
  930.     }
  931.  
  932.     /*
  933.      * Update the vertical scrollbar, if there is one.
  934.      */
  935.  
  936.     if (textPtr->yScrollCmd != NULL) {
  937.     int numLines, first, result, maxY, height;
  938.     char string[60];
  939.  
  940.     /*
  941.      * Count the number of text lines on the screen.
  942.      */
  943.  
  944.     maxY = 0;
  945.     for (numLines = 0, linePtr = NULL, dlPtr = dInfoPtr->dLinePtr;
  946.         dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  947.         if (dlPtr->linePtr != linePtr) {
  948.         numLines++;
  949.         linePtr = dlPtr->linePtr;
  950.         }
  951.         maxY = dlPtr->y + dlPtr->height;
  952.     }
  953.  
  954.     /*
  955.      * If the screen isn't completely full, then estimate the number of
  956.      * lines that would fit on it if it were full.
  957.      */
  958.  
  959.     height = dInfoPtr->maxY - dInfoPtr->y;
  960.     if (numLines == 0) {
  961.         numLines = height /
  962.             (textPtr->fontPtr->ascent + textPtr->fontPtr->descent);
  963.     } else if (maxY < height) {
  964.         numLines = (numLines * height)/maxY;
  965.     }
  966.     first = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
  967.     sprintf(string, " %d %d %d %d", TkBTreeNumLines(textPtr->tree),
  968.         numLines, first, first+numLines-1);
  969.     result = Tcl_VarEval(textPtr->interp, textPtr->yScrollCmd, string,
  970.         (char *) NULL);
  971.     if (result != TCL_OK) {
  972.         Tcl_AddErrorInfo(textPtr->interp,
  973.             "\n    (horizontal scrolling command executed by text)");
  974.         Tk_BackgroundError(textPtr->interp);
  975.     }
  976.     }
  977. }
  978.  
  979. /*
  980.  *----------------------------------------------------------------------
  981.  *
  982.  * FreeDLines --
  983.  *
  984.  *    This procedure is called to free up all of the resources
  985.  *    associated with one or more DLine structures.
  986.  *
  987.  * Results:
  988.  *    None.
  989.  *
  990.  * Side effects:
  991.  *    Memory gets freed and various other resources are released.
  992.  *
  993.  *----------------------------------------------------------------------
  994.  */
  995.  
  996. static void
  997. FreeDLines(textPtr, firstPtr, lastPtr, unlink)
  998.     TkText *textPtr;            /* Information about overall text
  999.                      * widget. */
  1000.     register DLine *firstPtr;        /* Pointer to first DLine to free up. */
  1001.     DLine *lastPtr;            /* Pointer to DLine just after last
  1002.                      * one to free (NULL means everything
  1003.                      * starting with firstPtr). */
  1004.     int unlink;                /* 1 means DLines are currently linked
  1005.                      * into the list rooted at
  1006.                      * textPtr->dInfoPtr->dLinePtr and
  1007.                      * they have to be unlinked.  0 means
  1008.                      * just free without unlinking. */
  1009. {
  1010.     register Chunk *chunkPtr, *nextChunkPtr;
  1011.     register DLine *nextDLinePtr;
  1012.  
  1013.     if (unlink) {
  1014.     if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
  1015.         textPtr->dInfoPtr->dLinePtr = lastPtr;
  1016.     } else {
  1017.         register DLine *prevPtr;
  1018.         for (prevPtr = textPtr->dInfoPtr->dLinePtr;
  1019.             prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
  1020.         /* Empty loop body. */
  1021.         }
  1022.         prevPtr->nextPtr = lastPtr;
  1023.     }
  1024.     }
  1025.     while (firstPtr != lastPtr) {
  1026.     nextDLinePtr = firstPtr->nextPtr;
  1027.     for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
  1028.         chunkPtr = nextChunkPtr) {
  1029.         FreeStyle(textPtr, chunkPtr->stylePtr);
  1030.         nextChunkPtr = chunkPtr->nextPtr;
  1031.         ckfree((char *) chunkPtr);
  1032.     }
  1033.     ckfree((char *) firstPtr);
  1034.     firstPtr = nextDLinePtr;
  1035.     }
  1036. }
  1037.  
  1038. /*
  1039.  *----------------------------------------------------------------------
  1040.  *
  1041.  * DisplayDLine --
  1042.  *
  1043.  *    This procedure is invoked to draw a single line on the
  1044.  *    screen.
  1045.  *
  1046.  * Results:
  1047.  *    None.
  1048.  *
  1049.  * Side effects:
  1050.  *    The line given by dlPtr is drawn at its correct position in
  1051.  *    textPtr's window.  Note that this is one *display* line, not
  1052.  *    one *text* line.
  1053.  *
  1054.  *----------------------------------------------------------------------
  1055.  */
  1056.  
  1057. static void
  1058. DisplayDLine(textPtr, dlPtr, pixmap)
  1059.     TkText *textPtr;        /* Text widget in which to draw line. */
  1060.     register DLine *dlPtr;    /* Information about line to draw. */
  1061.     Pixmap pixmap;        /* Pixmap to use for double-buffering.
  1062.                  * Caller must make sure it's large enough
  1063.                  * to hold line. */
  1064. {
  1065.     register Style *stylePtr;
  1066.     register StyleValues *sValuePtr;
  1067.     register Chunk *chunkPtr;
  1068.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1069.     Display *display;
  1070.     int width, height, count, x;
  1071.     XFontStruct *fontPtr;
  1072.  
  1073.     /*
  1074.      * First, clear the area of the line to the background color for the
  1075.      * text widget.
  1076.      */
  1077.  
  1078.     display = Tk_Display(textPtr->tkwin);
  1079.     Tk_Fill3DRectangle(display, pixmap, textPtr->border, 0, 0,
  1080.         Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
  1081.  
  1082.     /*
  1083.      * Next, cycle through all of the chunks in the line displaying
  1084.      * backgrounds.  We need to do two passes, one for the backgrounds
  1085.      * and one for the characters, because some characters (e.g. italics
  1086.      * with heavy slants) may cross background boundaries.  If some
  1087.      * backgrounds are drawn after some text, the later backgrounds may
  1088.      * obliterate parts of earlier characters.
  1089.      */
  1090.  
  1091.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1092.         chunkPtr = chunkPtr->nextPtr) {
  1093.  
  1094.     /*
  1095.      * Draw a special background for this chunk if one is specified
  1096.      * in its style.  Two tricks here:
  1097.      * 1. if this is the last chunk in the line then extend the
  1098.      *    background across to the end of the line.
  1099.      * 2. if the background is stippled, then we have to draw the
  1100.      *    stippled part specially, since Tk_Fill3DRectangle doesn't
  1101.      *    do stipples.
  1102.      */
  1103.  
  1104.     stylePtr = chunkPtr->stylePtr;
  1105.     sValuePtr = stylePtr->sValuePtr;
  1106.     if (sValuePtr->border != NULL) {
  1107.         if (chunkPtr->nextPtr != NULL) {
  1108.         width = chunkPtr->nextPtr->x - chunkPtr->x;
  1109.         } else {
  1110.         width = Tk_Width(textPtr->tkwin) - chunkPtr->x;
  1111.         }
  1112.         if (stylePtr->bgGC != NULL) {
  1113.         XFillRectangle(display, pixmap, stylePtr->bgGC, chunkPtr->x,
  1114.             0, (unsigned int) width, (unsigned int) dlPtr->height);
  1115.         Tk_Draw3DRectangle(display, pixmap, sValuePtr->border,
  1116.             chunkPtr->x, 0, width, dlPtr->height,
  1117.             sValuePtr->borderWidth, sValuePtr->relief);
  1118.         } else {
  1119.         Tk_Fill3DRectangle(display, pixmap, sValuePtr->border,
  1120.             chunkPtr->x, 0, width, dlPtr->height,
  1121.             sValuePtr->borderWidth, sValuePtr->relief);
  1122.         }
  1123.     }
  1124.     }
  1125.  
  1126.     /*
  1127.      * If the insertion cursor is displayed on this line, then draw it
  1128.      * now, on top of the background but before the text.  As a special
  1129.      * hack to keep the cursor visible on mono displays, write the default
  1130.      * background in the cursor area (instead of nothing) when the cursor
  1131.      * isn't on.  Otherwise the selection would hide the cursor.
  1132.      */
  1133.  
  1134.     if ((textPtr->insertAnnotPtr->linePtr == dlPtr->linePtr)
  1135.         && (textPtr->state == tkTextNormalUid)
  1136.         && (textPtr->flags & GOT_FOCUS)) {
  1137.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1138.         chunkPtr = chunkPtr->nextPtr) {
  1139.         count = textPtr->insertAnnotPtr->ch
  1140.             - (chunkPtr->text - dlPtr->linePtr->bytes);
  1141.         if (count < 0) {
  1142.         break;
  1143.         }
  1144.         if (count > chunkPtr->numChars) {
  1145.         continue;
  1146.         }
  1147.  
  1148.         /*
  1149.          * Deciding whether to display the cursor just after the last
  1150.          * character in a line is tricky because of various wrap
  1151.          * modes.  Do it unless we're in character wrap mode and
  1152.          * this line wraps, in which case it's better to display the
  1153.          * cursor on the next line.  For word wrap, there's an
  1154.          * undisplayed space character that the user must be able to
  1155.          * position the cursor in front of.  For no wrap, there's no
  1156.          * next line on which to display the cursor.
  1157.          */
  1158.         if ((count == chunkPtr->numChars)
  1159.             && (textPtr->wrapMode == tkTextCharUid)
  1160.             && (chunkPtr->text[count] != '\n')) {
  1161.         continue;
  1162.         }
  1163.         fontPtr = chunkPtr->stylePtr->sValuePtr->fontPtr;
  1164.         TkMeasureChars(fontPtr, chunkPtr->text, count, chunkPtr->x,
  1165.             (int) 1000000, 0, &x);
  1166.         if (textPtr->flags & INSERT_ON) {
  1167.         Tk_Fill3DRectangle(display, pixmap, textPtr->insertBorder,
  1168.             x - textPtr->insertWidth/2,
  1169.             dlPtr->baseline - fontPtr->ascent,
  1170.             textPtr->insertWidth,
  1171.             fontPtr->ascent + fontPtr->descent,
  1172.             textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  1173.         } else if (Tk_GetColorModel(textPtr->tkwin) != TK_COLOR) {
  1174.         Tk_Fill3DRectangle(display, pixmap, textPtr->border,
  1175.             x - textPtr->insertWidth/2,
  1176.             dlPtr->baseline - fontPtr->ascent,
  1177.             textPtr->insertWidth,
  1178.             fontPtr->ascent + fontPtr->descent,
  1179.             0, TK_RELIEF_FLAT);
  1180.         }
  1181.  
  1182.     }
  1183.     }
  1184.  
  1185.     /*
  1186.      * Make another pass through all of the chunks to redraw all of
  1187.      * the text (and underlines, etc., if they're wanted).
  1188.      */
  1189.  
  1190.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1191.         chunkPtr = chunkPtr->nextPtr) {
  1192.     stylePtr = chunkPtr->stylePtr;
  1193.     sValuePtr = stylePtr->sValuePtr;
  1194.     if (chunkPtr->numChars > 0) {
  1195.         TkDisplayChars(display, pixmap, stylePtr->fgGC, sValuePtr->fontPtr,
  1196.             chunkPtr->text, chunkPtr->numChars, chunkPtr->x,
  1197.             dlPtr->baseline, 0);
  1198.         if (sValuePtr->underline) {
  1199.         TkUnderlineChars(display, pixmap, stylePtr->fgGC,
  1200.             sValuePtr->fontPtr, chunkPtr->text, chunkPtr->x,
  1201.             dlPtr->baseline, 0, 0, chunkPtr->numChars-1);
  1202.         }
  1203.     }
  1204.     }
  1205.  
  1206.     /*
  1207.      * Copy the pixmap onto the screen.  If this is the last line on
  1208.      * the screen, only copy a piece of the line, so that it doesn't
  1209.      * overflow into the border area.  Another special trick:  copy the
  1210.      * padding area to the left of the line;  this is because the
  1211.      * insertion cursor sometimes overflows onto that area and we want
  1212.      * to get as much of the cursor as possible.
  1213.      */
  1214.  
  1215.     height = dlPtr->height;
  1216.     if ((height + dlPtr->y) > dInfoPtr->maxY) {
  1217.     height = dInfoPtr->maxY - dlPtr->y;
  1218.     }
  1219.     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin),
  1220.         dInfoPtr->copyGC, dInfoPtr->x - textPtr->padX, 0,
  1221.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  1222.         height, dInfoPtr->x - textPtr->padX, dlPtr->y);
  1223.     linesRedrawn++;
  1224. }
  1225.  
  1226. /*
  1227.  *----------------------------------------------------------------------
  1228.  *
  1229.  * DisplayText --
  1230.  *
  1231.  *    This procedure is invoked as a when-idle handler to update the
  1232.  *    display.  It only redisplays the parts of the text widget that
  1233.  *    are out of date.
  1234.  *
  1235.  * Results:
  1236.  *    None.
  1237.  *
  1238.  * Side effects:
  1239.  *    Information is redrawn on the screen.
  1240.  *
  1241.  *----------------------------------------------------------------------
  1242.  */
  1243.  
  1244. static void
  1245. DisplayText(clientData)
  1246.     ClientData clientData;    /* Information about widget. */
  1247. {
  1248.     register TkText *textPtr = (TkText *) clientData;
  1249.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1250.     Tk_Window tkwin;
  1251.     register DLine *dlPtr;
  1252.     Pixmap pixmap;
  1253.     int maxHeight;
  1254.     int bottomY = 0;        /* Initialization needed only to stop
  1255.                  * compiler warnings. */
  1256.  
  1257.     if ((textPtr->tkwin == NULL) || !Tk_IsMapped(textPtr->tkwin)
  1258.         || (dInfoPtr->maxX <= dInfoPtr->x)
  1259.         || (dInfoPtr->maxY <= dInfoPtr->y)) {
  1260.     goto done;
  1261.     }
  1262.     numRedisplays++;
  1263.  
  1264.     /*
  1265.      * Choose a new current item if that is needed (this could cause
  1266.      * event handlers to be invoked, hence the preserve/release calls
  1267.      * and the loop, since the handlers could conceivably necessitate
  1268.      * yet another current item calculation).  The tkwin check is because
  1269.      * the whole window could go away in the Tk_Release call.
  1270.      */
  1271.  
  1272.     while (dInfoPtr->flags & REPICK_NEEDED) {
  1273.     Tk_Preserve((ClientData) textPtr);
  1274.     dInfoPtr->flags &= ~REPICK_NEEDED;
  1275.     TkTextPickCurrent(textPtr, &textPtr->pickEvent);
  1276.     tkwin = textPtr->tkwin;
  1277.     Tk_Release((ClientData) textPtr);
  1278.     if (tkwin == NULL) {
  1279.         return;
  1280.     }
  1281.     }
  1282.  
  1283.     /*
  1284.      * First recompute what's supposed to be displayed.
  1285.      */
  1286.  
  1287.     UpdateDisplayInfo(textPtr);
  1288.  
  1289.     /*
  1290.      * Redraw the borders if that's needed.
  1291.      */
  1292.  
  1293.     if (dInfoPtr->flags & REDRAW_BORDERS) {
  1294.     Tk_Draw3DRectangle(Tk_Display(textPtr->tkwin),
  1295.         Tk_WindowId(textPtr->tkwin), textPtr->border,
  1296.         0, 0, Tk_Width(textPtr->tkwin), Tk_Height(textPtr->tkwin),
  1297.         textPtr->borderWidth, textPtr->relief);
  1298.     }
  1299.  
  1300.     /*
  1301.      * See if it's possible to bring some parts of the screen up-to-date
  1302.      * by scrolling (copying from other parts of the screen).
  1303.      */
  1304.  
  1305.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  1306.     register DLine *dlPtr2;
  1307.     int offset, height, y;
  1308.  
  1309.     if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
  1310.         || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
  1311.         continue;
  1312.     }
  1313.  
  1314.     /*
  1315.      * This line is already drawn somewhere in the window so it only
  1316.      * needs to be copied to its new location.  See if there's a group
  1317.      * of lines that can all be copied together.
  1318.      */
  1319.  
  1320.     offset = dlPtr->y - dlPtr->oldY;
  1321.     height = dlPtr->height;
  1322.     y = dlPtr->y;
  1323.     for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
  1324.         dlPtr2 = dlPtr2->nextPtr) {
  1325.         if ((dlPtr2->oldY == -1)
  1326.             || ((dlPtr2->oldY + offset) != dlPtr2->y)
  1327.             || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
  1328.         break;
  1329.         }
  1330.         height += dlPtr2->height;
  1331.     }
  1332.  
  1333.     /*
  1334.      * Copy the information and update the lines to show that they've
  1335.      * been copied.  Reduce the height of the area being copied if
  1336.      * necessary to avoid overwriting the border area.
  1337.      */
  1338.  
  1339.     if ((y + height) > dInfoPtr->maxY) {
  1340.         height = dInfoPtr->maxY -y;
  1341.     }
  1342.     XCopyArea(Tk_Display(textPtr->tkwin), Tk_WindowId(textPtr->tkwin),
  1343.         Tk_WindowId(textPtr->tkwin), dInfoPtr->scrollGC,
  1344.         dInfoPtr->x - textPtr->padX, dlPtr->oldY,
  1345.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  1346.         height, dInfoPtr->x - textPtr->padX, y);
  1347.     numCopies++;
  1348.     while (1) {
  1349.         dlPtr->oldY = dlPtr->y;
  1350.         if (dlPtr->nextPtr == dlPtr2) {
  1351.         break;
  1352.         }
  1353.         dlPtr = dlPtr->nextPtr;
  1354.     }
  1355.  
  1356.     /*
  1357.      * Scan through the lines following the copied ones to see if
  1358.      * we just overwrote them with the copy operation.  If so, mark
  1359.      * them for redisplay.
  1360.      */
  1361.  
  1362.     for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  1363.         if ((dlPtr2->oldY != -1)
  1364.             && ((dlPtr2->oldY + dlPtr2->height) > y)
  1365.             && (dlPtr2->oldY < (y + height))) {
  1366.         dlPtr2->oldY = -1;
  1367.         }
  1368.     }
  1369.  
  1370.     /*
  1371.      * It's possible that part of the area copied above was obscured.
  1372.      * To handle this situation, read expose-related events generated
  1373.      * during the XCopyArea operation.
  1374.      */
  1375.  
  1376.     while (1) {
  1377.         XEvent event;
  1378.  
  1379.         XWindowEvent(Tk_Display(textPtr->tkwin),
  1380.             Tk_WindowId(textPtr->tkwin), ExposureMask, &event);
  1381.         if (event.type == NoExpose) {
  1382.         break;
  1383.         } else if (event.type == GraphicsExpose) {
  1384.         TkTextRedrawRegion(textPtr, event.xgraphicsexpose.x,
  1385.             event.xgraphicsexpose.y, event.xgraphicsexpose.width,
  1386.             event.xgraphicsexpose.height);
  1387.         if (event.xgraphicsexpose.count == 0) {
  1388.             damagedCopies++;
  1389.             break;
  1390.         }
  1391.         } else if (event.type == Expose) {
  1392.         /*
  1393.          * A tricky situation.  This event must already have been
  1394.          * queued up before the XCopyArea was issued.  If the area
  1395.          * in this event overlaps the area copied, then some of the
  1396.          * bits that were copied were bogus.  The easiest way to
  1397.          * handle this is to issue two redisplays:  one for the
  1398.          * original area and one for the area shifted as if it was
  1399.          * in the copied area.
  1400.          */
  1401.  
  1402.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  1403.             event.xexpose.y, event.xexpose.width,
  1404.             event.xexpose.height);
  1405.         TkTextRedrawRegion(textPtr, event.xexpose.x,
  1406.             event.xexpose.y + offset, event.xexpose.width,
  1407.             event.xexpose.height);
  1408.         } else {
  1409.         panic("DisplayText received unknown exposure event");
  1410.         }
  1411.     }
  1412.     }
  1413.  
  1414.     /*
  1415.      * Now we have to redraw the lines that couldn't be updated by
  1416.      * scrolling.  First, compute the height of the largest line and
  1417.      * allocate an off-screen pixmap to use for double-buffered
  1418.      * displays.
  1419.      */
  1420.  
  1421.     maxHeight = -1;
  1422.     for (dlPtr = textPtr->dInfoPtr->dLinePtr; dlPtr != NULL;
  1423.         dlPtr = dlPtr->nextPtr) {
  1424.     if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
  1425.         maxHeight = dlPtr->height;
  1426.     }
  1427.     bottomY = dlPtr->y + dlPtr->height;
  1428.     }
  1429.     if (maxHeight >= 0) {
  1430.     pixmap = XCreatePixmap(Tk_Display(textPtr->tkwin),
  1431.         Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
  1432.         maxHeight, Tk_Depth(textPtr->tkwin));
  1433.     for (dlPtr = textPtr->dInfoPtr->dLinePtr;
  1434.         (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
  1435.         dlPtr = dlPtr->nextPtr) {
  1436.         if (dlPtr->oldY != dlPtr->y) {
  1437.         DisplayDLine(textPtr, dlPtr, pixmap);
  1438.         dlPtr->oldY = dlPtr->y;
  1439.         }
  1440.     }
  1441.     XFreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  1442.     }
  1443.  
  1444.     /*
  1445.      * Lastly, see if we need to refresh the part of the window below
  1446.      * the last line of text (if there is any such area).  Refresh the
  1447.      * padding area on the left too, since the insertion cursor might
  1448.      * have been displayed there previously).
  1449.      */
  1450.  
  1451.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  1452.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1453.     }
  1454.     if (bottomY < dInfoPtr->topOfEof) {
  1455.     Tk_Fill3DRectangle(Tk_Display(textPtr->tkwin),
  1456.         Tk_WindowId(textPtr->tkwin), textPtr->border,
  1457.         dInfoPtr->x - textPtr->padX, bottomY,
  1458.         dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  1459.         dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
  1460.     }
  1461.     dInfoPtr->topOfEof = bottomY;
  1462.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  1463.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1464.     }
  1465.  
  1466.     done:
  1467.     dInfoPtr->flags &= ~(REDRAW_PENDING|REDRAW_BORDERS);
  1468. }
  1469.  
  1470. /*
  1471.  *----------------------------------------------------------------------
  1472.  *
  1473.  * TkTextRedrawRegion --
  1474.  *
  1475.  *    This procedure is invoked to schedule a redisplay for a given
  1476.  *    region of a text widget.  The redisplay itself may not occur
  1477.  *    immediately:  it's scheduled as a when-idle handler.
  1478.  *
  1479.  * Results:
  1480.  *    None.
  1481.  *
  1482.  * Side effects:
  1483.  *    Information will eventually be redrawn on the screen.
  1484.  *
  1485.  *----------------------------------------------------------------------
  1486.  */
  1487.  
  1488.     /* ARGSUSED */
  1489. void
  1490. TkTextRedrawRegion(textPtr, x, y, width, height)
  1491.     TkText *textPtr;        /* Widget record for text widget. */
  1492.     int x, y;            /* Coordinates of upper-left corner of area
  1493.                  * to be redrawn, in pixels relative to
  1494.                  * textPtr's window. */
  1495.     int width, height;        /* Width and height of area to be redrawn. */
  1496. {
  1497.     register DLine *dlPtr;
  1498.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1499.     int maxY;
  1500.  
  1501.     /*
  1502.      * Find all lines that overlap the given region and mark them for
  1503.      * redisplay.
  1504.      */
  1505.  
  1506.     maxY = y + height;
  1507.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1508.         dlPtr = dlPtr->nextPtr) {
  1509.     if (((dlPtr->y + dlPtr->height) > y) && (dlPtr->y < maxY)) {
  1510.         dlPtr->oldY = -1;
  1511.     }
  1512.     }
  1513.     if (dInfoPtr->topOfEof < maxY) {
  1514.     dInfoPtr->topOfEof = maxY;
  1515.     }
  1516.  
  1517.     /*
  1518.      * Schedule the redisplay operation if there isn't one already
  1519.      * scheduled.
  1520.      */
  1521.  
  1522.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1523.     dInfoPtr->flags |= REDRAW_PENDING;
  1524.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1525.     }
  1526.     if ((x < dInfoPtr->x) || (y < dInfoPtr->y)
  1527.         || ((x + width) > dInfoPtr->maxX) || (maxY > dInfoPtr->maxY)) {
  1528.     dInfoPtr->flags |= REDRAW_BORDERS;
  1529.     }
  1530. }
  1531.  
  1532. /*
  1533.  *----------------------------------------------------------------------
  1534.  *
  1535.  * TkTextLinesChanged --
  1536.  *
  1537.  *    This procedure is invoked when lines in a text widget are about
  1538.  *    to be modified in a way that changes how they are displayed (e.g.
  1539.  *    characters were inserted, the line was deleted, or tag information
  1540.  *    was changed).  This procedure must be called *before* a change is
  1541.  *    made, so that pointers to TkTextLines in the display information
  1542.  *    are still valid.
  1543.  *
  1544.  * Results:
  1545.  *    None.
  1546.  *
  1547.  * Side effects:
  1548.  *    The indicated lines will be redisplayed at some point in the
  1549.  *    future (the actual redisplay is scheduled as a when-idle handler).
  1550.  *
  1551.  *----------------------------------------------------------------------
  1552.  */
  1553.  
  1554. void
  1555. TkTextLinesChanged(textPtr, first, last)
  1556.     TkText *textPtr;        /* Widget record for text widget. */
  1557.     int first;            /* Index of first line that must be
  1558.                  * redisplayed. */
  1559.     int last;            /* Index of last line to redisplay. */
  1560. {
  1561.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1562.     DLine *firstPtr, *lastPtr;
  1563.  
  1564.     /*
  1565.      * Find the DLines corresponding to first and last+1.
  1566.      */
  1567.  
  1568.     firstPtr = FindDLine(dInfoPtr->dLinePtr, first);
  1569.     if (firstPtr == NULL) {
  1570.     return;
  1571.     }
  1572.     lastPtr = FindDLine(dInfoPtr->dLinePtr, last+1);
  1573.     if (firstPtr == lastPtr) {
  1574.     return;
  1575.     }
  1576.  
  1577.     /*
  1578.      * Delete all the DLines from first up through last (but not including
  1579.      * lastPtr, which points to the first line *outside* the range).
  1580.      */
  1581.  
  1582.     FreeDLines(textPtr, firstPtr, lastPtr, 1);
  1583.  
  1584.     /*
  1585.      * Schedule both a redisplay and a recomputation of display information.
  1586.      */
  1587.  
  1588.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1589.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1590.     }
  1591.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1592. }
  1593.  
  1594. /*
  1595.  *----------------------------------------------------------------------
  1596.  *
  1597.  * TkTextRedrawTag --
  1598.  *
  1599.  *    This procedure is invoked to request a redraw of all characters
  1600.  *    in a given range of characters that have a particular tag on or
  1601.  *    off.  It's called, for example, when characters are tagged or
  1602.  *    untagged, or when tag options change.
  1603.  *
  1604.  * Results:
  1605.  *    None.
  1606.  *
  1607.  * Side effects:
  1608.  *    Information on the screen may be redrawn, and the layout of
  1609.  *    the screen may change.
  1610.  *
  1611.  *----------------------------------------------------------------------
  1612.  */
  1613.  
  1614. void
  1615. TkTextRedrawTag(textPtr, line1, ch1, line2, ch2, tagPtr, withTag)
  1616.     TkText *textPtr;        /* Widget record for text widget. */
  1617.     int line1, ch1;        /* Index of first character in range of
  1618.                  * interest. */
  1619.     int line2, ch2;        /* Index of character just after last one
  1620.                  * in range of interest. */
  1621.     TkTextTag *tagPtr;        /* Information about tag. */
  1622.     int withTag;        /* 1 means redraw characters that have the
  1623.                  * tag, 0 means redraw those without. */
  1624. {
  1625.     register DLine *dlPtr;
  1626.     DLine *endPtr;
  1627.     int topLine, tagOn;
  1628.     TkTextSearch search;
  1629.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1630.  
  1631.     /*
  1632.      * Round up the starting position if it's before the first line
  1633.      * visible on the screen (we only care about what's on the screen).
  1634.      */
  1635.  
  1636.     dlPtr = dInfoPtr->dLinePtr;
  1637.     if (dlPtr == NULL) {
  1638.     return;
  1639.     }
  1640.     topLine = TkBTreeLineIndex(dlPtr->linePtr);
  1641.     if (topLine > line1) {
  1642.     line1 = topLine;
  1643.     ch1 = 0;
  1644.     }
  1645.  
  1646.     /* 
  1647.      * Initialize a search through all transitions on the tag, starting
  1648.      * with the first transition where the tag's current state is different
  1649.      * from what it will eventually be.
  1650.      */
  1651.  
  1652.     TkBTreeStartSearch(textPtr->tree, line1, ch1+1, line2, ch2,
  1653.         tagPtr, &search);
  1654.     if (search.linePtr == NULL) {
  1655.     return;
  1656.     }
  1657.     tagOn = TkBTreeCharTagged(search.linePtr, ch1, tagPtr);
  1658.     if (tagOn != withTag) {
  1659.     if (!TkBTreeNextTag(&search)) {
  1660.         return;
  1661.     }
  1662.     }
  1663.  
  1664.     /*
  1665.      * Each loop through the loop below is for one range of characters
  1666.      * where the tag's current state is different than its eventual
  1667.      * state.  At the top of the loop, search contains information about
  1668.      * the first character in the range.
  1669.      */
  1670.  
  1671.     while (1) {
  1672.     /*
  1673.      * Find the first DLine structure in the range.
  1674.      */
  1675.  
  1676.     dlPtr = FindDLine(dlPtr, search.line1);
  1677.     if (dlPtr == NULL) {
  1678.         break;
  1679.     }
  1680.  
  1681.     /*
  1682.      * Find the first DLine structure that's past the end of the range.
  1683.      */
  1684.  
  1685.     if (TkBTreeNextTag(&search)) {
  1686.         endPtr = FindDLine(dlPtr,
  1687.             (search.ch1 > 0) ? (search.line1 + 1) : search.line1);
  1688.     } else {
  1689.         endPtr = FindDLine(dlPtr,
  1690.             (ch2 > 0) ? (search.line2 + 1) : search.line2);
  1691.     }
  1692.  
  1693.     /*
  1694.      * Delete all of the display lines in the range, so that they'll
  1695.      * be re-layed out and redrawn.
  1696.      */
  1697.  
  1698.     FreeDLines(textPtr, dlPtr, endPtr, 1);
  1699.     dlPtr = endPtr;
  1700.  
  1701.     /*
  1702.      * Find the first text line in the next range.
  1703.      */
  1704.  
  1705.     if (!TkBTreeNextTag(&search)) {
  1706.         break;
  1707.     }
  1708.     }
  1709.  
  1710.     /*
  1711.      * Lastly, schedule a redisplay and layout recalculation if they
  1712.      * aren't already pending.
  1713.      */
  1714.  
  1715.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1716.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1717.     }
  1718.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1719. }
  1720.  
  1721. /*
  1722.  *----------------------------------------------------------------------
  1723.  *
  1724.  * TkTextRelayoutWindow --
  1725.  *
  1726.  *    This procedure is called when something has happened that
  1727.  *    invalidates the whole layout of characters on the screen, such
  1728.  *    as a change in a configuration option for the overall text
  1729.  *    widget or a change in the window size.  It causes all display
  1730.  *    information to be recomputed and the window to be redrawn.
  1731.  *
  1732.  * Results:
  1733.  *    None.
  1734.  *
  1735.  * Side effects:
  1736.  *    All the display information will be recomputed for the window
  1737.  *    and the window will be redrawn.
  1738.  *
  1739.  *----------------------------------------------------------------------
  1740.  */
  1741.  
  1742. void
  1743. TkTextRelayoutWindow(textPtr)
  1744.     TkText *textPtr;        /* Widget record for text widget. */
  1745. {
  1746.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1747.  
  1748.     /*
  1749.      * Throw away all the current layout information.
  1750.      */
  1751.  
  1752.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  1753.     dInfoPtr->dLinePtr = NULL;
  1754.  
  1755.     /*
  1756.      * Recompute some overall things for the layout.
  1757.      */
  1758.  
  1759.     dInfoPtr->x = textPtr->borderWidth + textPtr->padX;
  1760.     dInfoPtr->y = textPtr->borderWidth + textPtr->padY;
  1761.     dInfoPtr->maxX = Tk_Width(textPtr->tkwin) - dInfoPtr->x;
  1762.     dInfoPtr->maxY = Tk_Height(textPtr->tkwin) - dInfoPtr->y;
  1763.     dInfoPtr->topOfEof = dInfoPtr->maxY;
  1764.  
  1765.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1766.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1767.     }
  1768.     dInfoPtr->flags |= REDRAW_PENDING|REDRAW_BORDERS|DINFO_OUT_OF_DATE
  1769.         |REPICK_NEEDED;
  1770. }
  1771.  
  1772. /*
  1773.  *----------------------------------------------------------------------
  1774.  *
  1775.  * TkTextSetView --
  1776.  *
  1777.  *    This procedure is called to specify what lines are to be
  1778.  *    displayed in a text widget.
  1779.  *
  1780.  * Results:
  1781.  *    None.
  1782.  *
  1783.  * Side effects:
  1784.  *    The display will (eventually) be updated so that the line
  1785.  *    given by "line" is visible on the screen at the position
  1786.  *    determined by "pickPlace".
  1787.  *
  1788.  *----------------------------------------------------------------------
  1789.  */
  1790.  
  1791. void
  1792. TkTextSetView(textPtr, line, pickPlace)
  1793.     TkText *textPtr;        /* Widget record for text widget. */
  1794.     int line;            /* Number of line that is to appear somewhere
  1795.                  * in the window.  This line number must
  1796.                  * be a valid one in the file. */
  1797.     int pickPlace;        /* 0 means topLine must appear at top of
  1798.                  * screen.  1 means we get to pick where it
  1799.                  * appears:  minimize screen motion or else
  1800.                  * display line at center of screen. */
  1801. {
  1802.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  1803.     register DLine *dlPtr, *dlPtr2;
  1804.     TkTextLine *linePtr;
  1805.     int curTopLine, curBotLine;
  1806.     int bottomY;
  1807.     TagInfo tagInfo;
  1808. #define CLOSE_LINES 5
  1809.  
  1810.     if (!pickPlace) {
  1811.     /*
  1812.      * The line must go at the top of the screen.  See if the new
  1813.      * topmost line is already somewhere on the screen.  If so then
  1814.      * delete all the DLine structures ahead of it.  Otherwise just
  1815.      * leave all the DLine's alone (if the new topmost line is above
  1816.      * the top of the current window, i.e. we're scrolling back towards
  1817.      * the beginning of the file we may be able to reuse some of the
  1818.      * information that's currently on the screen without redisplaying
  1819.      * it all.
  1820.      */
  1821.  
  1822.     dlPtr = FindDLine(dInfoPtr->dLinePtr, line);
  1823.     if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
  1824.         FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
  1825.     }
  1826.     
  1827.     textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
  1828.     goto scheduleUpdate;
  1829.     }
  1830.  
  1831.     /*
  1832.      * We have to pick where to display the given line.  First, bring
  1833.      * the display information up to date and see if the line will be
  1834.      * completely visible in the current screen configuration.  If so
  1835.      * then there's nothing to do.
  1836.      */
  1837.  
  1838.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  1839.     UpdateDisplayInfo(textPtr);
  1840.     }
  1841.     linePtr = TkBTreeFindLine(textPtr->tree, line);
  1842.     for (dlPtr = dInfoPtr->dLinePtr; ; dlPtr = dlPtr->nextPtr) {
  1843.     if (dlPtr->nextPtr == NULL) {
  1844.         break;
  1845.     }
  1846.     if ((dlPtr->linePtr == linePtr)
  1847.         && (dlPtr->nextPtr->linePtr != linePtr)) {
  1848.         break;
  1849.     }
  1850.     }
  1851.     if ((dlPtr->linePtr == linePtr)
  1852.         && ((dlPtr->y + dlPtr->height) <= dInfoPtr->maxY)) {
  1853.     return;
  1854.     }
  1855.  
  1856.     /*
  1857.      * The desired line isn't already on-screen.  See if it is within
  1858.      * a few lines of the top of the window.  If so then just make it
  1859.      * the top line on the screen.
  1860.      */
  1861.  
  1862.     bottomY = (dInfoPtr->y + dInfoPtr->maxY)/2;
  1863.     curTopLine = TkBTreeLineIndex(dInfoPtr->dLinePtr->linePtr);
  1864.     if (line < curTopLine) {
  1865.     if (line >= (curTopLine-CLOSE_LINES)) {
  1866.         textPtr->topLinePtr = TkBTreeFindLine(textPtr->tree, line);
  1867.         goto scheduleUpdate;
  1868.     }
  1869.     } else {
  1870.     /*
  1871.      * The desired line is below the bottom of the screen.  If it is
  1872.      * within a few lines of the bottom of the screen then position
  1873.      * it at the bottom of the screen. (At this point dlPtr points to
  1874.      * the last line on the screen)
  1875.      */
  1876.     
  1877.     curBotLine = TkBTreeLineIndex(dlPtr->linePtr);
  1878.     if (line <= (curBotLine+5)) {
  1879.         bottomY = dInfoPtr->maxY;
  1880.     }
  1881.     }
  1882.  
  1883.     /*
  1884.      * Our job now is arrange the display so that "line" appears as
  1885.      * low on the screen as possible but with its bottom no lower
  1886.      * than bottomY (bottomY is the bottom of the window if the
  1887.      * desired line is just below the current screen, otherwise it
  1888.      * is the center of the window.  Work upwards (through smaller
  1889.      * line numbers) computing how much space lines take, until we
  1890.      * fine the line that should be at the top of the screen.
  1891.      */
  1892.  
  1893.     for (textPtr->topLinePtr = linePtr = TkBTreeFindLine(textPtr->tree, line);
  1894.         ; line--, textPtr->topLinePtr = linePtr,
  1895.         linePtr = TkBTreeFindLine(textPtr->tree, line)) {
  1896.     tagInfo.tagPtrs = TkBTreeGetTags(textPtr->tree, linePtr, 0,
  1897.         &tagInfo.numTags);
  1898.     tagInfo.arraySize = tagInfo.numTags;
  1899.     TkBTreeStartSearch(textPtr->tree, line, 1, line+1, 0,
  1900.         (TkTextTag *) NULL, &tagInfo.search);
  1901.     TkBTreeNextTag(&tagInfo.search);
  1902.     dlPtr = LayoutLine(textPtr, line, linePtr, &tagInfo);
  1903.     for (dlPtr2 = dlPtr; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  1904.         bottomY -= dlPtr2->height;
  1905.     }
  1906.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 0);
  1907.     if (tagInfo.tagPtrs != NULL) {
  1908.         ckfree((char *) tagInfo.tagPtrs);
  1909.     }
  1910.     if ((bottomY <= 0) || (line <= 0)) {
  1911.         break;
  1912.     }
  1913.     }
  1914.  
  1915.     scheduleUpdate:
  1916.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  1917.     Tk_DoWhenIdle(DisplayText, (ClientData) textPtr);
  1918.     }
  1919.     dInfoPtr->flags |= REDRAW_PENDING|DINFO_OUT_OF_DATE|REPICK_NEEDED;
  1920. }
  1921.  
  1922. /*
  1923.  *----------------------------------------------------------------------
  1924.  *
  1925.  * FindDLine --
  1926.  *
  1927.  *    This procedure is called to find the DLine corresponding to a
  1928.  *    given text line.
  1929.  *
  1930.  * Results:
  1931.  *    The return value is a pointer to the first DLine found in the
  1932.  *    list headed by dlPtr whose line number is greater or equal to
  1933.  *    line.  If there is no such line in the list then NULL is returned.
  1934.  *
  1935.  * Side effects:
  1936.  *    None.
  1937.  *
  1938.  *----------------------------------------------------------------------
  1939.  */
  1940.  
  1941. static DLine *
  1942. FindDLine(dlPtr, line)
  1943.     register DLine *dlPtr;    /* Pointer to first in list of DLines
  1944.                  * to search. */
  1945.     int line;            /* Line number in text that is desired. */
  1946. {
  1947.     TkTextLine *linePtr;
  1948.     int thisLine;
  1949.  
  1950.     if (dlPtr == NULL) {
  1951.     return NULL;
  1952.     }
  1953.     thisLine = TkBTreeLineIndex(dlPtr->linePtr);
  1954.     while (thisLine < line) {
  1955.     /*
  1956.      * This DLine isn't the right one.  Go on to the next DLine
  1957.      * (skipping multiple DLine's for the same text line).
  1958.      */
  1959.  
  1960.     linePtr = dlPtr->linePtr;
  1961.     do {
  1962.         dlPtr = dlPtr->nextPtr;
  1963.         if (dlPtr == NULL) {
  1964.         return NULL;
  1965.         }
  1966.     } while (dlPtr->linePtr == linePtr);
  1967.  
  1968.     /*
  1969.      * Step through text lines, keeping track of the line number
  1970.      * we're on, until we catch up to dlPtr (remember, there could
  1971.      * be gaps in the DLine list where DLine's have been deleted).
  1972.      */
  1973.  
  1974.     do {
  1975.         linePtr = TkBTreeNextLine(linePtr);
  1976.         thisLine++;
  1977.         if (linePtr == NULL) {
  1978.         panic("FindDLine reached end of text");
  1979.         }
  1980.     } while (linePtr != dlPtr->linePtr);
  1981.     }
  1982.     return dlPtr;
  1983. }
  1984.  
  1985. /*
  1986.  *----------------------------------------------------------------------
  1987.  *
  1988.  * TkTextCharAtLoc --
  1989.  *
  1990.  *    Given an (x,y) coordinate on the screen, find the location of
  1991.  *    the closest character to that location.
  1992.  *
  1993.  * Results:
  1994.  *    The return value is a pointer to the text line containing the
  1995.  *    character displayed closest to (x,y).  The value at *chPtr is
  1996.  *    overwritten with the index with that line of the closest
  1997.  *    character.
  1998.  *
  1999.  * Side effects:
  2000.  *    None.
  2001.  *
  2002.  *----------------------------------------------------------------------
  2003.  */
  2004.  
  2005. TkTextLine *
  2006. TkTextCharAtLoc(textPtr, x, y, chPtr)
  2007.     TkText *textPtr;        /* Widget record for text widget. */
  2008.     int x, y;            /* Pixel coordinates of point in widget's
  2009.                  * window. */
  2010.     int *chPtr;            /* Place to store index-within-line of
  2011.                  * closest character. */
  2012. {
  2013.     DInfo *dInfoPtr = textPtr->dInfoPtr;
  2014.     register DLine *dlPtr;
  2015.     register Chunk *chunkPtr;
  2016.     int count;
  2017.     int endX;
  2018.  
  2019.     /*
  2020.      * Make sure that all of the layout information about what's
  2021.      * displayed where on the screen is up-to-date.
  2022.      */
  2023.  
  2024.     if (dInfoPtr->flags & DINFO_OUT_OF_DATE) {
  2025.     UpdateDisplayInfo(textPtr);
  2026.     }
  2027.  
  2028.     /*
  2029.      * If the coordinates are above the top of the window, then adjust
  2030.      * them to refer to the upper-right corner of the window.
  2031.      */
  2032.  
  2033.     if (y < dInfoPtr->y) {
  2034.     y = dInfoPtr->y;
  2035.     x = dInfoPtr->x;
  2036.     } else if (y >= dInfoPtr->topOfEof) {
  2037.     y = dInfoPtr->topOfEof;
  2038.     x = dInfoPtr->maxX;
  2039.     }
  2040.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  2041.     if (y > (dlPtr->y + dlPtr->height)) {
  2042.         if (dlPtr->nextPtr != NULL) {
  2043.         continue;
  2044.         }
  2045.  
  2046.         /*
  2047.          * The coordinates are off the bottom of the window.  Adjust
  2048.          * them to refer to the lower-right character on the window.
  2049.          */
  2050.  
  2051.         y = dlPtr->y;
  2052.         x = dInfoPtr->maxX;
  2053.     }
  2054.     for (chunkPtr = dlPtr->chunkPtr; ; chunkPtr = chunkPtr->nextPtr) {
  2055.         if ((chunkPtr->nextPtr == NULL) || (chunkPtr->nextPtr->x > x)) {
  2056.         break;
  2057.         }
  2058.     }
  2059.     count = TkMeasureChars(chunkPtr->stylePtr->sValuePtr->fontPtr,
  2060.         chunkPtr->text, chunkPtr->numChars, chunkPtr->x, x, 0, &endX);
  2061.     if (count >= chunkPtr->numChars) {
  2062.         /*
  2063.          * The point is off the end of the line.  Return the character
  2064.          * after the last one that fit, unless that character appears
  2065.          * as the first character on the next DLine or unless the last
  2066.          * one that fit extends beyond the edge of the window.
  2067.          */
  2068.  
  2069.         if ((dlPtr->nextPtr != NULL)
  2070.             && (dlPtr->nextPtr->chunkPtr->text
  2071.             == (chunkPtr->text + chunkPtr->numChars))) {
  2072.         count = chunkPtr->numChars-1;
  2073.         }
  2074.         if (endX >= dInfoPtr->maxX) {
  2075.         count = chunkPtr->numChars-1;
  2076.         }
  2077.     }
  2078.     *chPtr = count + (chunkPtr->text - dlPtr->linePtr->bytes);
  2079.     return dlPtr->linePtr;
  2080.     }
  2081.     panic("TkTextCharAtLoc ran out of lines");
  2082.     return (TkTextLine *) NULL;
  2083. }
  2084.