tkTextDisp.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:158k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkTextDisp.c --
  3.  *
  4.  * This module provides facilities to display text widgets.  It is
  5.  * the only place where information is kept about the screen layout
  6.  * of text widgets.
  7.  *
  8.  * Copyright (c) 1992-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tkTextDisp.c,v 1.14.2.5 2007/04/29 02:24:02 das Exp $
  15.  */
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18. #include "tkText.h"
  19. #ifdef __WIN32__
  20. #include "tkWinInt.h"
  21. #endif
  22. #ifdef TK_NO_DOUBLE_BUFFERING
  23. #ifdef MAC_OSX_TK
  24. #include "tkMacOSXInt.h"
  25. #endif
  26. #endif /* TK_NO_DOUBLE_BUFFERING */
  27. /*
  28.  * The following structure describes how to display a range of characters.
  29.  * The information is generated by scanning all of the tags associated
  30.  * with the characters and combining that with default information for
  31.  * the overall widget.  These structures form the hash keys for
  32.  * dInfoPtr->styleTable.
  33.  */
  34. typedef struct StyleValues {
  35.     Tk_3DBorder border; /* Used for drawing background under text.
  36.  * NULL means use widget background. */
  37.     int borderWidth; /* Width of 3-D border for background. */
  38.     int relief; /* 3-D relief for background. */
  39.     Pixmap bgStipple; /* Stipple bitmap for background.  None
  40.  * means draw solid. */
  41.     XColor *fgColor; /* Foreground color for text. */
  42.     Tk_Font tkfont; /* Font for displaying text. */
  43.     Pixmap fgStipple; /* Stipple bitmap for text and other
  44.  * foreground stuff.   None means draw
  45.  * solid.*/
  46.     int justify; /* Justification style for text. */
  47.     int lMargin1; /* Left margin, in pixels, for first display
  48.  * line of each text line. */
  49.     int lMargin2; /* Left margin, in pixels, for second and
  50.  * later display lines of each text line. */
  51.     int offset; /* Offset in pixels of baseline, relative to
  52.  * baseline of line. */
  53.     int overstrike; /* Non-zero means draw overstrike through
  54.  * text. */
  55.     int rMargin; /* Right margin, in pixels. */
  56.     int spacing1; /* Spacing above first dline in text line. */
  57.     int spacing2; /* Spacing between lines of dline. */
  58.     int spacing3; /* Spacing below last dline in text line. */
  59.     TkTextTabArray *tabArrayPtr;/* Locations and types of tab stops (may
  60.  * be NULL). */
  61.     int underline; /* Non-zero means draw underline underneath
  62.  * text. */
  63.     int elide; /* Non-zero means draw text */
  64.     TkWrapMode wrapMode; /* How to handle wrap-around for this tag.
  65.  * One of TEXT_WRAPMODE_CHAR,
  66.  * TEXT_WRAPMODE_NONE or TEXT_WRAPMODE_WORD.*/
  67. } StyleValues;
  68. /*
  69.  * The following structure extends the StyleValues structure above with
  70.  * graphics contexts used to actually draw the characters.  The entries
  71.  * in dInfoPtr->styleTable point to structures of this type.
  72.  */
  73. typedef struct TextStyle {
  74.     int refCount; /* Number of times this structure is
  75.  * referenced in Chunks. */
  76.     GC bgGC; /* Graphics context for background.  None
  77.  * means use widget background. */
  78.     GC fgGC; /* Graphics context for foreground. */
  79.     StyleValues *sValuePtr; /* Raw information from which GCs were
  80.  * derived. */
  81.     Tcl_HashEntry *hPtr; /* Pointer to entry in styleTable.  Used
  82.  * to delete entry. */
  83. } TextStyle;
  84. /*
  85.  * The following macro determines whether two styles have the same
  86.  * background so that, for example, no beveled border should be drawn
  87.  * between them.
  88.  */
  89. #define SAME_BACKGROUND(s1, s2) 
  90.     (((s1)->sValuePtr->border == (s2)->sValuePtr->border) 
  91.         && ((s1)->sValuePtr->borderWidth == (s2)->sValuePtr->borderWidth) 
  92.         && ((s1)->sValuePtr->relief == (s2)->sValuePtr->relief) 
  93.         && ((s1)->sValuePtr->bgStipple == (s2)->sValuePtr->bgStipple))
  94. /*
  95.  * The following macro is used to compare two floating-point numbers
  96.  * to within a certain degree of scale.  Direct comparison fails on
  97.  * processors where the processor and memory representations of FP
  98.  * numbers of a particular precision is different (e.g. Intel)
  99.  */
  100. #define FP_EQUAL_SCALE(double1, double2, scaleFactor) 
  101.     (fabs((double1)-(double2))*((scaleFactor)+1.0) < 0.3)
  102. /*
  103.  * The following structure describes one line of the display, which may
  104.  * be either part or all of one line of the text.
  105.  */
  106. typedef struct DLine {
  107.     TkTextIndex index; /* Identifies first character in text
  108.  * that is displayed on this line. */
  109.     int byteCount; /* Number of bytes accounted for by this
  110.  * display line, including a trailing space
  111.  * or newline that isn't actually displayed. */
  112.     int y; /* Y-position at which line is supposed to
  113.  * be drawn (topmost pixel of rectangular
  114.  * area occupied by line). */
  115.     int oldY; /* Y-position at which line currently
  116.  * appears on display.  -1 means line isn't
  117.  * currently visible on display and must be
  118.  * redrawn.  This is used to move lines by
  119.  * scrolling rather than re-drawing. */
  120.     int height; /* Height of line, in pixels. */
  121.     int baseline; /* Offset of text baseline from y, in
  122.  * pixels. */
  123.     int spaceAbove; /* How much extra space was added to the
  124.  * top of the line because of spacing
  125.  * options.  This is included in height
  126.  * and baseline. */
  127.     int spaceBelow; /* How much extra space was added to the
  128.  * bottom of the line because of spacing
  129.  * options.  This is included in height. */
  130.     int length; /* Total length of line, in pixels. */
  131.     TkTextDispChunk *chunkPtr; /* Pointer to first chunk in list of all
  132.  * of those that are displayed on this
  133.  * line of the screen. */
  134.     struct DLine *nextPtr; /* Next in list of all display lines for
  135.  * this window.   The list is sorted in
  136.  * order from top to bottom.  Note:  the
  137.  * next DLine doesn't always correspond
  138.  * to the next line of text:  (a) can have
  139.  * multiple DLines for one text line, and
  140.  * (b) can have gaps where DLine's have been
  141.  * deleted because they're out of date. */
  142.     int flags; /* Various flag bits:  see below for values. */
  143. } DLine;
  144. /*
  145.  * Flag bits for DLine structures:
  146.  *
  147.  * HAS_3D_BORDER - Non-zero means that at least one of the
  148.  * chunks in this line has a 3D border, so
  149.  * it potentially interacts with 3D borders
  150.  * in neighboring lines (see
  151.  * DisplayLineBackground).
  152.  * NEW_LAYOUT - Non-zero means that the line has been
  153.  * re-layed out since the last time the
  154.  * display was updated.
  155.  * TOP_LINE - Non-zero means that this was the top line
  156.  * in the window the last time that the window
  157.  * was laid out.  This is important because
  158.  * a line may be displayed differently if its
  159.  * at the top or bottom than if it's in the
  160.  * middle (e.g. beveled edges aren't displayed
  161.  * for middle lines if the adjacent line has
  162.  * a similar background).
  163.  * BOTTOM_LINE - Non-zero means that this was the bottom line
  164.  * in the window the last time that the window
  165.  * was laid out.
  166.  * IS_DISABLED - This Dline cannot be edited.
  167.  */
  168. #define HAS_3D_BORDER 1
  169. #define NEW_LAYOUT 2
  170. #define TOP_LINE 4
  171. #define BOTTOM_LINE 8
  172. #define IS_DISABLED    16
  173. /*
  174.  * Overall display information for a text widget:
  175.  */
  176. typedef struct TextDInfo {
  177.     Tcl_HashTable styleTable; /* Hash table that maps from StyleValues
  178.  * to TextStyles for this widget. */
  179.     DLine *dLinePtr; /* First in list of all display lines for
  180.  * this widget, in order from top to bottom. */
  181.     GC copyGC; /* Graphics context for copying from off-
  182.  * screen pixmaps onto screen. */
  183.     GC scrollGC; /* Graphics context for copying from one place
  184.  * in the window to another (scrolling):
  185.  * differs from copyGC in that we need to get
  186.  * GraphicsExpose events. */
  187.     int x; /* First x-coordinate that may be used for
  188.  * actually displaying line information.
  189.  * Leaves space for border, etc. */
  190.     int y; /* First y-coordinate that may be used for
  191.  * actually displaying line information.
  192.  * Leaves space for border, etc. */
  193.     int maxX; /* First x-coordinate to right of available
  194.  * space for displaying lines. */
  195.     int maxY; /* First y-coordinate below available
  196.  * space for displaying lines. */
  197.     int topOfEof; /* Top-most pixel (lowest y-value) that has
  198.  * been drawn in the appropriate fashion for
  199.  * the portion of the window after the last
  200.  * line of the text.  This field is used to
  201.  * figure out when to redraw part or all of
  202.  * the eof field. */
  203.     /*
  204.      * Information used for scrolling:
  205.      */
  206.     int newByteOffset; /* Desired x scroll position, measured as the
  207.  * number of average-size characters off-screen
  208.  * to the left for a line with no left
  209.  * margin. */
  210.     int curPixelOffset; /* Actual x scroll position, measured as the
  211.  * number of pixels off-screen to the left. */
  212.     int maxLength; /* Length in pixels of longest line that's
  213.  * visible in window (length may exceed window
  214.  * size).  If there's no wrapping, this will
  215.  * be zero. */
  216.     double xScrollFirst, xScrollLast;
  217. /* Most recent values reported to horizontal
  218.  * scrollbar;  used to eliminate unnecessary
  219.  * reports. */
  220.     double yScrollFirst, yScrollLast;
  221. /* Most recent values reported to vertical
  222.  * scrollbar;  used to eliminate unnecessary
  223.  * reports. */
  224.     /*
  225.      * The following information is used to implement scanning:
  226.      */
  227.     int scanMarkIndex; /* Byte index of character that was at the
  228.  * left edge of the window when the scan
  229.  * started. */
  230.     int scanMarkX; /* X-position of mouse at time scan started. */
  231.     int scanTotalScroll; /* Total scrolling (in screen lines) that has
  232.  * occurred since scanMarkY was set. */
  233.     int scanMarkY; /* Y-position of mouse at time scan started. */
  234.     /*
  235.      * Miscellaneous information:
  236.      */
  237.     int dLinesInvalidated; /* This value is set to 1 whenever something
  238.  * happens that invalidates information in
  239.  * DLine structures;  if a redisplay
  240.  * is in progress, it will see this and
  241.  * abort the redisplay.  This is needed
  242.  * because, for example, an embedded window
  243.  * could change its size when it is first
  244.  * displayed, invalidating the DLine that
  245.  * is currently being displayed.  If redisplay
  246.  * continues, it will use freed memory and
  247.  * could dump core. */
  248.     int flags; /* Various flag values:  see below for
  249.  * definitions. */
  250. } TextDInfo;
  251. /*
  252.  * In TkTextDispChunk structures for character segments, the clientData
  253.  * field points to one of the following structures:
  254.  */
  255. typedef struct CharInfo {
  256.     int numBytes; /* Number of bytes to display. */
  257.     char chars[4]; /* UTF characters to display.  Actual size
  258.  * will be numBytes, not 4.  THIS MUST BE
  259.  * THE LAST FIELD IN THE STRUCTURE. */
  260. } CharInfo;
  261. /*
  262.  * Flag values for TextDInfo structures:
  263.  *
  264.  * DINFO_OUT_OF_DATE: Non-zero means that the DLine structures
  265.  * for this window are partially or completely
  266.  * out of date and need to be recomputed.
  267.  * REDRAW_PENDING: Means that a when-idle handler has been
  268.  * scheduled to update the display.
  269.  * REDRAW_BORDERS: Means window border or pad area has
  270.  * potentially been damaged and must be redrawn.
  271.  * REPICK_NEEDED: 1 means that the widget has been modified
  272.  * in a way that could change the current
  273.  * character (a different character might be
  274.  * under the mouse cursor now).  Need to
  275.  * recompute the current character before
  276.  * the next redisplay.
  277.  */
  278. #define DINFO_OUT_OF_DATE 1
  279. #define REDRAW_PENDING 2
  280. #define REDRAW_BORDERS 4
  281. #define REPICK_NEEDED 8
  282. /*
  283.  * The following counters keep statistics about redisplay that can be
  284.  * checked to see how clever this code is at reducing redisplays.
  285.  */
  286. static int numRedisplays; /* Number of calls to DisplayText. */
  287. static int linesRedrawn; /* Number of calls to DisplayDLine. */
  288. static int numCopies; /* Number of calls to XCopyArea to copy part
  289.  * of the screen. */
  290. /*
  291.  * Forward declarations for procedures defined later in this file:
  292.  */
  293. static void AdjustForTab _ANSI_ARGS_((TkText *textPtr,
  294.     TkTextTabArray *tabArrayPtr, int index,
  295.     TkTextDispChunk *chunkPtr));
  296. static void CharBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  297.     int index, int y, int lineHeight, int baseline,
  298.     int *xPtr, int *yPtr, int *widthPtr,
  299.     int *heightPtr));
  300. static void CharDisplayProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  301.     int x, int y, int height, int baseline,
  302.     Display *display, Drawable dst, int screenY));
  303. static int CharMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  304.     int x));
  305. static void CharUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  306.     TkTextDispChunk *chunkPtr));
  307. /*
  308.    Definitions of elided procs.
  309.    Compiler can't inline these since we use pointers to these functions.
  310.    ElideDisplayProc, ElideUndisplayProc special-cased for speed,
  311.    as potentially many elided DLine chunks if large, tag toggle-filled
  312.    elided region.
  313. */
  314. static void ElideBboxProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  315.     int index, int y, int lineHeight, int baseline,
  316.     int *xPtr, int *yPtr, int *widthPtr,
  317.     int *heightPtr));
  318. static int ElideMeasureProc _ANSI_ARGS_((TkTextDispChunk *chunkPtr,
  319.     int x));
  320. static void DisplayDLine _ANSI_ARGS_((TkText *textPtr,
  321.     DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  322. static void DisplayLineBackground _ANSI_ARGS_((TkText *textPtr,
  323.     DLine *dlPtr, DLine *prevPtr, Pixmap pixmap));
  324. static void DisplayText _ANSI_ARGS_((ClientData clientData));
  325. static DLine * FindDLine _ANSI_ARGS_((DLine *dlPtr,
  326.     TkTextIndex *indexPtr));
  327. static void FreeDLines _ANSI_ARGS_((TkText *textPtr,
  328.     DLine *firstPtr, DLine *lastPtr, int unlink));
  329. static void FreeStyle _ANSI_ARGS_((TkText *textPtr,
  330.     TextStyle *stylePtr));
  331. static TextStyle * GetStyle _ANSI_ARGS_((TkText *textPtr,
  332.     TkTextIndex *indexPtr));
  333. static void GetXView _ANSI_ARGS_((Tcl_Interp *interp,
  334.     TkText *textPtr, int report));
  335. static void GetYView _ANSI_ARGS_((Tcl_Interp *interp,
  336.     TkText *textPtr, int report));
  337. static DLine * LayoutDLine _ANSI_ARGS_((TkText *textPtr,
  338.     TkTextIndex *indexPtr));
  339. static int MeasureChars _ANSI_ARGS_((Tk_Font tkfont,
  340.     CONST char *source, int maxBytes, int startX,
  341.     int maxX, int tabOrigin, int *nextXPtr));
  342. static void MeasureUp _ANSI_ARGS_((TkText *textPtr,
  343.     TkTextIndex *srcPtr, int distance,
  344.     TkTextIndex *dstPtr));
  345. static int NextTabStop _ANSI_ARGS_((Tk_Font tkfont, int x,
  346.     int tabOrigin));
  347. static void UpdateDisplayInfo _ANSI_ARGS_((TkText *textPtr));
  348. static void ScrollByLines _ANSI_ARGS_((TkText *textPtr,
  349.     int offset));
  350. static int SizeOfTab _ANSI_ARGS_((TkText *textPtr,
  351.     TkTextTabArray *tabArrayPtr, int index, int x,
  352.     int maxX));
  353. static void TextInvalidateRegion _ANSI_ARGS_((TkText *textPtr,
  354.     TkRegion region));
  355. /*
  356.  *----------------------------------------------------------------------
  357.  *
  358.  * TkTextCreateDInfo --
  359.  *
  360.  * This procedure is called when a new text widget is created.
  361.  * Its job is to set up display-related information for the widget.
  362.  *
  363.  * Results:
  364.  * None.
  365.  *
  366.  * Side effects:
  367.  * A TextDInfo data structure is allocated and initialized and attached
  368.  * to textPtr.
  369.  *
  370.  *----------------------------------------------------------------------
  371.  */
  372. void
  373. TkTextCreateDInfo(textPtr)
  374.     TkText *textPtr; /* Overall information for text widget. */
  375. {
  376.     register TextDInfo *dInfoPtr;
  377.     XGCValues gcValues;
  378.     dInfoPtr = (TextDInfo *) ckalloc(sizeof(TextDInfo));
  379.     Tcl_InitHashTable(&dInfoPtr->styleTable, sizeof(StyleValues)/sizeof(int));
  380.     dInfoPtr->dLinePtr = NULL;
  381.     dInfoPtr->copyGC = None;
  382.     gcValues.graphics_exposures = True;
  383.     dInfoPtr->scrollGC = Tk_GetGC(textPtr->tkwin, GCGraphicsExposures,
  384.     &gcValues);
  385.     dInfoPtr->topOfEof = 0;
  386.     dInfoPtr->newByteOffset = 0;
  387.     dInfoPtr->curPixelOffset = 0;
  388.     dInfoPtr->maxLength = 0;
  389.     dInfoPtr->xScrollFirst = -1;
  390.     dInfoPtr->xScrollLast = -1;
  391.     dInfoPtr->yScrollFirst = -1;
  392.     dInfoPtr->yScrollLast = -1;
  393.     dInfoPtr->scanMarkIndex = 0;
  394.     dInfoPtr->scanMarkX = 0;
  395.     dInfoPtr->scanTotalScroll = 0;
  396.     dInfoPtr->scanMarkY = 0;
  397.     dInfoPtr->dLinesInvalidated = 0;
  398.     dInfoPtr->flags = DINFO_OUT_OF_DATE;
  399.     textPtr->dInfoPtr = dInfoPtr;
  400. }
  401. /*
  402.  *----------------------------------------------------------------------
  403.  *
  404.  * TkTextFreeDInfo --
  405.  *
  406.  * This procedure is called to free up all of the private display
  407.  * information kept by this file for a text widget.
  408.  *
  409.  * Results:
  410.  * None.
  411.  *
  412.  * Side effects:
  413.  * Lots of resources get freed.
  414.  *
  415.  *----------------------------------------------------------------------
  416.  */
  417. void
  418. TkTextFreeDInfo(textPtr)
  419.     TkText *textPtr; /* Overall information for text widget. */
  420. {
  421.     register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  422.     /*
  423.      * Be careful to free up styleTable *after* freeing up all the
  424.      * DLines, so that the hash table is still intact to free up the
  425.      * style-related information from the lines.  Once the lines are
  426.      * all free then styleTable will be empty.
  427.      */
  428.     FreeDLines(textPtr, dInfoPtr->dLinePtr, (DLine *) NULL, 1);
  429.     Tcl_DeleteHashTable(&dInfoPtr->styleTable);
  430.     if (dInfoPtr->copyGC != None) {
  431. Tk_FreeGC(textPtr->display, dInfoPtr->copyGC);
  432.     }
  433.     Tk_FreeGC(textPtr->display, dInfoPtr->scrollGC);
  434.     if (dInfoPtr->flags & REDRAW_PENDING) {
  435. Tcl_CancelIdleCall(DisplayText, (ClientData) textPtr);
  436.     }
  437.     ckfree((char *) dInfoPtr);
  438. }
  439. /*
  440.  *----------------------------------------------------------------------
  441.  *
  442.  * GetStyle --
  443.  *
  444.  * This procedure creates all the information needed to display
  445.  * text at a particular location.
  446.  *
  447.  * Results:
  448.  * The return value is a pointer to a TextStyle structure that
  449.  * corresponds to *sValuePtr.
  450.  *
  451.  * Side effects:
  452.  * A new entry may be created in the style table for the widget.
  453.  *
  454.  *----------------------------------------------------------------------
  455.  */
  456. static TextStyle *
  457. GetStyle(textPtr, indexPtr)
  458.     TkText *textPtr; /* Overall information about text widget. */
  459.     TkTextIndex *indexPtr; /* The character in the text for which
  460.  * display information is wanted. */
  461. {
  462.     TkTextTag **tagPtrs;
  463.     register TkTextTag *tagPtr;
  464.     StyleValues styleValues;
  465.     TextStyle *stylePtr;
  466.     Tcl_HashEntry *hPtr;
  467.     int numTags, new, i;
  468.     XGCValues gcValues;
  469.     unsigned long mask;
  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.     int borderPrio, borderWidthPrio, reliefPrio, bgStipplePrio;
  475.     int fgPrio, fontPrio, fgStipplePrio;
  476.     int underlinePrio, elidePrio, justifyPrio, offsetPrio;
  477.     int lMargin1Prio, lMargin2Prio, rMarginPrio;
  478.     int spacing1Prio, spacing2Prio, spacing3Prio;
  479.     int overstrikePrio, tabPrio, wrapPrio;
  480.     /*
  481.      * Find out what tags are present for the character, then compute
  482.      * a StyleValues structure corresponding to those tags (scan
  483.      * through all of the tags, saving information for the highest-
  484.      * priority tag).
  485.      */
  486.     tagPtrs = TkBTreeGetTags(indexPtr, &numTags);
  487.     borderPrio = borderWidthPrio = reliefPrio = bgStipplePrio = -1;
  488.     fgPrio = fontPrio = fgStipplePrio = -1;
  489.     underlinePrio = elidePrio = justifyPrio = offsetPrio = -1;
  490.     lMargin1Prio = lMargin2Prio = rMarginPrio = -1;
  491.     spacing1Prio = spacing2Prio = spacing3Prio = -1;
  492.     overstrikePrio = tabPrio = wrapPrio = -1;
  493.     memset((VOID *) &styleValues, 0, sizeof(StyleValues));
  494.     styleValues.relief = TK_RELIEF_FLAT;
  495.     styleValues.fgColor = textPtr->fgColor;
  496.     styleValues.tkfont = textPtr->tkfont;
  497.     styleValues.justify = TK_JUSTIFY_LEFT;
  498.     styleValues.spacing1 = textPtr->spacing1;
  499.     styleValues.spacing2 = textPtr->spacing2;
  500.     styleValues.spacing3 = textPtr->spacing3;
  501.     styleValues.tabArrayPtr = textPtr->tabArrayPtr;
  502.     styleValues.wrapMode = textPtr->wrapMode;
  503.     styleValues.elide = 0;
  504.     for (i = 0 ; i < numTags; i++) {
  505. tagPtr = tagPtrs[i];
  506. /*
  507.  * Skip the selection tag if we don't have focus,
  508.  * unless we always want to show the selection.
  509.  */
  510. if (
  511. #ifndef MAC_OSX_TK
  512. !TkpAlwaysShowSelection(textPtr->tkwin)
  513. #else
  514. /* Don't show inactive selection in disabled widgets. */
  515. textPtr->state == TK_STATE_DISABLED
  516. #endif
  517. && (tagPtr == textPtr->selTagPtr)
  518. && !(textPtr->flags & GOT_FOCUS)) {
  519.     continue;
  520. }
  521. if ((tagPtr->border != NULL) && (tagPtr->priority > borderPrio)) {
  522.     styleValues.border = tagPtr->border;
  523.     borderPrio = tagPtr->priority;
  524. }
  525. if ((tagPtr->bdString != NULL)
  526. && (tagPtr->priority > borderWidthPrio)) {
  527.     styleValues.borderWidth = tagPtr->borderWidth;
  528.     borderWidthPrio = tagPtr->priority;
  529. }
  530. if ((tagPtr->reliefString != NULL)
  531. && (tagPtr->priority > reliefPrio)) {
  532.     if (styleValues.border == NULL) {
  533. styleValues.border = textPtr->border;
  534.     }
  535.     styleValues.relief = tagPtr->relief;
  536.     reliefPrio = tagPtr->priority;
  537. }
  538. if ((tagPtr->bgStipple != None)
  539. && (tagPtr->priority > bgStipplePrio)) {
  540.     styleValues.bgStipple = tagPtr->bgStipple;
  541.     bgStipplePrio = tagPtr->priority;
  542. }
  543. if ((tagPtr->fgColor != None) && (tagPtr->priority > fgPrio)) {
  544.     styleValues.fgColor = tagPtr->fgColor;
  545.     fgPrio = tagPtr->priority;
  546. }
  547. if ((tagPtr->tkfont != None) && (tagPtr->priority > fontPrio)) {
  548.     styleValues.tkfont = tagPtr->tkfont;
  549.     fontPrio = tagPtr->priority;
  550. }
  551. if ((tagPtr->fgStipple != None)
  552. && (tagPtr->priority > fgStipplePrio)) {
  553.     styleValues.fgStipple = tagPtr->fgStipple;
  554.     fgStipplePrio = tagPtr->priority;
  555. }
  556. if ((tagPtr->justifyString != NULL)
  557. && (tagPtr->priority > justifyPrio)) {
  558.     styleValues.justify = tagPtr->justify;
  559.     justifyPrio = tagPtr->priority;
  560. }
  561. if ((tagPtr->lMargin1String != NULL)
  562. && (tagPtr->priority > lMargin1Prio)) {
  563.     styleValues.lMargin1 = tagPtr->lMargin1;
  564.     lMargin1Prio = tagPtr->priority;
  565. }
  566. if ((tagPtr->lMargin2String != NULL)
  567. && (tagPtr->priority > lMargin2Prio)) {
  568.     styleValues.lMargin2 = tagPtr->lMargin2;
  569.     lMargin2Prio = tagPtr->priority;
  570. }
  571. if ((tagPtr->offsetString != NULL)
  572. && (tagPtr->priority > offsetPrio)) {
  573.     styleValues.offset = tagPtr->offset;
  574.     offsetPrio = tagPtr->priority;
  575. }
  576. if ((tagPtr->overstrikeString != NULL)
  577. && (tagPtr->priority > overstrikePrio)) {
  578.     styleValues.overstrike = tagPtr->overstrike;
  579.     overstrikePrio = tagPtr->priority;
  580. }
  581. if ((tagPtr->rMarginString != NULL)
  582. && (tagPtr->priority > rMarginPrio)) {
  583.     styleValues.rMargin = tagPtr->rMargin;
  584.     rMarginPrio = tagPtr->priority;
  585. }
  586. if ((tagPtr->spacing1String != NULL)
  587. && (tagPtr->priority > spacing1Prio)) {
  588.     styleValues.spacing1 = tagPtr->spacing1;
  589.     spacing1Prio = tagPtr->priority;
  590. }
  591. if ((tagPtr->spacing2String != NULL)
  592. && (tagPtr->priority > spacing2Prio)) {
  593.     styleValues.spacing2 = tagPtr->spacing2;
  594.     spacing2Prio = tagPtr->priority;
  595. }
  596. if ((tagPtr->spacing3String != NULL)
  597. && (tagPtr->priority > spacing3Prio)) {
  598.     styleValues.spacing3 = tagPtr->spacing3;
  599.     spacing3Prio = tagPtr->priority;
  600. }
  601. if ((tagPtr->tabString != NULL)
  602. && (tagPtr->priority > tabPrio)) {
  603.     styleValues.tabArrayPtr = tagPtr->tabArrayPtr;
  604.     tabPrio = tagPtr->priority;
  605. }
  606. if ((tagPtr->underlineString != NULL)
  607. && (tagPtr->priority > underlinePrio)) {
  608.     styleValues.underline = tagPtr->underline;
  609.     underlinePrio = tagPtr->priority;
  610. }
  611. if ((tagPtr->elideString != NULL)
  612. && (tagPtr->priority > elidePrio)) {
  613.     styleValues.elide = tagPtr->elide;
  614.     elidePrio = tagPtr->priority;
  615. }
  616. if ((tagPtr->wrapMode != TEXT_WRAPMODE_NULL)
  617. && (tagPtr->priority > wrapPrio)) {
  618.     styleValues.wrapMode = tagPtr->wrapMode;
  619.     wrapPrio = tagPtr->priority;
  620. }
  621.     }
  622.     if (tagPtrs != NULL) {
  623. ckfree((char *) tagPtrs);
  624.     }
  625.     /*
  626.      * Use an existing style if there's one around that matches.
  627.      */
  628.     hPtr = Tcl_CreateHashEntry(&textPtr->dInfoPtr->styleTable,
  629.     (char *) &styleValues, &new);
  630.     if (!new) {
  631. stylePtr = (TextStyle *) Tcl_GetHashValue(hPtr);
  632. stylePtr->refCount++;
  633. return stylePtr;
  634.     }
  635.     /*
  636.      * No existing style matched.  Make a new one.
  637.      */
  638.     stylePtr = (TextStyle *) ckalloc(sizeof(TextStyle));
  639.     stylePtr->refCount = 1;
  640.     if (styleValues.border != NULL) {
  641. gcValues.foreground = Tk_3DBorderColor(styleValues.border)->pixel;
  642. mask = GCForeground;
  643. if (styleValues.bgStipple != None) {
  644.     gcValues.stipple = styleValues.bgStipple;
  645.     gcValues.fill_style = FillStippled;
  646.     mask |= GCStipple|GCFillStyle;
  647. }
  648. stylePtr->bgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  649.     } else {
  650. stylePtr->bgGC = None;
  651.     }
  652.     mask = GCFont;
  653.     gcValues.font = Tk_FontId(styleValues.tkfont);
  654.     mask |= GCForeground;
  655.     gcValues.foreground = styleValues.fgColor->pixel;
  656.     if (styleValues.fgStipple != None) {
  657. gcValues.stipple = styleValues.fgStipple;
  658. gcValues.fill_style = FillStippled;
  659. mask |= GCStipple|GCFillStyle;
  660.     }
  661.     stylePtr->fgGC = Tk_GetGC(textPtr->tkwin, mask, &gcValues);
  662.     stylePtr->sValuePtr = (StyleValues *)
  663.     Tcl_GetHashKey(&textPtr->dInfoPtr->styleTable, hPtr);
  664.     stylePtr->hPtr = hPtr;
  665.     Tcl_SetHashValue(hPtr, stylePtr);
  666.     return stylePtr;
  667. }
  668. /*
  669.  *----------------------------------------------------------------------
  670.  *
  671.  * FreeStyle --
  672.  *
  673.  * This procedure is called when a TextStyle structure is no longer
  674.  * needed.  It decrements the reference count and frees up the
  675.  * space for the style structure if the reference count is 0.
  676.  *
  677.  * Results:
  678.  * None.
  679.  *
  680.  * Side effects:
  681.  * The storage and other resources associated with the style
  682.  * are freed up if no-one's still using it.
  683.  *
  684.  *----------------------------------------------------------------------
  685.  */
  686. static void
  687. FreeStyle(textPtr, stylePtr)
  688.     TkText *textPtr; /* Information about overall widget. */
  689.     register TextStyle *stylePtr; /* Information about style to free. */
  690. {
  691.     stylePtr->refCount--;
  692.     if (stylePtr->refCount == 0) {
  693. if (stylePtr->bgGC != None) {
  694.     Tk_FreeGC(textPtr->display, stylePtr->bgGC);
  695. }
  696. if (stylePtr->fgGC != None) {
  697.     Tk_FreeGC(textPtr->display, stylePtr->fgGC);
  698. }
  699. Tcl_DeleteHashEntry(stylePtr->hPtr);
  700. ckfree((char *) stylePtr);
  701.     }
  702. }
  703. /*
  704.  *----------------------------------------------------------------------
  705.  *
  706.  * LayoutDLine --
  707.  *
  708.  * This procedure generates a single DLine structure for a display
  709.  * line whose leftmost character is given by indexPtr.
  710.  *
  711.  * Results:
  712.  * The return value is a pointer to a DLine structure desribing the
  713.  * display line.  All fields are filled in and correct except for
  714.  * y and nextPtr.
  715.  *
  716.  * Side effects:
  717.  * Storage is allocated for the new DLine.
  718.  *
  719.  *----------------------------------------------------------------------
  720.  */
  721. static DLine *
  722. LayoutDLine(textPtr, indexPtr)
  723.     TkText *textPtr; /* Overall information about text widget. */
  724.     TkTextIndex *indexPtr; /* Beginning of display line.  May not
  725.  * necessarily point to a character segment. */
  726. {
  727.     register DLine *dlPtr; /* New display line. */
  728.     TkTextSegment *segPtr; /* Current segment in text. */
  729.     TkTextDispChunk *lastChunkPtr; /* Last chunk allocated so far
  730.  * for line. */
  731.     TkTextDispChunk *chunkPtr; /* Current chunk. */
  732.     TkTextIndex curIndex;
  733.     TkTextDispChunk *breakChunkPtr; /* Chunk containing best word break
  734.  * point, if any. */
  735.     TkTextIndex breakIndex; /* Index of first character in
  736.  * breakChunkPtr. */
  737.     int breakByteOffset; /* Byte offset of character within
  738.  * breakChunkPtr just to right of best
  739.  * break point. */
  740.     int noCharsYet; /* Non-zero means that no characters
  741.  * have been placed on the line yet. */
  742.     int justify; /* How to justify line: taken from
  743.  * style for the first character in
  744.  * line. */
  745.     int jIndent; /* Additional indentation (beyond
  746.  * margins) due to justification. */
  747.     int rMargin; /* Right margin width for line. */
  748.     TkWrapMode wrapMode; /* Wrap mode to use for this line. */
  749.     int x = 0, maxX = 0; /* Initializations needed only to
  750.  * stop compiler warnings. */
  751.     int wholeLine; /* Non-zero means this display line
  752.  * runs to the end of the text line. */
  753.     int tabIndex; /* Index of the current tab stop. */
  754.     int gotTab; /* Non-zero means the current chunk
  755.  * contains a tab. */
  756.     TkTextDispChunk *tabChunkPtr; /* Pointer to the chunk containing
  757.  * the previous tab stop. */
  758.     int maxBytes; /* Maximum number of bytes to
  759.  * include in this chunk. */
  760.     TkTextTabArray *tabArrayPtr; /* Tab stops for line; taken from
  761.  * style for the first character on
  762.  * line. */
  763.     int tabSize; /* Number of pixels consumed by current
  764.  * tab stop. */
  765.     TkTextDispChunk *lastCharChunkPtr; /* Pointer to last chunk in display
  766.  * lines with numBytes > 0.  Used to
  767.  * drop 0-sized chunks from the end
  768.  * of the line. */
  769.     int byteOffset, ascent, descent, code, elide, elidesize;
  770.     StyleValues *sValuePtr;
  771.     /*
  772.      * Create and initialize a new DLine structure.
  773.      */
  774.     dlPtr = (DLine *) ckalloc(sizeof(DLine));
  775.     dlPtr->index = *indexPtr;
  776.     dlPtr->byteCount = 0;
  777.     dlPtr->y = 0;
  778.     dlPtr->oldY = -1;
  779.     dlPtr->height = 0;
  780.     dlPtr->baseline = 0;
  781.     dlPtr->chunkPtr = NULL;
  782.     dlPtr->nextPtr = NULL;
  783.     dlPtr->flags = NEW_LAYOUT;
  784.     /*
  785.      * Special case entirely elide line as there may be 1000s or more
  786.      */
  787.     elide = TkTextIsElided(textPtr, indexPtr); /* save a malloc */
  788.     if (elide && indexPtr->byteIndex==0) {
  789. maxBytes = 0;
  790. for (segPtr = indexPtr->linePtr->segPtr;
  791.      elide && (segPtr != NULL);
  792.      segPtr = segPtr->nextPtr) {
  793.     if ((elidesize = segPtr->size) > 0) {
  794. maxBytes += elidesize;
  795. /*
  796.  * If have we have a tag toggle, there is a chance
  797.  * that invisibility state changed, so bail out
  798.  */
  799.     } else if ((segPtr->typePtr == &tkTextToggleOffType)
  800.     || (segPtr->typePtr == &tkTextToggleOnType)) {
  801. if (segPtr->body.toggle.tagPtr->elideString != NULL) {
  802.     elide = (segPtr->typePtr == &tkTextToggleOffType)
  803. ^ segPtr->body.toggle.tagPtr->elide;
  804. }
  805.     }
  806. }
  807. if (elide) {
  808.     dlPtr->byteCount = maxBytes;
  809.     dlPtr->spaceAbove = dlPtr->spaceBelow = dlPtr->length = 0;
  810.     return dlPtr;
  811. }
  812.     }
  813.     /*
  814.      * Each iteration of the loop below creates one TkTextDispChunk for
  815.      * the new display line.  The line will always have at least one
  816.      * chunk (for the newline character at the end, if there's nothing
  817.      * else available).
  818.      */
  819.     curIndex = *indexPtr;
  820.     lastChunkPtr = NULL;
  821.     chunkPtr = NULL;
  822.     noCharsYet = 1;
  823.     elide = 0;
  824.     breakChunkPtr = NULL;
  825.     breakByteOffset = 0;
  826.     justify = TK_JUSTIFY_LEFT;
  827.     tabIndex = -1;
  828.     tabChunkPtr = NULL;
  829.     tabArrayPtr = NULL;
  830.     rMargin = 0;
  831.     wrapMode = TEXT_WRAPMODE_CHAR;
  832.     tabSize = 0;
  833.     lastCharChunkPtr = NULL;
  834.     /*
  835.      * Find the first segment to consider for the line.  Can't call
  836.      * TkTextIndexToSeg for this because it won't return a segment
  837.      * with zero size (such as the insertion cursor's mark).
  838.      */
  839.     for (byteOffset = curIndex.byteIndex, segPtr = curIndex.linePtr->segPtr;
  840.  (byteOffset > 0) && (byteOffset >= segPtr->size);
  841.  byteOffset -= segPtr->size, segPtr = segPtr->nextPtr) {
  842. /* Empty loop body. */
  843.     }
  844.     while (segPtr != NULL) {
  845. /*
  846.  * Every line still gets at least one chunk due to expectations
  847.  * in the rest of the code, but we are able to skip elided portions
  848.  * of the line quickly.
  849.  * If current chunk is elided and last chunk was too, coalese
  850.  */
  851. if (elide && (lastChunkPtr != NULL)
  852. && (lastChunkPtr->displayProc == NULL /*ElideDisplayProc*/)) {
  853.     if ((elidesize = segPtr->size - byteOffset) > 0) {
  854. curIndex.byteIndex += elidesize;
  855. lastChunkPtr->numBytes += elidesize;
  856. breakByteOffset = lastChunkPtr->breakIndex = lastChunkPtr->numBytes;
  857. /*
  858.  * If have we have a tag toggle, there is a chance
  859.  * that invisibility state changed, so bail out
  860.  */
  861.     } else if ((segPtr->typePtr == &tkTextToggleOffType)
  862.     || (segPtr->typePtr == &tkTextToggleOnType)) {
  863. if (segPtr->body.toggle.tagPtr->elideString != NULL) {
  864.     elide = (segPtr->typePtr == &tkTextToggleOffType)
  865. ^ segPtr->body.toggle.tagPtr->elide;
  866. }
  867.     }
  868.     byteOffset = 0;
  869.     segPtr = segPtr->nextPtr;
  870.     if (segPtr == NULL && chunkPtr != NULL) {
  871. ckfree((char *) chunkPtr);
  872.     }
  873.     continue;
  874. }
  875. if (segPtr->typePtr->layoutProc == NULL) {
  876.     segPtr = segPtr->nextPtr;
  877.     byteOffset = 0;
  878.     continue;
  879. }
  880. if (chunkPtr == NULL) {
  881.     chunkPtr = (TkTextDispChunk *) ckalloc(sizeof(TkTextDispChunk));
  882.     chunkPtr->nextPtr = NULL;
  883. }
  884. chunkPtr->stylePtr = GetStyle(textPtr, &curIndex);
  885. elide = chunkPtr->stylePtr->sValuePtr->elide;
  886. /*
  887.  * Save style information such as justification and indentation,
  888.  * up until the first character is encountered, then retain that
  889.  * information for the rest of the line.
  890.  */
  891. if (noCharsYet) {
  892.     tabArrayPtr = chunkPtr->stylePtr->sValuePtr->tabArrayPtr;
  893.     justify = chunkPtr->stylePtr->sValuePtr->justify;
  894.     rMargin = chunkPtr->stylePtr->sValuePtr->rMargin;
  895.     wrapMode = chunkPtr->stylePtr->sValuePtr->wrapMode;
  896.     x = ((curIndex.byteIndex == 0)
  897.     ? chunkPtr->stylePtr->sValuePtr->lMargin1
  898.     : chunkPtr->stylePtr->sValuePtr->lMargin2);
  899.     if (wrapMode == TEXT_WRAPMODE_NONE) {
  900. maxX = -1;
  901.     } else {
  902. maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x
  903. - rMargin;
  904. if (maxX < x) {
  905.     maxX = x;
  906. }
  907.     }
  908. }
  909. /*
  910.  * See if there is a tab in the current chunk; if so, only
  911.  * layout characters up to (and including) the tab.
  912.  */
  913. gotTab = 0;
  914. maxBytes = segPtr->size - byteOffset;
  915. if (!elide && justify == TK_JUSTIFY_LEFT) {
  916.     if (segPtr->typePtr == &tkTextCharType) {
  917. char *p;
  918. for (p = segPtr->body.chars  + byteOffset; *p != 0; p++) {
  919.     if (*p == 't') {
  920. maxBytes = (p + 1 - segPtr->body.chars) - byteOffset;
  921. gotTab = 1;
  922. break;
  923.     }
  924. }
  925.     }
  926. }
  927. chunkPtr->x = x;
  928. if (elide && maxBytes) {
  929.     /* don't free style here, as other code expects to be able to do that */
  930.     /*breakByteOffset =*/ chunkPtr->breakIndex = chunkPtr->numBytes = maxBytes;
  931.     chunkPtr->width = 0;
  932.     chunkPtr->minAscent = chunkPtr->minDescent = chunkPtr->minHeight = 0;
  933.     /* would just like to point to canonical empty chunk */
  934.     chunkPtr->displayProc = (Tk_ChunkDisplayProc *) NULL;
  935.     chunkPtr->undisplayProc = (Tk_ChunkUndisplayProc *) NULL;
  936.     chunkPtr->measureProc = ElideMeasureProc;
  937.     chunkPtr->bboxProc = ElideBboxProc;
  938.     code = 1;
  939. } else
  940. code = (*segPtr->typePtr->layoutProc)(textPtr, &curIndex, segPtr,
  941. byteOffset, maxX-tabSize, maxBytes, noCharsYet, wrapMode,
  942. chunkPtr);
  943. if (code <= 0) {
  944.     FreeStyle(textPtr, chunkPtr->stylePtr);
  945.     if (code < 0) {
  946. /*
  947.  * This segment doesn't wish to display itself (e.g. most
  948.  * marks).
  949.  */
  950. segPtr = segPtr->nextPtr;
  951. byteOffset = 0;
  952. continue;
  953.     }
  954.     /*
  955.      * No characters from this segment fit in the window: this
  956.      * means we're at the end of the display line.
  957.      */
  958.     if (chunkPtr != NULL) {
  959. ckfree((char *) chunkPtr);
  960.     }
  961.     break;
  962. }
  963. if (chunkPtr->numBytes > 0) {
  964.     noCharsYet = 0;
  965.     lastCharChunkPtr = chunkPtr;
  966. }
  967. if (lastChunkPtr == NULL) {
  968.     dlPtr->chunkPtr = chunkPtr;
  969. } else {
  970.     lastChunkPtr->nextPtr = chunkPtr;
  971. }
  972. lastChunkPtr = chunkPtr;
  973. x += chunkPtr->width;
  974. if (chunkPtr->breakIndex > 0) {
  975.     breakByteOffset = chunkPtr->breakIndex;
  976.     breakIndex = curIndex;
  977.     breakChunkPtr = chunkPtr;
  978. }
  979. if (chunkPtr->numBytes != maxBytes) {
  980.     break;
  981. }
  982. /*
  983.  * If we're at a new tab, adjust the layout for all the chunks
  984.  * pertaining to the previous tab.  Also adjust the amount of
  985.  * space left in the line to account for space that will be eaten
  986.  * up by the tab.
  987.  */
  988. if (gotTab) {
  989.     if (tabIndex >= 0) {
  990. AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  991. x = chunkPtr->x + chunkPtr->width;
  992.     }
  993.     tabIndex++;
  994.     tabChunkPtr = chunkPtr;
  995.     tabSize = SizeOfTab(textPtr, tabArrayPtr, tabIndex, x, maxX);
  996.     if ((maxX >= 0) && (tabSize >= maxX - x)) {
  997. break;
  998.     }
  999. }
  1000. curIndex.byteIndex += chunkPtr->numBytes;
  1001. byteOffset += chunkPtr->numBytes;
  1002. if (byteOffset >= segPtr->size) {
  1003.     byteOffset = 0;
  1004.     segPtr = segPtr->nextPtr;
  1005. }
  1006. chunkPtr = NULL;
  1007.     }
  1008.     if (noCharsYet) {
  1009. panic("LayoutDLine couldn't place any characters on a line");
  1010.     }
  1011.     wholeLine = (segPtr == NULL);
  1012.     /*
  1013.      * We're at the end of the display line.  Throw away everything
  1014.      * after the most recent word break, if there is one;  this may
  1015.      * potentially require the last chunk to be layed out again.
  1016.      */
  1017.     if (breakChunkPtr == NULL) {
  1018. /*
  1019.  * This code makes sure that we don't accidentally display
  1020.  * chunks with no characters at the end of the line (such as
  1021.  * the insertion cursor).  These chunks belong on the next
  1022.  * line.  So, throw away everything after the last chunk that
  1023.  * has characters in it.
  1024.  */
  1025. breakChunkPtr = lastCharChunkPtr;
  1026. breakByteOffset = breakChunkPtr->numBytes;
  1027.     }
  1028.     if ((breakChunkPtr != NULL) && ((lastChunkPtr != breakChunkPtr)
  1029.     || (breakByteOffset != lastChunkPtr->numBytes))) {
  1030. while (1) {
  1031.     chunkPtr = breakChunkPtr->nextPtr;
  1032.     if (chunkPtr == NULL) {
  1033. break;
  1034.     }
  1035.     FreeStyle(textPtr, chunkPtr->stylePtr);
  1036.     breakChunkPtr->nextPtr = chunkPtr->nextPtr;
  1037.     (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  1038.     ckfree((char *) chunkPtr);
  1039. }
  1040. if (breakByteOffset != breakChunkPtr->numBytes) {
  1041.     (*breakChunkPtr->undisplayProc)(textPtr, breakChunkPtr);
  1042.     segPtr = TkTextIndexToSeg(&breakIndex, &byteOffset);
  1043.     (*segPtr->typePtr->layoutProc)(textPtr, &breakIndex,
  1044.     segPtr, byteOffset, maxX, breakByteOffset, 0, 
  1045.     wrapMode, breakChunkPtr);
  1046. }
  1047. lastChunkPtr = breakChunkPtr;
  1048. wholeLine = 0;
  1049.     }
  1050.     /*
  1051.      * Make tab adjustments for the last tab stop, if there is one.
  1052.      */
  1053.     if ((tabIndex >= 0) && (tabChunkPtr != NULL)) {
  1054. AdjustForTab(textPtr, tabArrayPtr, tabIndex, tabChunkPtr);
  1055.     }
  1056.     /*
  1057.      * Make one more pass over the line to recompute various things
  1058.      * like its height, length, and total number of bytes.  Also
  1059.      * modify the x-locations of chunks to reflect justification.
  1060.      * If we're not wrapping, I'm not sure what is the best way to
  1061.      * handle left and center justification:  should the total length,
  1062.      * for purposes of justification, be (a) the window width, (b)
  1063.      * the length of the longest line in the window, or (c) the length
  1064.      * of the longest line in the text?  (c) isn't available, (b) seems
  1065.      * weird, since it can change with vertical scrolling, so (a) is
  1066.      * what is implemented below.
  1067.      */
  1068.     if (wrapMode == TEXT_WRAPMODE_NONE) {
  1069. maxX = textPtr->dInfoPtr->maxX - textPtr->dInfoPtr->x - rMargin;
  1070.     }
  1071.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  1072.     if (justify == TK_JUSTIFY_LEFT) {
  1073. jIndent = 0;
  1074.     } else if (justify == TK_JUSTIFY_RIGHT) {
  1075. jIndent = maxX - dlPtr->length;
  1076.     } else {
  1077. jIndent = (maxX - dlPtr->length)/2;
  1078.     }
  1079.     ascent = descent = 0;
  1080.     for (chunkPtr = dlPtr->chunkPtr; chunkPtr != NULL;
  1081.     chunkPtr = chunkPtr->nextPtr) {
  1082. chunkPtr->x += jIndent;
  1083. dlPtr->byteCount += chunkPtr->numBytes;
  1084. if (chunkPtr->minAscent > ascent) {
  1085.     ascent = chunkPtr->minAscent;
  1086. }
  1087. if (chunkPtr->minDescent > descent) {
  1088.     descent = chunkPtr->minDescent;
  1089. }
  1090. if (chunkPtr->minHeight > dlPtr->height) {
  1091.     dlPtr->height = chunkPtr->minHeight;
  1092. }
  1093. sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1094. if ((sValuePtr->borderWidth > 0)
  1095. && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1096.     dlPtr->flags |= HAS_3D_BORDER;
  1097. }
  1098.     }
  1099.     if (dlPtr->height < (ascent + descent)) {
  1100. dlPtr->height = ascent + descent;
  1101. dlPtr->baseline = ascent;
  1102.     } else {
  1103. dlPtr->baseline = ascent + (dlPtr->height - ascent - descent)/2;
  1104.     }
  1105.     sValuePtr = dlPtr->chunkPtr->stylePtr->sValuePtr;
  1106.     if (dlPtr->index.byteIndex == 0) {
  1107. dlPtr->spaceAbove = sValuePtr->spacing1;
  1108.     } else {
  1109. dlPtr->spaceAbove = sValuePtr->spacing2 - sValuePtr->spacing2/2;
  1110.     }
  1111.     if (wholeLine) {
  1112. dlPtr->spaceBelow = sValuePtr->spacing3;
  1113.     } else {
  1114. dlPtr->spaceBelow = sValuePtr->spacing2/2;
  1115.     }
  1116.     dlPtr->height += dlPtr->spaceAbove + dlPtr->spaceBelow;
  1117.     dlPtr->baseline += dlPtr->spaceAbove;
  1118.     /*
  1119.      * Recompute line length:  may have changed because of justification.
  1120.      */
  1121.     dlPtr->length = lastChunkPtr->x + lastChunkPtr->width;
  1122.     return dlPtr;
  1123. }
  1124. /*
  1125.  *----------------------------------------------------------------------
  1126.  *
  1127.  * UpdateDisplayInfo --
  1128.  *
  1129.  * This procedure is invoked to recompute some or all of the
  1130.  * DLine structures for a text widget.  At the time it is called
  1131.  * the DLine structures still left in the widget are guaranteed
  1132.  * to be correct except that (a) the y-coordinates aren't
  1133.  * necessarily correct, (b) there may be missing structures
  1134.  * (the DLine structures get removed as soon as they are potentially
  1135.  * out-of-date), and (c) DLine structures that don't start at the
  1136.  * beginning of a line may be incorrect if previous information in
  1137.  * the same line changed size in a way that moved a line boundary
  1138.  * (DLines for any info that changed will have been deleted, but
  1139.  * not DLines for unchanged info in the same text line).
  1140.  *
  1141.  * Results:
  1142.  * None.
  1143.  *
  1144.  * Side effects:
  1145.  * Upon return, the DLine information for textPtr correctly reflects
  1146.  * the positions where characters will be displayed.  However, this
  1147.  * procedure doesn't actually bring the display up-to-date.
  1148.  *
  1149.  *----------------------------------------------------------------------
  1150.  */
  1151. static void
  1152. UpdateDisplayInfo(textPtr)
  1153.     TkText *textPtr; /* Text widget to update. */
  1154. {
  1155.     register TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1156.     register DLine *dlPtr, *prevPtr;
  1157.     TkTextIndex index;
  1158.     TkTextLine *lastLinePtr;
  1159.     int y, maxY, pixelOffset, maxOffset;
  1160.     if (!(dInfoPtr->flags & DINFO_OUT_OF_DATE)) {
  1161. return;
  1162.     }
  1163.     dInfoPtr->flags &= ~DINFO_OUT_OF_DATE;
  1164.     /*
  1165.      * Delete any DLines that are now above the top of the window.
  1166.      */
  1167.     index = textPtr->topIndex;
  1168.     dlPtr = FindDLine(dInfoPtr->dLinePtr, &index);
  1169.     if ((dlPtr != NULL) && (dlPtr != dInfoPtr->dLinePtr)) {
  1170. FreeDLines(textPtr, dInfoPtr->dLinePtr, dlPtr, 1);
  1171.     }
  1172.     /*
  1173.      *--------------------------------------------------------------
  1174.      * Scan through the contents of the window from top to bottom,
  1175.      * recomputing information for lines that are missing.
  1176.      *--------------------------------------------------------------
  1177.      */
  1178.     lastLinePtr = TkBTreeFindLine(textPtr->tree,
  1179.     TkBTreeNumLines(textPtr->tree));
  1180.     dlPtr = dInfoPtr->dLinePtr;
  1181.     prevPtr = NULL;
  1182.     y = dInfoPtr->y;
  1183.     maxY = dInfoPtr->maxY;
  1184.     while (1) {
  1185. register DLine *newPtr;
  1186. if (index.linePtr == lastLinePtr) {
  1187.     break;
  1188. }
  1189. /*
  1190.  * There are three possibilities right now:
  1191.  * (a) the next DLine (dlPtr) corresponds exactly to the next
  1192.  *     information we want to display: just use it as-is.
  1193.  * (b) the next DLine corresponds to a different line, or to
  1194.  *     a segment that will be coming later in the same line:
  1195.  *     leave this DLine alone in the hopes that we'll be able
  1196.  *     to use it later, then create a new DLine in front of
  1197.  *     it.
  1198.  * (c) the next DLine corresponds to a segment in the line we
  1199.  *     want, but it's a segment that has already been processed
  1200.  *     or will never be processed.  Delete the DLine and try
  1201.  *     again.
  1202.  *
  1203.  * One other twist on all this.  It's possible for 3D borders
  1204.  * to interact between lines (see DisplayLineBackground) so if
  1205.  * a line is relayed out and has styles with 3D borders, its
  1206.  * neighbors have to be redrawn if they have 3D borders too,
  1207.  * since the interactions could have changed (the neighbors
  1208.  * don't have to be relayed out, just redrawn).
  1209.  */
  1210. if ((dlPtr == NULL) || (dlPtr->index.linePtr != index.linePtr)) {
  1211.     /*
  1212.      * Case (b) -- must make new DLine.
  1213.      */
  1214.     makeNewDLine:
  1215.     if (tkTextDebug) {
  1216. char string[TK_POS_CHARS];
  1217. /*
  1218.  * Debugging is enabled, so keep a log of all the lines
  1219.  * that were re-layed out.  The test suite uses this
  1220.  * information.
  1221.  */
  1222. TkTextPrintIndex(&index, string);
  1223. Tcl_SetVar2(textPtr->interp, "tk_textRelayout", (char *) NULL,
  1224. string,
  1225. TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1226.     }
  1227.     newPtr = LayoutDLine(textPtr, &index);
  1228.     if (prevPtr == NULL) {
  1229. dInfoPtr->dLinePtr = newPtr;
  1230.     } else {
  1231. prevPtr->nextPtr = newPtr;
  1232. if (prevPtr->flags & HAS_3D_BORDER) {
  1233.     prevPtr->oldY = -1;
  1234. }
  1235.     }
  1236.     newPtr->nextPtr = dlPtr;
  1237.     dlPtr = newPtr;
  1238. } else {
  1239.     /*
  1240.      * DlPtr refers to the line we want.  Next check the
  1241.      * index within the line.
  1242.      */
  1243.     if (index.byteIndex == dlPtr->index.byteIndex) {
  1244. /*
  1245.  * Case (a) -- can use existing display line as-is.
  1246.  */
  1247. if ((dlPtr->flags & HAS_3D_BORDER) && (prevPtr != NULL)
  1248. && (prevPtr->flags & (NEW_LAYOUT))) {
  1249.     dlPtr->oldY = -1;
  1250. }
  1251. goto lineOK;
  1252.     }
  1253.     if (index.byteIndex < dlPtr->index.byteIndex) {
  1254. goto makeNewDLine;
  1255.     }
  1256.     /*
  1257.      * Case (c) -- dlPtr is useless.  Discard it and start
  1258.      * again with the next display line.
  1259.      */
  1260.     newPtr = dlPtr->nextPtr;
  1261.     FreeDLines(textPtr, dlPtr, newPtr, 0);
  1262.     dlPtr = newPtr;
  1263.     if (prevPtr != NULL) {
  1264. prevPtr->nextPtr = newPtr;
  1265.     } else {
  1266. dInfoPtr->dLinePtr = newPtr;
  1267.     }
  1268.     continue;
  1269. }
  1270. /*
  1271.  * Advance to the start of the next line.
  1272.  */
  1273. lineOK:
  1274. dlPtr->y = y;
  1275. y += dlPtr->height;
  1276. TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
  1277. prevPtr = dlPtr;
  1278. dlPtr = dlPtr->nextPtr;
  1279. /*
  1280.  * If we switched text lines, delete any DLines left for the
  1281.  * old text line.
  1282.  */
  1283. if (index.linePtr != prevPtr->index.linePtr) {
  1284.     register DLine *nextPtr;
  1285.     nextPtr = dlPtr;
  1286.     while ((nextPtr != NULL)
  1287.     && (nextPtr->index.linePtr == prevPtr->index.linePtr)) {
  1288. nextPtr = nextPtr->nextPtr;
  1289.     }
  1290.     if (nextPtr != dlPtr) {
  1291. FreeDLines(textPtr, dlPtr, nextPtr, 0);
  1292. prevPtr->nextPtr = nextPtr;
  1293. dlPtr = nextPtr;
  1294.     }
  1295. }
  1296. /*
  1297.  * It's important to have the following check here rather than in
  1298.  * the while statement for the loop, so that there's always at least
  1299.  * one DLine generated, regardless of how small the window is.  This
  1300.  * keeps a lot of other code from breaking.
  1301.  */
  1302. if (y >= maxY) {
  1303.     break;
  1304. }
  1305.     }
  1306.     /*
  1307.      * Delete any DLine structures that don't fit on the screen.
  1308.      */
  1309.     FreeDLines(textPtr, dlPtr, (DLine *) NULL, 1);
  1310.     /*
  1311.      *--------------------------------------------------------------
  1312.      * If there is extra space at the bottom of the window (because
  1313.      * we've hit the end of the text), then bring in more lines at
  1314.      * the top of the window, if there are any, to fill in the view.
  1315.      *--------------------------------------------------------------
  1316.      */
  1317.     if (y < maxY) {
  1318. int lineNum, spaceLeft, bytesToCount;
  1319. DLine *lowestPtr;
  1320. /*
  1321.  * Layout an entire text line (potentially > 1 display line),
  1322.  * then link in as many display lines as fit without moving
  1323.  * the bottom line out of the window.  Repeat this until
  1324.  * all the extra space has been used up or we've reached the
  1325.  * beginning of the text.
  1326.  */
  1327. spaceLeft = maxY - y;
  1328. lineNum = TkBTreeLineIndex(dInfoPtr->dLinePtr->index.linePtr);
  1329. bytesToCount = dInfoPtr->dLinePtr->index.byteIndex;
  1330. if (bytesToCount == 0) {
  1331.     bytesToCount = INT_MAX;
  1332.     lineNum--;
  1333. }
  1334. for ( ; (lineNum >= 0) && (spaceLeft > 0); lineNum--) {
  1335.     index.linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
  1336.     index.byteIndex = 0;
  1337.     lowestPtr = NULL;
  1338.     do {
  1339. dlPtr = LayoutDLine(textPtr, &index);
  1340. dlPtr->nextPtr = lowestPtr;
  1341. lowestPtr = dlPtr;
  1342. if (dlPtr->length == 0 && dlPtr->height == 0) { bytesToCount--; break; } /* elide */
  1343. TkTextIndexForwBytes(&index, dlPtr->byteCount, &index);
  1344. bytesToCount -= dlPtr->byteCount;
  1345.     } while ((bytesToCount > 0)
  1346.     && (index.linePtr == lowestPtr->index.linePtr));
  1347.     /*
  1348.      * Scan through the display lines from the bottom one up to
  1349.      * the top one.
  1350.      */
  1351.     while (lowestPtr != NULL) {
  1352. dlPtr = lowestPtr;
  1353. spaceLeft -= dlPtr->height;
  1354. if (spaceLeft < 0) {
  1355.     break;
  1356. }
  1357. lowestPtr = dlPtr->nextPtr;
  1358. dlPtr->nextPtr = dInfoPtr->dLinePtr;
  1359. dInfoPtr->dLinePtr = dlPtr;
  1360. if (tkTextDebug) {
  1361.     char string[TK_POS_CHARS];
  1362.     TkTextPrintIndex(&dlPtr->index, string);
  1363.     Tcl_SetVar2(textPtr->interp, "tk_textRelayout",
  1364.     (char *) NULL, string,
  1365.     TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  1366. }
  1367.     }
  1368.     FreeDLines(textPtr, lowestPtr, (DLine *) NULL, 0);
  1369.     bytesToCount = INT_MAX;
  1370. }
  1371. /*
  1372.  * Now we're all done except that the y-coordinates in all the
  1373.  * DLines are wrong and the top index for the text is wrong.
  1374.  * Update them.
  1375.  */
  1376. textPtr->topIndex = dInfoPtr->dLinePtr->index;
  1377. y = dInfoPtr->y;
  1378. for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1379. dlPtr = dlPtr->nextPtr) {
  1380.     if (y > dInfoPtr->maxY) {
  1381. panic("Added too many new lines in UpdateDisplayInfo");
  1382.     }
  1383.     dlPtr->y = y;
  1384.     y += dlPtr->height; 
  1385. }
  1386.     }
  1387.     /*
  1388.      *--------------------------------------------------------------
  1389.      * If the old top or bottom line has scrolled elsewhere on the
  1390.      * screen, we may not be able to re-use its old contents by
  1391.      * copying bits (e.g., a beveled edge that was drawn when it was
  1392.      * at the top or bottom won't be drawn when the line is in the
  1393.      * middle and its neighbor has a matching background).  Similarly,
  1394.      * if the new top or bottom line came from somewhere else on the
  1395.      * screen, we may not be able to copy the old bits.
  1396.      *--------------------------------------------------------------
  1397.      */
  1398.     dlPtr = dInfoPtr->dLinePtr;
  1399.     if ((dlPtr->flags & HAS_3D_BORDER) && !(dlPtr->flags & TOP_LINE)) {
  1400. dlPtr->oldY = -1;
  1401.     }
  1402.     while (1) {
  1403. if ((dlPtr->flags & TOP_LINE) && (dlPtr != dInfoPtr->dLinePtr)
  1404. && (dlPtr->flags & HAS_3D_BORDER)) {
  1405.     dlPtr->oldY = -1;
  1406. }
  1407. if ((dlPtr->flags & BOTTOM_LINE) && (dlPtr->nextPtr != NULL)
  1408. && (dlPtr->flags & HAS_3D_BORDER)) {
  1409.     dlPtr->oldY = -1;
  1410. }
  1411. if (dlPtr->nextPtr == NULL) {
  1412.     if ((dlPtr->flags & HAS_3D_BORDER)
  1413.     && !(dlPtr->flags & BOTTOM_LINE)) {
  1414. dlPtr->oldY = -1;
  1415.     }
  1416.     dlPtr->flags &= ~TOP_LINE;
  1417.     dlPtr->flags |= BOTTOM_LINE;
  1418.     break;
  1419. }
  1420. dlPtr->flags &= ~(TOP_LINE|BOTTOM_LINE);
  1421. dlPtr = dlPtr->nextPtr;
  1422.     }
  1423.     dInfoPtr->dLinePtr->flags |= TOP_LINE;
  1424.     /*
  1425.      * Arrange for scrollbars to be updated.
  1426.      */
  1427.     textPtr->flags |= UPDATE_SCROLLBARS;
  1428.     /*
  1429.      *--------------------------------------------------------------
  1430.      * Deal with horizontal scrolling:
  1431.      * 1. If there's empty space to the right of the longest line,
  1432.      *    shift the screen to the right to fill in the empty space.
  1433.      * 2. If the desired horizontal scroll position has changed,
  1434.      *    force a full redisplay of all the lines in the widget.
  1435.      * 3. If the wrap mode isn't "none" then re-scroll to the base
  1436.      *    position.
  1437.      *--------------------------------------------------------------
  1438.      */
  1439.     dInfoPtr->maxLength = 0;
  1440.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1441.     dlPtr = dlPtr->nextPtr) {
  1442. if (dlPtr->length > dInfoPtr->maxLength) {
  1443.     dInfoPtr->maxLength = dlPtr->length;
  1444. }
  1445.     }
  1446.     maxOffset = (dInfoPtr->maxLength - (dInfoPtr->maxX - dInfoPtr->x)
  1447.     + textPtr->charWidth - 1)/textPtr->charWidth;
  1448.     if (dInfoPtr->newByteOffset > maxOffset) {
  1449. dInfoPtr->newByteOffset = maxOffset;
  1450.     }
  1451.     if (dInfoPtr->newByteOffset < 0) {
  1452. dInfoPtr->newByteOffset = 0;
  1453.     }
  1454.     pixelOffset = dInfoPtr->newByteOffset * textPtr->charWidth;
  1455.     if (pixelOffset != dInfoPtr->curPixelOffset) {
  1456. dInfoPtr->curPixelOffset = pixelOffset;
  1457. for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  1458. dlPtr = dlPtr->nextPtr) {
  1459.     dlPtr->oldY = -1;
  1460. }
  1461.     }
  1462. }
  1463. /*
  1464.  *----------------------------------------------------------------------
  1465.  *
  1466.  * FreeDLines --
  1467.  *
  1468.  * This procedure is called to free up all of the resources
  1469.  * associated with one or more DLine structures.
  1470.  *
  1471.  * Results:
  1472.  * None.
  1473.  *
  1474.  * Side effects:
  1475.  * Memory gets freed and various other resources are released.
  1476.  *
  1477.  *----------------------------------------------------------------------
  1478.  */
  1479. static void
  1480. FreeDLines(textPtr, firstPtr, lastPtr, unlink)
  1481.     TkText *textPtr; /* Information about overall text
  1482.  * widget. */
  1483.     register DLine *firstPtr; /* Pointer to first DLine to free up. */
  1484.     DLine *lastPtr; /* Pointer to DLine just after last
  1485.  * one to free (NULL means everything
  1486.  * starting with firstPtr). */
  1487.     int unlink; /* 1 means DLines are currently linked
  1488.  * into the list rooted at
  1489.  * textPtr->dInfoPtr->dLinePtr and
  1490.  * they have to be unlinked.  0 means
  1491.  * just free without unlinking. */
  1492. {
  1493.     register TkTextDispChunk *chunkPtr, *nextChunkPtr;
  1494.     register DLine *nextDLinePtr;
  1495.     if (unlink) {
  1496. if (textPtr->dInfoPtr->dLinePtr == firstPtr) {
  1497.     textPtr->dInfoPtr->dLinePtr = lastPtr;
  1498. } else {
  1499.     register DLine *prevPtr;
  1500.     for (prevPtr = textPtr->dInfoPtr->dLinePtr;
  1501.     prevPtr->nextPtr != firstPtr; prevPtr = prevPtr->nextPtr) {
  1502. /* Empty loop body. */
  1503.     }
  1504.     prevPtr->nextPtr = lastPtr;
  1505. }
  1506.     }
  1507.     while (firstPtr != lastPtr) {
  1508. nextDLinePtr = firstPtr->nextPtr;
  1509. for (chunkPtr = firstPtr->chunkPtr; chunkPtr != NULL;
  1510. chunkPtr = nextChunkPtr) {
  1511.     if (chunkPtr->undisplayProc != NULL) {
  1512. (*chunkPtr->undisplayProc)(textPtr, chunkPtr);
  1513.     }
  1514.     FreeStyle(textPtr, chunkPtr->stylePtr);
  1515.     nextChunkPtr = chunkPtr->nextPtr;
  1516.     ckfree((char *) chunkPtr);
  1517. }
  1518. ckfree((char *) firstPtr);
  1519. firstPtr = nextDLinePtr;
  1520.     }
  1521.     textPtr->dInfoPtr->dLinesInvalidated = 1;
  1522. }
  1523. /*
  1524.  *----------------------------------------------------------------------
  1525.  *
  1526.  * DisplayDLine --
  1527.  *
  1528.  * This procedure is invoked to draw a single line on the
  1529.  * screen.
  1530.  *
  1531.  * Results:
  1532.  * None.
  1533.  *
  1534.  * Side effects:
  1535.  * The line given by dlPtr is drawn at its correct position in
  1536.  * textPtr's window.  Note that this is one *display* line, not
  1537.  * one *text* line.
  1538.  *
  1539.  *----------------------------------------------------------------------
  1540.  */
  1541. static void
  1542. DisplayDLine(textPtr, dlPtr, prevPtr, pixmap)
  1543.     TkText *textPtr; /* Text widget in which to draw line. */
  1544.     register DLine *dlPtr; /* Information about line to draw. */
  1545.     DLine *prevPtr; /* Line just before one to draw, or NULL
  1546.  * if dlPtr is the top line. */
  1547.     Pixmap pixmap; /* Pixmap to use for double-buffering.
  1548.  * Caller must make sure it's large enough
  1549.  * to hold line. */
  1550. {
  1551.     register TkTextDispChunk *chunkPtr;
  1552.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1553.     Display *display;
  1554.     int height, x;
  1555. #ifndef TK_NO_DOUBLE_BUFFERING
  1556.     const int y = 0;
  1557. #else
  1558.     const int y = dlPtr->y;
  1559. #endif /* TK_NO_DOUBLE_BUFFERING */
  1560.     if (dlPtr->chunkPtr == NULL) return;
  1561.     display = Tk_Display(textPtr->tkwin);
  1562.     height = dlPtr->height;
  1563.     if ((height + dlPtr->y) > dInfoPtr->maxY) {
  1564. height = dInfoPtr->maxY - dlPtr->y;
  1565.     }
  1566. #ifdef TK_NO_DOUBLE_BUFFERING
  1567.     TkpClipDrawableToRect(display, pixmap, dInfoPtr->x, y,
  1568.     dInfoPtr->maxX - dInfoPtr->x, height);
  1569. #endif /* TK_NO_DOUBLE_BUFFERING */
  1570.     /*
  1571.      * First, clear the area of the line to the background color for the
  1572.      * text widget.
  1573.      */
  1574.     Tk_Fill3DRectangle(textPtr->tkwin, pixmap, textPtr->border, 0, y,
  1575.     Tk_Width(textPtr->tkwin), dlPtr->height, 0, TK_RELIEF_FLAT);
  1576.     /*
  1577.      * Next, draw background information for the whole line.
  1578.      */
  1579.     DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap);
  1580.     /*
  1581.      * Make another pass through all of the chunks to redraw the
  1582.      * insertion cursor, if it is visible on this line.  Must do
  1583.      * it here rather than in the foreground pass below because
  1584.      * otherwise a wide insertion cursor will obscure the character
  1585.      * to its left.
  1586.      */
  1587.     if (textPtr->state == TK_STATE_NORMAL) {
  1588. for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1589. chunkPtr = chunkPtr->nextPtr) {
  1590.     x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1591.     if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1592. (*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
  1593. dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1594. dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1595. dlPtr->y + dlPtr->spaceAbove);
  1596.     }
  1597. }
  1598.     }
  1599.     /*
  1600.      * Make yet another pass through all of the chunks to redraw all of
  1601.      * foreground information.  Note:  we have to call the displayProc
  1602.      * even for chunks that are off-screen.  This is needed, for
  1603.      * example, so that embedded windows can be unmapped in this case.
  1604.      * Conve
  1605.      */
  1606.     for (chunkPtr = dlPtr->chunkPtr; (chunkPtr != NULL);
  1607.     chunkPtr = chunkPtr->nextPtr) {
  1608. if (chunkPtr->displayProc == TkTextInsertDisplayProc) {
  1609.     /*
  1610.      * Already displayed the insertion cursor above.  Don't
  1611.      * do it again here.
  1612.      */
  1613.     continue;
  1614. }
  1615. x = chunkPtr->x + dInfoPtr->x - dInfoPtr->curPixelOffset;
  1616. if ((x + chunkPtr->width <= 0) || (x >= dInfoPtr->maxX)) {
  1617.     /*
  1618.      * Note:  we have to call the displayProc even for chunks
  1619.      * that are off-screen.  This is needed, for example, so
  1620.      * that embedded windows can be unmapped in this case.
  1621.      * Display the chunk at a coordinate that can be clearly
  1622.      * identified by the displayProc as being off-screen to
  1623.      * the left (the displayProc may not be able to tell if
  1624.      * something is off to the right).
  1625.      */
  1626.     if (chunkPtr->displayProc != NULL)
  1627.     (*chunkPtr->displayProc)(chunkPtr, -chunkPtr->width,
  1628.     y + dlPtr->spaceAbove,
  1629.     dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1630.     dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1631.     dlPtr->y + dlPtr->spaceAbove);
  1632. } else {
  1633.     /* don't call if elide.  This tax ok since not very many visible DLine's in
  1634.   an area, but potentially many elide ones */
  1635.     if (chunkPtr->displayProc != NULL)
  1636.     (*chunkPtr->displayProc)(chunkPtr, x, y + dlPtr->spaceAbove,
  1637.     dlPtr->height - dlPtr->spaceAbove - dlPtr->spaceBelow,
  1638.     dlPtr->baseline - dlPtr->spaceAbove, display, pixmap,
  1639.     dlPtr->y + dlPtr->spaceAbove);
  1640. }
  1641. if (dInfoPtr->dLinesInvalidated) {
  1642.     return;
  1643. }
  1644.     }
  1645. #ifndef TK_NO_DOUBLE_BUFFERING
  1646.     /*
  1647.      * Copy the pixmap onto the screen.  If this is the last line on
  1648.      * the screen then copy a piece of the line, so that it doesn't
  1649.      * overflow into the border area.  Another special trick:  copy the
  1650.      * padding area to the left of the line;  this is because the
  1651.      * insertion cursor sometimes overflows onto that area and we want
  1652.      * to get as much of the cursor as possible.
  1653.      */
  1654.     XCopyArea(display, pixmap, Tk_WindowId(textPtr->tkwin), dInfoPtr->copyGC,
  1655.     dInfoPtr->x, y, (unsigned) (dInfoPtr->maxX - dInfoPtr->x),
  1656.     (unsigned) height, dInfoPtr->x, dlPtr->y);
  1657. #else
  1658.     TkpClipDrawableToRect(display, pixmap, 0, 0, -1, -1);
  1659. #endif /* TK_NO_DOUBLE_BUFFERING */
  1660.     linesRedrawn++;
  1661. }
  1662. /*
  1663.  *--------------------------------------------------------------
  1664.  *
  1665.  * DisplayLineBackground --
  1666.  *
  1667.  * This procedure is called to fill in the background for
  1668.  * a display line.  It draws 3D borders cleverly so that
  1669.  * adjacent chunks with the same style (whether on the same
  1670.  * line or different lines) have a single 3D border around
  1671.  * the whole region.
  1672.  *
  1673.  * Results:
  1674.  * There is no return value.  Pixmap is filled in with background
  1675.  * information for dlPtr.
  1676.  *
  1677.  * Side effects:
  1678.  * None.
  1679.  *
  1680.  *--------------------------------------------------------------
  1681.  */
  1682. static void
  1683. DisplayLineBackground(textPtr, dlPtr, prevPtr, pixmap)
  1684.     TkText *textPtr; /* Text widget containing line. */
  1685.     register DLine *dlPtr; /* Information about line to draw. */
  1686.     DLine *prevPtr; /* Line just above dlPtr, or NULL if dlPtr
  1687.  * is the top-most line in the window. */
  1688.     Pixmap pixmap; /* Pixmap to use for double-buffering.
  1689.  * Caller must make sure it's large enough
  1690.  * to hold line.  Caller must also have
  1691.  * filled it with the background color for
  1692.  * the widget. */
  1693. {
  1694.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  1695.     TkTextDispChunk *chunkPtr;  /* Pointer to chunk in the current line. */
  1696.     TkTextDispChunk *chunkPtr2; /* Pointer to chunk in the line above or
  1697.  * below the current one.  NULL if we're to
  1698.  * the left of or to the right of the chunks
  1699.  * in the line. */
  1700.     TkTextDispChunk *nextPtr2; /* Next chunk after chunkPtr2 (it's not the
  1701.  * same as chunkPtr2->nextPtr in the case
  1702.  * where chunkPtr2 is NULL because the line
  1703.  * is indented). */
  1704.     int leftX; /* The left edge of the region we're
  1705.  * currently working on. */
  1706.     int leftXIn; /* 1 means beveled edge at leftX slopes right
  1707.  * as it goes down, 0 means it slopes left
  1708.  * as it goes down. */
  1709.     int rightX; /* Right edge of chunkPtr. */
  1710.     int rightX2; /* Right edge of chunkPtr2. */
  1711.     int matchLeft; /* Does the style of this line match that
  1712.  * of its neighbor just to the left of
  1713.  * the current x coordinate? */
  1714.     int matchRight; /* Does line's style match its neighbor
  1715.  * just to the right of the current x-coord? */
  1716.     int minX, maxX, xOffset;
  1717.     StyleValues *sValuePtr;
  1718.     Display *display;
  1719. #ifndef TK_NO_DOUBLE_BUFFERING
  1720.     const int y = 0;
  1721. #else
  1722.     const int y = dlPtr->y;
  1723. #endif /* TK_NO_DOUBLE_BUFFERING */
  1724.     /*
  1725.      * Pass 1: scan through dlPtr from left to right.  For each range of
  1726.      * chunks with the same style, draw the main background for the style
  1727.      * plus the vertical parts of the 3D borders (the left and right
  1728.      * edges).
  1729.      */
  1730.     display = Tk_Display(textPtr->tkwin);
  1731.     minX = dInfoPtr->curPixelOffset;
  1732.     xOffset = dInfoPtr->x - minX;
  1733.     maxX = minX + dInfoPtr->maxX - dInfoPtr->x;
  1734.     chunkPtr = dlPtr->chunkPtr;
  1735.     /*
  1736.      * Note A: in the following statement, and a few others later in
  1737.      * this file marked with "See Note A above", the right side of the
  1738.      * assignment was replaced with 0 on 6/18/97.  This has the effect
  1739.      * of highlighting the empty space to the left of a line whenever
  1740.      * the leftmost character of the line is highlighted.  This way,
  1741.      * multi-line highlights always line up along their left edges. 
  1742.      * However, this may look funny in the case where a single word is
  1743.      * highlighted. To undo the change, replace "leftX = 0" with "leftX
  1744.      * = chunkPtr->x" and "rightX2 = 0" with "rightX2 = nextPtr2->x"
  1745.      * here and at all the marked points below.  This restores the old
  1746.      * behavior where empty space to the left of a line is not
  1747.      * highlighted, leaving a ragged left edge for multi-line
  1748.      * highlights.
  1749.      */
  1750.     leftX = 0;
  1751.     for (; leftX < maxX; chunkPtr = chunkPtr->nextPtr) {
  1752. if ((chunkPtr->nextPtr != NULL)
  1753. && SAME_BACKGROUND(chunkPtr->nextPtr->stylePtr,
  1754. chunkPtr->stylePtr)) {
  1755.     continue;
  1756. }
  1757. sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1758. rightX = chunkPtr->x + chunkPtr->width;
  1759. if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1760.     rightX = maxX;
  1761. }
  1762. if (chunkPtr->stylePtr->bgGC != None) {
  1763.     /* Not visible - bail out now */
  1764.     if (rightX + xOffset <= 0) {
  1765.         leftX = rightX;
  1766. continue;
  1767.     }
  1768.     /*
  1769.      * Trim the start position for drawing to be no further away than
  1770.      * -borderWidth. The reason is that on many X servers drawing from
  1771.      * -32768 (or less) to +something simply does not display
  1772.      * correctly. [Patch #541999]
  1773.      */
  1774.     if ((leftX + xOffset) < -(sValuePtr->borderWidth)) {
  1775.         leftX = -sValuePtr->borderWidth - xOffset;
  1776.     }
  1777.     if ((rightX - leftX) > 32767) {
  1778.         rightX = leftX + 32767;
  1779.     }
  1780.     XFillRectangle(display, pixmap, chunkPtr->stylePtr->bgGC,
  1781.     leftX + xOffset, y, (unsigned int) (rightX - leftX),
  1782.     (unsigned int) dlPtr->height);
  1783.     if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1784. Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1785. leftX + xOffset, y, sValuePtr->borderWidth,
  1786. dlPtr->height, 1, sValuePtr->relief);
  1787. Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1788. rightX - sValuePtr->borderWidth + xOffset,
  1789. y, sValuePtr->borderWidth, dlPtr->height, 0,
  1790. sValuePtr->relief);
  1791.     }
  1792. }
  1793. leftX = rightX;
  1794.     }
  1795.     /*
  1796.      * Pass 2: draw the horizontal bevels along the top of the line.  To
  1797.      * do this, scan through dlPtr from left to right while simultaneously
  1798.      * scanning through the line just above dlPtr.  ChunkPtr2 and nextPtr2
  1799.      * refer to two adjacent chunks in the line above.
  1800.      */
  1801.     chunkPtr = dlPtr->chunkPtr;
  1802.     leftX = 0; /* See Note A above. */
  1803.     leftXIn = 1;
  1804.     rightX = chunkPtr->x + chunkPtr->width;
  1805.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1806. rightX = maxX;
  1807.     }
  1808.     chunkPtr2 = NULL;
  1809.     if (prevPtr != NULL && prevPtr->chunkPtr != NULL) {
  1810. /*
  1811.  * Find the chunk in the previous line that covers leftX.
  1812.  */
  1813. nextPtr2 = prevPtr->chunkPtr;
  1814. rightX2 = 0; /* See Note A above. */
  1815. while (rightX2 <= leftX) {
  1816.     chunkPtr2 = nextPtr2;
  1817.     if (chunkPtr2 == NULL) {
  1818. break;
  1819.     }
  1820.     nextPtr2 = chunkPtr2->nextPtr;
  1821.     rightX2 = chunkPtr2->x + chunkPtr2->width;
  1822.     if (nextPtr2 == NULL) {
  1823. rightX2 = INT_MAX;
  1824.     }
  1825. }
  1826.     } else {
  1827. nextPtr2 = NULL;
  1828. rightX2 = INT_MAX;
  1829.     }
  1830.     while (leftX < maxX) {
  1831. matchLeft = (chunkPtr2 != NULL)
  1832. && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1833. sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1834. if (rightX <= rightX2) {
  1835.     /*
  1836.      * The chunk in our line is about to end.  If its style
  1837.      * changes then draw the bevel for the current style.
  1838.      */
  1839.     if ((chunkPtr->nextPtr == NULL)
  1840.     || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1841.     chunkPtr->nextPtr->stylePtr)) {
  1842. if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1843.     Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1844.     sValuePtr->border, leftX + xOffset, y,
  1845.     rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1846.     1, 1, sValuePtr->relief);
  1847. }
  1848. leftX = rightX;
  1849. leftXIn = 1;
  1850. /*
  1851.  * If the chunk in the line above is also ending at
  1852.  * the same point then advance to the next chunk in
  1853.  * that line.
  1854.  */
  1855. if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1856.     goto nextChunk2;
  1857. }
  1858.     }
  1859.     chunkPtr = chunkPtr->nextPtr;
  1860.     if (chunkPtr == NULL) {
  1861. break;
  1862.     }
  1863.     rightX = chunkPtr->x + chunkPtr->width;
  1864.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1865. rightX = maxX;
  1866.     }
  1867.     continue;
  1868. }
  1869. /*
  1870.  * The chunk in the line above is ending at an x-position where
  1871.  * there is no change in the style of the current line.  If the
  1872.  * style above matches the current line on one side of the change
  1873.  * but not on the other, we have to draw an L-shaped piece of
  1874.  * bevel.
  1875.  */
  1876. matchRight = (nextPtr2 != NULL)
  1877. && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1878. if (matchLeft && !matchRight) {
  1879.     if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1880. Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1881. rightX2 - sValuePtr->borderWidth + xOffset, y,
  1882. sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1883. sValuePtr->relief);
  1884.     }
  1885.     leftX = rightX2 - sValuePtr->borderWidth;
  1886.     leftXIn = 0;
  1887. } else if (!matchLeft && matchRight
  1888. && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1889.     Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1890.     rightX2 + xOffset, y, sValuePtr->borderWidth,
  1891.     sValuePtr->borderWidth, 1, sValuePtr->relief);
  1892.     Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1893.     leftX + xOffset, y, rightX2 + sValuePtr->borderWidth -
  1894.     leftX, sValuePtr->borderWidth, leftXIn, 0, 1,
  1895.     sValuePtr->relief);
  1896. }
  1897. nextChunk2:
  1898. chunkPtr2 = nextPtr2;
  1899. if (chunkPtr2 == NULL) {
  1900.     rightX2 = INT_MAX;
  1901. } else {
  1902.     nextPtr2 = chunkPtr2->nextPtr;
  1903.     rightX2 = chunkPtr2->x + chunkPtr2->width;
  1904.     if (nextPtr2 == NULL) {
  1905. rightX2 = INT_MAX;
  1906.     }
  1907. }
  1908.     }
  1909.     /*
  1910.      * Pass 3: draw the horizontal bevels along the bottom of the line.
  1911.      * This uses the same approach as pass 2.
  1912.      */
  1913.     chunkPtr = dlPtr->chunkPtr;
  1914.     leftX = 0; /* See Note A above. */
  1915.     leftXIn = 0;
  1916.     rightX = chunkPtr->x + chunkPtr->width;
  1917.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1918. rightX = maxX;
  1919.     }
  1920.     chunkPtr2 = NULL;
  1921.     if (dlPtr->nextPtr != NULL && dlPtr->nextPtr->chunkPtr != NULL) {
  1922. /*
  1923.  * Find the chunk in the previous line that covers leftX.
  1924.  */
  1925. nextPtr2 = dlPtr->nextPtr->chunkPtr;
  1926. rightX2 = 0; /* See Note A above. */
  1927. while (rightX2 <= leftX) {
  1928.     chunkPtr2 = nextPtr2;
  1929.     if (chunkPtr2 == NULL) {
  1930. break;
  1931.     }
  1932.     nextPtr2 = chunkPtr2->nextPtr;
  1933.     rightX2 = chunkPtr2->x + chunkPtr2->width;
  1934.     if (nextPtr2 == NULL) {
  1935. rightX2 = INT_MAX;
  1936.     }
  1937. }
  1938.     } else {
  1939. nextPtr2 = NULL;
  1940. rightX2 = INT_MAX;
  1941.     }
  1942.     while (leftX < maxX) {
  1943. matchLeft = (chunkPtr2 != NULL)
  1944. && SAME_BACKGROUND(chunkPtr2->stylePtr, chunkPtr->stylePtr);
  1945. sValuePtr = chunkPtr->stylePtr->sValuePtr;
  1946. if (rightX <= rightX2) {
  1947.     if ((chunkPtr->nextPtr == NULL)
  1948.     || !SAME_BACKGROUND(chunkPtr->stylePtr,
  1949.     chunkPtr->nextPtr->stylePtr)) {
  1950. if (!matchLeft && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1951.     Tk_3DHorizontalBevel(textPtr->tkwin, pixmap,
  1952.     sValuePtr->border, leftX + xOffset,
  1953.     y + dlPtr->height - sValuePtr->borderWidth,
  1954.     rightX - leftX, sValuePtr->borderWidth, leftXIn,
  1955.     0, 0, sValuePtr->relief);
  1956. }
  1957. leftX = rightX;
  1958. leftXIn = 0;
  1959. if ((rightX == rightX2) && (chunkPtr2 != NULL)) {
  1960.     goto nextChunk2b;
  1961. }
  1962.     }
  1963.     chunkPtr = chunkPtr->nextPtr;
  1964.     if (chunkPtr == NULL) {
  1965. break;
  1966.     }
  1967.     rightX = chunkPtr->x + chunkPtr->width;
  1968.     if ((chunkPtr->nextPtr == NULL) && (rightX < maxX)) {
  1969. rightX = maxX;
  1970.     }
  1971.     continue;
  1972. }
  1973. matchRight = (nextPtr2 != NULL)
  1974. && SAME_BACKGROUND(nextPtr2->stylePtr, chunkPtr->stylePtr);
  1975. if (matchLeft && !matchRight) {
  1976.     if (sValuePtr->relief != TK_RELIEF_FLAT) {
  1977. Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1978. rightX2 - sValuePtr->borderWidth + xOffset,
  1979. y + dlPtr->height - sValuePtr->borderWidth,
  1980. sValuePtr->borderWidth, sValuePtr->borderWidth, 0,
  1981. sValuePtr->relief);
  1982.     }
  1983.     leftX = rightX2 - sValuePtr->borderWidth;
  1984.     leftXIn = 1;
  1985. } else if (!matchLeft && matchRight
  1986. && (sValuePtr->relief != TK_RELIEF_FLAT)) {
  1987.     Tk_3DVerticalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1988.     rightX2 + xOffset, y + dlPtr->height -
  1989.     sValuePtr->borderWidth, sValuePtr->borderWidth,
  1990.     sValuePtr->borderWidth, 1, sValuePtr->relief);
  1991.     Tk_3DHorizontalBevel(textPtr->tkwin, pixmap, sValuePtr->border,
  1992.     leftX + xOffset, y + dlPtr->height -
  1993.     sValuePtr->borderWidth, rightX2 + sValuePtr->borderWidth -
  1994.     leftX, sValuePtr->borderWidth, leftXIn, 1, 0,
  1995.     sValuePtr->relief);
  1996. }
  1997. nextChunk2b:
  1998. chunkPtr2 = nextPtr2;
  1999. if (chunkPtr2 == NULL) {
  2000.     rightX2 = INT_MAX;
  2001. } else {
  2002.     nextPtr2 = chunkPtr2->nextPtr;
  2003.     rightX2 = chunkPtr2->x + chunkPtr2->width;
  2004.     if (nextPtr2 == NULL) {
  2005. rightX2 = INT_MAX;
  2006.     }
  2007. }
  2008.     }
  2009. }
  2010. /*
  2011.  *----------------------------------------------------------------------
  2012.  *
  2013.  * DisplayText --
  2014.  *
  2015.  * This procedure is invoked as a when-idle handler to update the
  2016.  * display.  It only redisplays the parts of the text widget that
  2017.  * are out of date.
  2018.  *
  2019.  * Results:
  2020.  * None.
  2021.  *
  2022.  * Side effects:
  2023.  * Information is redrawn on the screen.
  2024.  *
  2025.  *----------------------------------------------------------------------
  2026.  */
  2027. static void
  2028. DisplayText(clientData)
  2029.     ClientData clientData; /* Information about widget. */
  2030. {
  2031.     register TkText *textPtr = (TkText *) clientData;
  2032.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2033.     Tk_Window tkwin;
  2034.     register DLine *dlPtr;
  2035.     DLine *prevPtr;
  2036.     Pixmap pixmap;
  2037.     int maxHeight, borders;
  2038.     int bottomY = 0; /* Initialization needed only to stop
  2039.  * compiler warnings. */
  2040.     Tcl_Interp *interp;
  2041.     if (textPtr->tkwin == NULL) {
  2042. /*
  2043.  * The widget has been deleted.  Don't do anything.
  2044.  */
  2045. return;
  2046.     }
  2047.     interp = textPtr->interp;
  2048.     Tcl_Preserve((ClientData) interp);
  2049.     if (tkTextDebug) {
  2050. Tcl_SetVar2(interp, "tk_textRelayout", (char *) NULL, "",
  2051.                 TCL_GLOBAL_ONLY);
  2052.     }
  2053.     if (textPtr->tkwin == NULL) {
  2054. /*
  2055.  * The widget has been deleted.  Don't do anything.
  2056.  */
  2057.         goto end;
  2058.     }
  2059.     if (!Tk_IsMapped(textPtr->tkwin) || (dInfoPtr->maxX <= dInfoPtr->x)
  2060.     || (dInfoPtr->maxY <= dInfoPtr->y)) {
  2061. UpdateDisplayInfo(textPtr);
  2062. dInfoPtr->flags &= ~REDRAW_PENDING;
  2063. goto doScrollbars;
  2064.     }
  2065.     numRedisplays++;
  2066.     if (tkTextDebug) {
  2067. Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "",
  2068.                 TCL_GLOBAL_ONLY);
  2069.     }
  2070.     if (textPtr->tkwin == NULL) {
  2071. /*
  2072.  * The widget has been deleted.  Don't do anything.
  2073.  */
  2074. goto end;
  2075.     }
  2076.     /*
  2077.      * Choose a new current item if that is needed (this could cause
  2078.      * event handlers to be invoked, hence the preserve/release calls
  2079.      * and the loop, since the handlers could conceivably necessitate
  2080.      * yet another current item calculation).  The tkwin check is because
  2081.      * the whole window could go away in the Tcl_Release call.
  2082.      */
  2083.     while (dInfoPtr->flags & REPICK_NEEDED) {
  2084. Tcl_Preserve((ClientData) textPtr);
  2085. dInfoPtr->flags &= ~REPICK_NEEDED;
  2086. TkTextPickCurrent(textPtr, &textPtr->pickEvent);
  2087. tkwin = textPtr->tkwin;
  2088. Tcl_Release((ClientData) textPtr);
  2089. if (tkwin == NULL) {
  2090.     goto end;
  2091. }
  2092.     }
  2093.     /*
  2094.      * First recompute what's supposed to be displayed.
  2095.      */
  2096.     UpdateDisplayInfo(textPtr);
  2097.     dInfoPtr->dLinesInvalidated = 0;
  2098.     /*
  2099.      * See if it's possible to bring some parts of the screen up-to-date
  2100.      * by scrolling (copying from other parts of the screen).
  2101.      */
  2102.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL; dlPtr = dlPtr->nextPtr) {
  2103. register DLine *dlPtr2;
  2104. int offset, height, y, oldY;
  2105. TkRegion damageRgn;
  2106. if ((dlPtr->oldY == -1) || (dlPtr->y == dlPtr->oldY)
  2107. || ((dlPtr->oldY + dlPtr->height) > dInfoPtr->maxY)) {
  2108.     continue;
  2109. }
  2110. /*
  2111.  * This line is already drawn somewhere in the window so it only
  2112.  * needs to be copied to its new location.  See if there's a group
  2113.  * of lines that can all be copied together.
  2114.  */
  2115. offset = dlPtr->y - dlPtr->oldY;
  2116. height = dlPtr->height;
  2117. y = dlPtr->y;
  2118. for (dlPtr2 = dlPtr->nextPtr; dlPtr2 != NULL;
  2119. dlPtr2 = dlPtr2->nextPtr) {
  2120.     if ((dlPtr2->oldY == -1)
  2121.     || ((dlPtr2->oldY + offset) != dlPtr2->y)
  2122.     || ((dlPtr2->oldY + dlPtr2->height) > dInfoPtr->maxY)) {
  2123. break;
  2124.     }
  2125.     height += dlPtr2->height;
  2126. }
  2127. /*
  2128.  * Reduce the height of the area being copied if necessary to
  2129.  * avoid overwriting the border area.
  2130.  */
  2131. if ((y + height) > dInfoPtr->maxY) {
  2132.     height = dInfoPtr->maxY -y;
  2133. }
  2134. oldY = dlPtr->oldY;
  2135. /*
  2136.  * Update the lines we are going to scroll to show that they
  2137.  * have been copied.
  2138.  */
  2139. while (1) {
  2140.     dlPtr->oldY = dlPtr->y;
  2141.     if (dlPtr->nextPtr == dlPtr2) {
  2142. break;
  2143.     }
  2144.     dlPtr = dlPtr->nextPtr;
  2145. }
  2146. /*
  2147.  * Scan through the lines following the copied ones to see if
  2148.  * we are going to overwrite them with the copy operation.
  2149.  * If so, mark them for redisplay.
  2150.  */
  2151. for ( ; dlPtr2 != NULL; dlPtr2 = dlPtr2->nextPtr) {
  2152.     if ((dlPtr2->oldY != -1)
  2153.     && ((dlPtr2->oldY + dlPtr2->height) > y)
  2154.     && (dlPtr2->oldY < (y + height))) {
  2155. dlPtr2->oldY = -1;
  2156.     }
  2157. }
  2158. /*
  2159.  * Now scroll the lines.  This may generate damage which we
  2160.  * handle by calling TextInvalidateRegion to mark the display
  2161.  * blocks as stale.
  2162.  */
  2163. damageRgn = TkCreateRegion();
  2164. if (TkScrollWindow(textPtr->tkwin, dInfoPtr->scrollGC,
  2165. dInfoPtr->x, oldY,
  2166. (dInfoPtr->maxX - dInfoPtr->x), height,
  2167. 0, y - oldY, damageRgn)) {
  2168.     TextInvalidateRegion(textPtr, damageRgn);
  2169. }
  2170. numCopies++;
  2171. TkDestroyRegion(damageRgn);
  2172.     }
  2173.     /*
  2174.      * Clear the REDRAW_PENDING flag here.  This is actually pretty
  2175.      * tricky.  We want to wait until *after* doing the scrolling,
  2176.      * since that could generate more areas to redraw and don't
  2177.      * want to reschedule a redisplay for them.  On the other hand,
  2178.      * we can't wait until after all the redisplaying, because the
  2179.      * act of redisplaying could actually generate more redisplays
  2180.      * (e.g. in the case of a nested window with event bindings triggered
  2181.      * by redisplay).
  2182.      */
  2183.     dInfoPtr->flags &= ~REDRAW_PENDING;
  2184.     /*
  2185.      * Redraw the borders if that's needed.
  2186.      */
  2187.     if (dInfoPtr->flags & REDRAW_BORDERS) {
  2188. if (tkTextDebug) {
  2189.     Tcl_SetVar2(interp, "tk_textRedraw", (char *) NULL, "borders",
  2190.     TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2191. }
  2192.         if (textPtr->tkwin == NULL) {
  2193.     /*
  2194.              * The widget has been deleted.  Don't do anything.
  2195.              */
  2196.             goto end;
  2197.         }
  2198. Tk_Draw3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2199. textPtr->border, textPtr->highlightWidth,
  2200. textPtr->highlightWidth,
  2201. Tk_Width(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2202. Tk_Height(textPtr->tkwin) - 2*textPtr->highlightWidth,
  2203. textPtr->borderWidth, textPtr->relief);
  2204. if (textPtr->highlightWidth != 0) {
  2205.     GC fgGC, bgGC;
  2206.     
  2207.     bgGC = Tk_GCForColor(textPtr->highlightBgColorPtr,
  2208. Tk_WindowId(textPtr->tkwin));
  2209.     if (textPtr->flags & GOT_FOCUS) {
  2210. fgGC = Tk_GCForColor(textPtr->highlightColorPtr,
  2211. Tk_WindowId(textPtr->tkwin));
  2212.         TkpDrawHighlightBorder(textPtr->tkwin, fgGC, bgGC, 
  2213.         textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
  2214.     } else {
  2215.         TkpDrawHighlightBorder(textPtr->tkwin, bgGC, bgGC, 
  2216.         textPtr->highlightWidth, Tk_WindowId(textPtr->tkwin));
  2217.     }
  2218. }
  2219. borders = textPtr->borderWidth + textPtr->highlightWidth;
  2220. if (textPtr->padY > 0) {
  2221.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2222.     textPtr->border, borders, borders,
  2223.     Tk_Width(textPtr->tkwin) - 2*borders, textPtr->padY,
  2224.     0, TK_RELIEF_FLAT);
  2225.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2226.     textPtr->border, borders,
  2227.     Tk_Height(textPtr->tkwin) - borders - textPtr->padY,
  2228.     Tk_Width(textPtr->tkwin) - 2*borders,
  2229.     textPtr->padY, 0, TK_RELIEF_FLAT);
  2230. }
  2231. if (textPtr->padX > 0) {
  2232.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2233.     textPtr->border, borders, borders + textPtr->padY,
  2234.     textPtr->padX,
  2235.     Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2236.     0, TK_RELIEF_FLAT);
  2237.     Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2238.     textPtr->border,
  2239.     Tk_Width(textPtr->tkwin) - borders - textPtr->padX,
  2240.     borders + textPtr->padY, textPtr->padX,
  2241.     Tk_Height(textPtr->tkwin) - 2*borders -2*textPtr->padY,
  2242.     0, TK_RELIEF_FLAT);
  2243. }
  2244. dInfoPtr->flags &= ~REDRAW_BORDERS;
  2245.     }
  2246.     /*
  2247.      * Now we have to redraw the lines that couldn't be updated by
  2248.      * scrolling.  First, compute the height of the largest line and
  2249.      * allocate an off-screen pixmap to use for double-buffered
  2250.      * displays.
  2251.      */
  2252.     maxHeight = -1;
  2253.     for (dlPtr = dInfoPtr->dLinePtr; dlPtr != NULL;
  2254.     dlPtr = dlPtr->nextPtr) {
  2255. if ((dlPtr->height > maxHeight) && (dlPtr->oldY != dlPtr->y)) {
  2256.     maxHeight = dlPtr->height;
  2257. }
  2258. bottomY = dlPtr->y + dlPtr->height;
  2259.     }
  2260.     if (maxHeight > dInfoPtr->maxY) {
  2261. maxHeight = dInfoPtr->maxY;
  2262.     }
  2263.     if (maxHeight > 0) {
  2264. #ifndef TK_NO_DOUBLE_BUFFERING
  2265. pixmap = Tk_GetPixmap(Tk_Display(textPtr->tkwin),
  2266. Tk_WindowId(textPtr->tkwin), Tk_Width(textPtr->tkwin),
  2267. maxHeight, Tk_Depth(textPtr->tkwin));
  2268. #else
  2269. pixmap = Tk_WindowId(textPtr->tkwin);
  2270. #endif /* TK_NO_DOUBLE_BUFFERING */
  2271. for (prevPtr = NULL, dlPtr = textPtr->dInfoPtr->dLinePtr;
  2272. (dlPtr != NULL) && (dlPtr->y < dInfoPtr->maxY);
  2273. prevPtr = dlPtr, dlPtr = dlPtr->nextPtr) {
  2274.     if (dlPtr->chunkPtr == NULL) continue;
  2275.     if (dlPtr->oldY != dlPtr->y) {
  2276. if (tkTextDebug) {
  2277.     char string[TK_POS_CHARS];
  2278.     TkTextPrintIndex(&dlPtr->index, string);
  2279.     Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2280.     (char *) NULL, string,
  2281.     TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2282. }
  2283. DisplayDLine(textPtr, dlPtr, prevPtr, pixmap);
  2284. if (dInfoPtr->dLinesInvalidated) {
  2285. #ifndef TK_NO_DOUBLE_BUFFERING
  2286.     Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2287. #endif /* TK_NO_DOUBLE_BUFFERING */
  2288.     return;
  2289. }
  2290. dlPtr->oldY = dlPtr->y;
  2291. dlPtr->flags &= ~NEW_LAYOUT;
  2292.     }
  2293.     /*prevPtr = dlPtr;*/
  2294. }
  2295. #ifndef TK_NO_DOUBLE_BUFFERING
  2296. Tk_FreePixmap(Tk_Display(textPtr->tkwin), pixmap);
  2297. #endif /* TK_NO_DOUBLE_BUFFERING */
  2298.     }
  2299.     /*
  2300.      * See if we need to refresh the part of the window below the
  2301.      * last line of text (if there is any such area).  Refresh the
  2302.      * padding area on the left too, since the insertion cursor might
  2303.      * have been displayed there previously).
  2304.      */
  2305.     if (dInfoPtr->topOfEof > dInfoPtr->maxY) {
  2306. dInfoPtr->topOfEof = dInfoPtr->maxY;
  2307.     }
  2308.     if (bottomY < dInfoPtr->topOfEof) {
  2309. if (tkTextDebug) {
  2310.     Tcl_SetVar2(textPtr->interp, "tk_textRedraw",
  2311.     (char *) NULL, "eof",
  2312.     TCL_GLOBAL_ONLY|TCL_APPEND_VALUE|TCL_LIST_ELEMENT);
  2313. }
  2314.         if (textPtr->tkwin == NULL) {
  2315.     /*
  2316.              * The widget has been deleted.  Don't do anything.
  2317.              */
  2318.             goto end;
  2319.         }
  2320. Tk_Fill3DRectangle(textPtr->tkwin, Tk_WindowId(textPtr->tkwin),
  2321. textPtr->border, dInfoPtr->x - textPtr->padX, bottomY,
  2322. dInfoPtr->maxX - (dInfoPtr->x - textPtr->padX),
  2323. dInfoPtr->topOfEof-bottomY, 0, TK_RELIEF_FLAT);
  2324.     }
  2325.     dInfoPtr->topOfEof = bottomY;
  2326.     doScrollbars:
  2327.     /*
  2328.      * Update the vertical scrollbar, if there is one.  Note:  it's
  2329.      * important to clear REDRAW_PENDING here, just in case the
  2330.      * scroll procedure does something that requires redisplay.
  2331.      */
  2332.     
  2333.     if (textPtr->flags & UPDATE_SCROLLBARS) {
  2334. textPtr->flags &= ~UPDATE_SCROLLBARS;
  2335. if (textPtr->yScrollCmd != NULL) {
  2336.     GetYView(textPtr->interp, textPtr, 1);
  2337. }
  2338.         if (textPtr->tkwin == NULL) {
  2339.     /*
  2340.              * The widget has been deleted.  Don't do anything.
  2341.              */
  2342.             goto end;
  2343.         }
  2344. /*
  2345.  * Update the horizontal scrollbar, if any.
  2346.  */
  2347. if (textPtr->xScrollCmd != NULL) {
  2348.     GetXView(textPtr->interp, textPtr, 1);
  2349. }
  2350.     }
  2351. end:
  2352.     Tcl_Release((ClientData) interp);
  2353. }
  2354. /*
  2355.  *----------------------------------------------------------------------
  2356.  *
  2357.  * TkTextEventuallyRepick --
  2358.  *
  2359.  * This procedure is invoked whenever something happens that
  2360.  * could change the current character or the tags associated
  2361.  * with it.
  2362.  *
  2363.  * Results:
  2364.  * None.
  2365.  *
  2366.  * Side effects:
  2367.  * A repick is scheduled as an idle handler.
  2368.  *
  2369.  *----------------------------------------------------------------------
  2370.  */
  2371. /* ARGSUSED */
  2372. void
  2373. TkTextEventuallyRepick(textPtr)
  2374.     TkText *textPtr; /* Widget record for text widget. */
  2375. {
  2376.     TextDInfo *dInfoPtr = textPtr->dInfoPtr;
  2377.     dInfoPtr->flags |= REPICK_NEEDED;
  2378.     if (!(dInfoPtr->flags & REDRAW_PENDING)) {
  2379. dInfoPtr->flags |= REDRAW_PENDING;
  2380. Tcl_DoWhenIdle(DisplayText, (ClientData) textPtr);
  2381.     }
  2382. }
  2383. /*
  2384.  *----------------------------------------------------------------------
  2385.  *
  2386.  * TkTextRedrawRegion --
  2387.  *
  2388.  * This procedure is invoked to schedule a redisplay for a given
  2389.  * region of a text widget.  The redisplay itself may not occur
  2390.  * immediately:  it's scheduled as a when-idle handler.
  2391.  *
  2392.  * Results:
  2393.  * None.
  2394.  *
  2395.  * Side effects:
  2396.  * Information will eventually be redrawn on the screen.
  2397.  *
  2398.  *----------------------------------------------------------------------
  2399.  */
  2400. /* ARGSUSED */
  2401. void
  2402. TkTextRedrawRegion(textPtr, x, y, width, height)
  2403.     TkText *textPtr; /* Widget record for text widget. */
  2404.     int x, y; /* Coordinates of upper-left corner of area
  2405.  * to be redrawn, in pixels relative to