/[dtapublic]/projs/emts/trunk/src/c_tk_base_7_5_w_mods/tktextdisp.c
ViewVC logotype

Diff of /projs/emts/trunk/src/c_tk_base_7_5_w_mods/tktextdisp.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.69  
changed lines
  Added in v.269

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25