/[dtapublic]/projs/ets/trunk/src/c_tk_base_7_5_w_mods/tktext.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tk_base_7_5_w_mods/tktext.c

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

revision 70 by dashley, Sat Nov 5 10:54:17 2016 UTC revision 71 by dashley, Sat Nov 5 11:07:06 2016 UTC
# Line 1  Line 1 
1  /* $Header$ */  /* $Header$ */
2    
3  /*  /*
4   * tkText.c --   * tkText.c --
5   *   *
6   *      This module provides a big chunk of the implementation of   *      This module provides a big chunk of the implementation of
7   *      multi-line editable text widgets for Tk.  Among other things,   *      multi-line editable text widgets for Tk.  Among other things,
8   *      it provides the Tcl command interfaces to text widgets and   *      it provides the Tcl command interfaces to text widgets and
9   *      the display code.  The B-tree representation of text is   *      the display code.  The B-tree representation of text is
10   *      implemented elsewhere.   *      implemented elsewhere.
11   *   *
12   * Copyright (c) 1992-1994 The Regents of the University of California.   * Copyright (c) 1992-1994 The Regents of the University of California.
13   * Copyright (c) 1994-1996 Sun Microsystems, Inc.   * Copyright (c) 1994-1996 Sun Microsystems, Inc.
14   * Copyright (c) 1999 by Scriptics Corporation.   * Copyright (c) 1999 by Scriptics Corporation.
15   *   *
16   * See the file "license.terms" for information on usage and redistribution   * See the file "license.terms" for information on usage and redistribution
17   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18   *   *
19   * RCS: @(#) $Id: tktext.c,v 1.1.1.1 2001/06/13 05:08:42 dtashley Exp $   * RCS: @(#) $Id: tktext.c,v 1.1.1.1 2001/06/13 05:08:42 dtashley Exp $
20   */   */
21    
22  #include "default.h"  #include "default.h"
23  #include "tkPort.h"  #include "tkPort.h"
24  #include "tkInt.h"  #include "tkInt.h"
25    
26  #ifdef MAC_TCL  #ifdef MAC_TCL
27  #define Style TkStyle  #define Style TkStyle
28  #define DInfo TkDInfo  #define DInfo TkDInfo
29  #endif  #endif
30    
31  #include "tkText.h"  #include "tkText.h"
32    
33  /*  /*
34   * Custom options for handling "-state"   * Custom options for handling "-state"
35   */   */
36    
37  static Tk_CustomOption stateOption = {  static Tk_CustomOption stateOption = {
38      (Tk_OptionParseProc *) TkStateParseProc,      (Tk_OptionParseProc *) TkStateParseProc,
39      TkStatePrintProc, (ClientData) NULL /* only "normal" and "disabled" */      TkStatePrintProc, (ClientData) NULL /* only "normal" and "disabled" */
40  };  };
41    
42  /*  /*
43   * Information used to parse text configuration options:   * Information used to parse text configuration options:
44   */   */
45    
46  static Tk_ConfigSpec configSpecs[] = {  static Tk_ConfigSpec configSpecs[] = {
47      {TK_CONFIG_BORDER, "-background", "background", "Background",      {TK_CONFIG_BORDER, "-background", "background", "Background",
48          DEF_TEXT_BG_COLOR, Tk_Offset(TkText, border), TK_CONFIG_COLOR_ONLY},          DEF_TEXT_BG_COLOR, Tk_Offset(TkText, border), TK_CONFIG_COLOR_ONLY},
49      {TK_CONFIG_BORDER, "-background", "background", "Background",      {TK_CONFIG_BORDER, "-background", "background", "Background",
50          DEF_TEXT_BG_MONO, Tk_Offset(TkText, border), TK_CONFIG_MONO_ONLY},          DEF_TEXT_BG_MONO, Tk_Offset(TkText, border), TK_CONFIG_MONO_ONLY},
51      {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,      {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
52          (char *) NULL, 0, 0},          (char *) NULL, 0, 0},
53      {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,      {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
54          (char *) NULL, 0, 0},          (char *) NULL, 0, 0},
55      {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",      {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
56          DEF_TEXT_BORDER_WIDTH, Tk_Offset(TkText, borderWidth), 0},          DEF_TEXT_BORDER_WIDTH, Tk_Offset(TkText, borderWidth), 0},
57      {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",      {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
58          DEF_TEXT_CURSOR, Tk_Offset(TkText, cursor), TK_CONFIG_NULL_OK},          DEF_TEXT_CURSOR, Tk_Offset(TkText, cursor), TK_CONFIG_NULL_OK},
59      {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",      {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
60          "ExportSelection", DEF_TEXT_EXPORT_SELECTION,          "ExportSelection", DEF_TEXT_EXPORT_SELECTION,
61          Tk_Offset(TkText, exportSelection), 0},          Tk_Offset(TkText, exportSelection), 0},
62      {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,      {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
63          (char *) NULL, 0, 0},          (char *) NULL, 0, 0},
64      {TK_CONFIG_FONT, "-font", "font", "Font",      {TK_CONFIG_FONT, "-font", "font", "Font",
65          DEF_TEXT_FONT, Tk_Offset(TkText, tkfont), 0},          DEF_TEXT_FONT, Tk_Offset(TkText, tkfont), 0},
66      {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",      {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
67          DEF_TEXT_FG, Tk_Offset(TkText, fgColor), 0},          DEF_TEXT_FG, Tk_Offset(TkText, fgColor), 0},
68      {TK_CONFIG_PIXELS, "-height", "height", "Height",      {TK_CONFIG_PIXELS, "-height", "height", "Height",
69          DEF_TEXT_HEIGHT, Tk_Offset(TkText, height), 0},          DEF_TEXT_HEIGHT, Tk_Offset(TkText, height), 0},
70      {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",      {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
71          "HighlightBackground", DEF_TEXT_HIGHLIGHT_BG,          "HighlightBackground", DEF_TEXT_HIGHLIGHT_BG,
72          Tk_Offset(TkText, highlightBgColorPtr), 0},          Tk_Offset(TkText, highlightBgColorPtr), 0},
73      {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",      {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
74          DEF_TEXT_HIGHLIGHT, Tk_Offset(TkText, highlightColorPtr), 0},          DEF_TEXT_HIGHLIGHT, Tk_Offset(TkText, highlightColorPtr), 0},
75      {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",      {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
76          "HighlightThickness",          "HighlightThickness",
77          DEF_TEXT_HIGHLIGHT_WIDTH, Tk_Offset(TkText, highlightWidth), 0},          DEF_TEXT_HIGHLIGHT_WIDTH, Tk_Offset(TkText, highlightWidth), 0},
78      {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",      {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
79          DEF_TEXT_INSERT_BG, Tk_Offset(TkText, insertBorder), 0},          DEF_TEXT_INSERT_BG, Tk_Offset(TkText, insertBorder), 0},
80      {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",      {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
81          DEF_TEXT_INSERT_BD_COLOR, Tk_Offset(TkText, insertBorderWidth),          DEF_TEXT_INSERT_BD_COLOR, Tk_Offset(TkText, insertBorderWidth),
82          TK_CONFIG_COLOR_ONLY},          TK_CONFIG_COLOR_ONLY},
83      {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",      {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
84          DEF_TEXT_INSERT_BD_MONO, Tk_Offset(TkText, insertBorderWidth),          DEF_TEXT_INSERT_BD_MONO, Tk_Offset(TkText, insertBorderWidth),
85          TK_CONFIG_MONO_ONLY},          TK_CONFIG_MONO_ONLY},
86      {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",      {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
87          DEF_TEXT_INSERT_OFF_TIME, Tk_Offset(TkText, insertOffTime), 0},          DEF_TEXT_INSERT_OFF_TIME, Tk_Offset(TkText, insertOffTime), 0},
88      {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",      {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
89          DEF_TEXT_INSERT_ON_TIME, Tk_Offset(TkText, insertOnTime), 0},          DEF_TEXT_INSERT_ON_TIME, Tk_Offset(TkText, insertOnTime), 0},
90      {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",      {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
91          DEF_TEXT_INSERT_WIDTH, Tk_Offset(TkText, insertWidth), 0},          DEF_TEXT_INSERT_WIDTH, Tk_Offset(TkText, insertWidth), 0},
92      {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",      {TK_CONFIG_PIXELS, "-padx", "padX", "Pad",
93          DEF_TEXT_PADX, Tk_Offset(TkText, padX), 0},          DEF_TEXT_PADX, Tk_Offset(TkText, padX), 0},
94      {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",      {TK_CONFIG_PIXELS, "-pady", "padY", "Pad",
95          DEF_TEXT_PADY, Tk_Offset(TkText, padY), 0},          DEF_TEXT_PADY, Tk_Offset(TkText, padY), 0},
96      {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",      {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
97          DEF_TEXT_RELIEF, Tk_Offset(TkText, relief), 0},          DEF_TEXT_RELIEF, Tk_Offset(TkText, relief), 0},
98      {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",      {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
99          DEF_TEXT_SELECT_COLOR, Tk_Offset(TkText, selBorder),          DEF_TEXT_SELECT_COLOR, Tk_Offset(TkText, selBorder),
100          TK_CONFIG_COLOR_ONLY},          TK_CONFIG_COLOR_ONLY},
101      {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",      {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
102          DEF_TEXT_SELECT_MONO, Tk_Offset(TkText, selBorder),          DEF_TEXT_SELECT_MONO, Tk_Offset(TkText, selBorder),
103          TK_CONFIG_MONO_ONLY},          TK_CONFIG_MONO_ONLY},
104      {TK_CONFIG_STRING, "-selectborderwidth", "selectBorderWidth", "BorderWidth",      {TK_CONFIG_STRING, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
105          DEF_TEXT_SELECT_BD_COLOR, Tk_Offset(TkText, selBdString),          DEF_TEXT_SELECT_BD_COLOR, Tk_Offset(TkText, selBdString),
106          TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},          TK_CONFIG_COLOR_ONLY|TK_CONFIG_NULL_OK},
107      {TK_CONFIG_STRING, "-selectborderwidth", "selectBorderWidth", "BorderWidth",      {TK_CONFIG_STRING, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
108          DEF_TEXT_SELECT_BD_MONO, Tk_Offset(TkText, selBdString),          DEF_TEXT_SELECT_BD_MONO, Tk_Offset(TkText, selBdString),
109          TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},          TK_CONFIG_MONO_ONLY|TK_CONFIG_NULL_OK},
110      {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",      {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
111          DEF_TEXT_SELECT_FG_COLOR, Tk_Offset(TkText, selFgColorPtr),          DEF_TEXT_SELECT_FG_COLOR, Tk_Offset(TkText, selFgColorPtr),
112          TK_CONFIG_COLOR_ONLY},          TK_CONFIG_COLOR_ONLY},
113      {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",      {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
114          DEF_TEXT_SELECT_FG_MONO, Tk_Offset(TkText, selFgColorPtr),          DEF_TEXT_SELECT_FG_MONO, Tk_Offset(TkText, selFgColorPtr),
115          TK_CONFIG_MONO_ONLY},          TK_CONFIG_MONO_ONLY},
116      {TK_CONFIG_BOOLEAN, "-setgrid", "setGrid", "SetGrid",      {TK_CONFIG_BOOLEAN, "-setgrid", "setGrid", "SetGrid",
117          DEF_TEXT_SET_GRID, Tk_Offset(TkText, setGrid), 0},          DEF_TEXT_SET_GRID, Tk_Offset(TkText, setGrid), 0},
118      {TK_CONFIG_PIXELS, "-spacing1", "spacing1", "Spacing",      {TK_CONFIG_PIXELS, "-spacing1", "spacing1", "Spacing",
119          DEF_TEXT_SPACING1, Tk_Offset(TkText, spacing1),          DEF_TEXT_SPACING1, Tk_Offset(TkText, spacing1),
120          TK_CONFIG_DONT_SET_DEFAULT},          TK_CONFIG_DONT_SET_DEFAULT},
121      {TK_CONFIG_PIXELS, "-spacing2", "spacing2", "Spacing",      {TK_CONFIG_PIXELS, "-spacing2", "spacing2", "Spacing",
122          DEF_TEXT_SPACING2, Tk_Offset(TkText, spacing2),          DEF_TEXT_SPACING2, Tk_Offset(TkText, spacing2),
123          TK_CONFIG_DONT_SET_DEFAULT},          TK_CONFIG_DONT_SET_DEFAULT},
124      {TK_CONFIG_PIXELS, "-spacing3", "spacing3", "Spacing",      {TK_CONFIG_PIXELS, "-spacing3", "spacing3", "Spacing",
125          DEF_TEXT_SPACING3, Tk_Offset(TkText, spacing3),          DEF_TEXT_SPACING3, Tk_Offset(TkText, spacing3),
126          TK_CONFIG_DONT_SET_DEFAULT},          TK_CONFIG_DONT_SET_DEFAULT},
127      {TK_CONFIG_CUSTOM, "-state", "state", "State",      {TK_CONFIG_CUSTOM, "-state", "state", "State",
128          DEF_TEXT_STATE, Tk_Offset(TkText, state), 0, &stateOption},          DEF_TEXT_STATE, Tk_Offset(TkText, state), 0, &stateOption},
129      {TK_CONFIG_STRING, "-tabs", "tabs", "Tabs",      {TK_CONFIG_STRING, "-tabs", "tabs", "Tabs",
130          DEF_TEXT_TABS, Tk_Offset(TkText, tabOptionString), TK_CONFIG_NULL_OK},          DEF_TEXT_TABS, Tk_Offset(TkText, tabOptionString), TK_CONFIG_NULL_OK},
131      {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",      {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
132          DEF_TEXT_TAKE_FOCUS, Tk_Offset(TkText, takeFocus),          DEF_TEXT_TAKE_FOCUS, Tk_Offset(TkText, takeFocus),
133          TK_CONFIG_NULL_OK},          TK_CONFIG_NULL_OK},
134      {TK_CONFIG_INT, "-width", "width", "Width",      {TK_CONFIG_INT, "-width", "width", "Width",
135          DEF_TEXT_WIDTH, Tk_Offset(TkText, width), 0},          DEF_TEXT_WIDTH, Tk_Offset(TkText, width), 0},
136      {TK_CONFIG_CUSTOM,      {TK_CONFIG_CUSTOM,
137      "-wrap", "wrap", "Wrap",      "-wrap", "wrap", "Wrap",
138          DEF_TEXT_WRAP,          DEF_TEXT_WRAP,
139     Tk_Offset(TkText, wrapMode),     Tk_Offset(TkText, wrapMode),
140     0,     0,
141     &textWrapModeOption},     &textWrapModeOption},
142      {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",      {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
143          DEF_TEXT_XSCROLL_COMMAND, Tk_Offset(TkText, xScrollCmd),          DEF_TEXT_XSCROLL_COMMAND, Tk_Offset(TkText, xScrollCmd),
144          TK_CONFIG_NULL_OK},          TK_CONFIG_NULL_OK},
145      {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",      {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
146          DEF_TEXT_YSCROLL_COMMAND, Tk_Offset(TkText, yScrollCmd),          DEF_TEXT_YSCROLL_COMMAND, Tk_Offset(TkText, yScrollCmd),
147          TK_CONFIG_NULL_OK},          TK_CONFIG_NULL_OK},
148      {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,      {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
149          (char *) NULL, 0, 0}          (char *) NULL, 0, 0}
150  };  };
151    
152  /*  /*
153   * Boolean variable indicating whether or not special debugging code   * Boolean variable indicating whether or not special debugging code
154   * should be executed.   * should be executed.
155   */   */
156    
157  int tkTextDebug = 0;  int tkTextDebug = 0;
158    
159  /*  /*
160   * Custom options for handling "-wrap":   * Custom options for handling "-wrap":
161   */   */
162    
163  static int              WrapModeParseProc _ANSI_ARGS_((ClientData clientData,  static int              WrapModeParseProc _ANSI_ARGS_((ClientData clientData,
164                              Tcl_Interp *interp, Tk_Window tkwin, char *value,                              Tcl_Interp *interp, Tk_Window tkwin, char *value,
165                              char *widgRec, int offset));                              char *widgRec, int offset));
166  static char *           WrapModePrintProc _ANSI_ARGS_((ClientData clientData,  static char *           WrapModePrintProc _ANSI_ARGS_((ClientData clientData,
167                              Tk_Window tkwin, char *widgRec, int offset,                              Tk_Window tkwin, char *widgRec, int offset,
168                              Tcl_FreeProc **freeProcPtr));                              Tcl_FreeProc **freeProcPtr));
169    
170  Tk_CustomOption textWrapModeOption = {  Tk_CustomOption textWrapModeOption = {
171      WrapModeParseProc,      WrapModeParseProc,
172      WrapModePrintProc,      WrapModePrintProc,
173      (ClientData) NULL      (ClientData) NULL
174  };  };
175    
176  /*  /*
177   *--------------------------------------------------------------   *--------------------------------------------------------------
178   *   *
179   * WrapModeParseProc --   * WrapModeParseProc --
180   *   *
181   *      This procedure is invoked during option processing to handle   *      This procedure is invoked during option processing to handle
182   *      "-wrap" options for text widgets.   *      "-wrap" options for text widgets.
183   *   *
184   * Results:   * Results:
185   *      A standard Tcl return value.   *      A standard Tcl return value.
186   *   *
187   * Side effects:   * Side effects:
188   *      The wrap mode for a given item gets replaced by the wrap mode   *      The wrap mode for a given item gets replaced by the wrap mode
189   *      indicated in the value argument.   *      indicated in the value argument.
190   *   *
191   *--------------------------------------------------------------   *--------------------------------------------------------------
192   */   */
193    
194  static int  static int
195  WrapModeParseProc(clientData, interp, tkwin, value, widgRec, offset)  WrapModeParseProc(clientData, interp, tkwin, value, widgRec, offset)
196      ClientData clientData;              /* some flags.*/      ClientData clientData;              /* some flags.*/
197      Tcl_Interp *interp;                 /* Used for reporting errors. */      Tcl_Interp *interp;                 /* Used for reporting errors. */
198      Tk_Window tkwin;                    /* Window containing canvas widget. */      Tk_Window tkwin;                    /* Window containing canvas widget. */
199      char *value;                        /* Value of option (list of tag      char *value;                        /* Value of option (list of tag
200                                           * names). */                                           * names). */
201      char *widgRec;                      /* Pointer to record for item. */      char *widgRec;                      /* Pointer to record for item. */
202      int offset;                         /* Offset into item. */      int offset;                         /* Offset into item. */
203  {  {
204      int c;      int c;
205      size_t length;      size_t length;
206    
207      register TkWrapMode *wrapPtr = (TkWrapMode *) (widgRec + offset);      register TkWrapMode *wrapPtr = (TkWrapMode *) (widgRec + offset);
208    
209      if(value == NULL || *value == 0) {      if(value == NULL || *value == 0) {
210          *wrapPtr = TEXT_WRAPMODE_NULL;          *wrapPtr = TEXT_WRAPMODE_NULL;
211          return TCL_OK;          return TCL_OK;
212      }      }
213    
214      c = value[0];      c = value[0];
215      length = strlen(value);      length = strlen(value);
216    
217      if ((c == 'c') && (strncmp(value, "char", length) == 0)) {      if ((c == 'c') && (strncmp(value, "char", length) == 0)) {
218          *wrapPtr = TEXT_WRAPMODE_CHAR;          *wrapPtr = TEXT_WRAPMODE_CHAR;
219          return TCL_OK;          return TCL_OK;
220      }      }
221      if ((c == 'n') && (strncmp(value, "none", length) == 0)) {      if ((c == 'n') && (strncmp(value, "none", length) == 0)) {
222          *wrapPtr = TEXT_WRAPMODE_NONE;          *wrapPtr = TEXT_WRAPMODE_NONE;
223          return TCL_OK;          return TCL_OK;
224      }      }
225      if ((c == 'w') && (strncmp(value, "word", length) == 0)) {      if ((c == 'w') && (strncmp(value, "word", length) == 0)) {
226          *wrapPtr = TEXT_WRAPMODE_WORD;          *wrapPtr = TEXT_WRAPMODE_WORD;
227          return TCL_OK;          return TCL_OK;
228      }      }
229      Tcl_AppendResult(interp, "bad wrap mode \"", value,      Tcl_AppendResult(interp, "bad wrap mode \"", value,
230              "\": must be char, none, or word",              "\": must be char, none, or word",
231              (char *) NULL);              (char *) NULL);
232      *wrapPtr = TEXT_WRAPMODE_CHAR;      *wrapPtr = TEXT_WRAPMODE_CHAR;
233      return TCL_ERROR;      return TCL_ERROR;
234  }  }
235    
236  /*  /*
237   *--------------------------------------------------------------   *--------------------------------------------------------------
238   *   *
239   * WrapModePrintProc --   * WrapModePrintProc --
240   *   *
241   *      This procedure is invoked by the Tk configuration code   *      This procedure is invoked by the Tk configuration code
242   *      to produce a printable string for the "-wrap" configuration   *      to produce a printable string for the "-wrap" configuration
243   *      option for canvas items.   *      option for canvas items.
244   *   *
245   * Results:   * Results:
246   *      The return value is a string describing the state for   *      The return value is a string describing the state for
247   *      the item referred to by "widgRec".  In addition, *freeProcPtr   *      the item referred to by "widgRec".  In addition, *freeProcPtr
248   *      is filled in with the address of a procedure to call to free   *      is filled in with the address of a procedure to call to free
249   *      the result string when it's no longer needed (or NULL to   *      the result string when it's no longer needed (or NULL to
250   *      indicate that the string doesn't need to be freed).   *      indicate that the string doesn't need to be freed).
251   *   *
252   * Side effects:   * Side effects:
253   *      None.   *      None.
254   *   *
255   *--------------------------------------------------------------   *--------------------------------------------------------------
256   */   */
257    
258  static char *  static char *
259  WrapModePrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)  WrapModePrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
260      ClientData clientData;              /* Ignored. */      ClientData clientData;              /* Ignored. */
261      Tk_Window tkwin;                    /* Window containing canvas widget. */      Tk_Window tkwin;                    /* Window containing canvas widget. */
262      char *widgRec;                      /* Pointer to record for item. */      char *widgRec;                      /* Pointer to record for item. */
263      int offset;                         /* Ignored. */      int offset;                         /* Ignored. */
264      Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with      Tcl_FreeProc **freeProcPtr;         /* Pointer to variable to fill in with
265                                           * information about how to reclaim                                           * information about how to reclaim
266                                           * storage for return string. */                                           * storage for return string. */
267  {  {
268      register TkWrapMode *wrapPtr = (TkWrapMode *) (widgRec + offset);      register TkWrapMode *wrapPtr = (TkWrapMode *) (widgRec + offset);
269    
270      if (*wrapPtr==TEXT_WRAPMODE_CHAR) {      if (*wrapPtr==TEXT_WRAPMODE_CHAR) {
271          return "char";          return "char";
272      } else if (*wrapPtr==TEXT_WRAPMODE_NONE) {      } else if (*wrapPtr==TEXT_WRAPMODE_NONE) {
273          return "none";          return "none";
274      } else if (*wrapPtr==TEXT_WRAPMODE_WORD) {      } else if (*wrapPtr==TEXT_WRAPMODE_WORD) {
275          return "word";          return "word";
276      } else {      } else {
277          return "";          return "";
278      }      }
279  }  }
280    
281  /*  /*
282   * Forward declarations for procedures defined later in this file:   * Forward declarations for procedures defined later in this file:
283   */   */
284    
285  static int              ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,  static int              ConfigureText _ANSI_ARGS_((Tcl_Interp *interp,
286                              TkText *textPtr, int argc, char **argv, int flags));                              TkText *textPtr, int argc, char **argv, int flags));
287  static int              DeleteChars _ANSI_ARGS_((TkText *textPtr,  static int              DeleteChars _ANSI_ARGS_((TkText *textPtr,
288                              char *index1String, char *index2String));                              char *index1String, char *index2String));
289  static void             DestroyText _ANSI_ARGS_((char *memPtr));  static void             DestroyText _ANSI_ARGS_((char *memPtr));
290  static void             InsertChars _ANSI_ARGS_((TkText *textPtr,  static void             InsertChars _ANSI_ARGS_((TkText *textPtr,
291                              TkTextIndex *indexPtr, char *string));                              TkTextIndex *indexPtr, char *string));
292  static void             TextBlinkProc _ANSI_ARGS_((ClientData clientData));  static void             TextBlinkProc _ANSI_ARGS_((ClientData clientData));
293  static void             TextCmdDeletedProc _ANSI_ARGS_((  static void             TextCmdDeletedProc _ANSI_ARGS_((
294                              ClientData clientData));                              ClientData clientData));
295  static void             TextEventProc _ANSI_ARGS_((ClientData clientData,  static void             TextEventProc _ANSI_ARGS_((ClientData clientData,
296                              XEvent *eventPtr));                              XEvent *eventPtr));
297  static int              TextFetchSelection _ANSI_ARGS_((ClientData clientData,  static int              TextFetchSelection _ANSI_ARGS_((ClientData clientData,
298                              int offset, char *buffer, int maxBytes));                              int offset, char *buffer, int maxBytes));
299  static int              TextSearchCmd _ANSI_ARGS_((TkText *textPtr,  static int              TextSearchCmd _ANSI_ARGS_((TkText *textPtr,
300                              Tcl_Interp *interp, int argc, char **argv));                              Tcl_Interp *interp, int argc, char **argv));
301  static int              TextWidgetCmd _ANSI_ARGS_((ClientData clientData,  static int              TextWidgetCmd _ANSI_ARGS_((ClientData clientData,
302                              Tcl_Interp *interp, int argc, char **argv));                              Tcl_Interp *interp, int argc, char **argv));
303  static void             TextWorldChanged _ANSI_ARGS_((  static void             TextWorldChanged _ANSI_ARGS_((
304                              ClientData instanceData));                              ClientData instanceData));
305  static int              TextDumpCmd _ANSI_ARGS_((TkText *textPtr,  static int              TextDumpCmd _ANSI_ARGS_((TkText *textPtr,
306                              Tcl_Interp *interp, int argc, char **argv));                              Tcl_Interp *interp, int argc, char **argv));
307  static void             DumpLine _ANSI_ARGS_((Tcl_Interp *interp,  static void             DumpLine _ANSI_ARGS_((Tcl_Interp *interp,
308                              TkText *textPtr, int what, TkTextLine *linePtr,                              TkText *textPtr, int what, TkTextLine *linePtr,
309                              int start, int end, int lineno, char *command));                              int start, int end, int lineno, char *command));
310  static int              DumpSegment _ANSI_ARGS_((Tcl_Interp *interp, char *key,  static int              DumpSegment _ANSI_ARGS_((Tcl_Interp *interp, char *key,
311                              char *value, char * command, TkTextIndex *index,                              char *value, char * command, TkTextIndex *index,
312                              int what));                              int what));
313    
314  /*  /*
315   * The structure below defines text class behavior by means of procedures   * The structure below defines text class behavior by means of procedures
316   * that can be invoked from generic window code.   * that can be invoked from generic window code.
317   */   */
318    
319  static TkClassProcs textClass = {  static TkClassProcs textClass = {
320      NULL,                       /* createProc. */      NULL,                       /* createProc. */
321      TextWorldChanged,           /* geometryProc. */      TextWorldChanged,           /* geometryProc. */
322      NULL                        /* modalProc. */      NULL                        /* modalProc. */
323  };  };
324    
325    
326  /*  /*
327   *--------------------------------------------------------------   *--------------------------------------------------------------
328   *   *
329   * Tk_TextCmd --   * Tk_TextCmd --
330   *   *
331   *      This procedure is invoked to process the "text" Tcl command.   *      This procedure is invoked to process the "text" Tcl command.
332   *      See the user documentation for details on what it does.   *      See the user documentation for details on what it does.
333   *   *
334   * Results:   * Results:
335   *      A standard Tcl result.   *      A standard Tcl result.
336   *   *
337   * Side effects:   * Side effects:
338   *      See the user documentation.   *      See the user documentation.
339   *   *
340   *--------------------------------------------------------------   *--------------------------------------------------------------
341   */   */
342    
343  int  int
344  Tk_TextCmd(clientData, interp, argc, argv)  Tk_TextCmd(clientData, interp, argc, argv)
345      ClientData clientData;      /* Main window associated with      ClientData clientData;      /* Main window associated with
346                                   * interpreter. */                                   * interpreter. */
347      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
348      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
349      char **argv;                /* Argument strings. */      char **argv;                /* Argument strings. */
350  {  {
351      Tk_Window tkwin = (Tk_Window) clientData;      Tk_Window tkwin = (Tk_Window) clientData;
352      Tk_Window new;      Tk_Window new;
353      register TkText *textPtr;      register TkText *textPtr;
354      TkTextIndex startIndex;      TkTextIndex startIndex;
355    
356      if (argc < 2) {      if (argc < 2) {
357          Tcl_AppendResult(interp, "wrong # args: should be \"",          Tcl_AppendResult(interp, "wrong # args: should be \"",
358                  argv[0], " pathName ?options?\"", (char *) NULL);                  argv[0], " pathName ?options?\"", (char *) NULL);
359          return TCL_ERROR;          return TCL_ERROR;
360      }      }
361    
362      /*      /*
363       * Create the window.       * Create the window.
364       */       */
365    
366      new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);      new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
367      if (new == NULL) {      if (new == NULL) {
368          return TCL_ERROR;          return TCL_ERROR;
369      }      }
370    
371      textPtr = (TkText *) ckalloc(sizeof(TkText));      textPtr = (TkText *) ckalloc(sizeof(TkText));
372      textPtr->tkwin = new;      textPtr->tkwin = new;
373      textPtr->display = Tk_Display(new);      textPtr->display = Tk_Display(new);
374      textPtr->interp = interp;      textPtr->interp = interp;
375      textPtr->widgetCmd = Tcl_CreateCommand(interp,      textPtr->widgetCmd = Tcl_CreateCommand(interp,
376              Tk_PathName(textPtr->tkwin), TextWidgetCmd,              Tk_PathName(textPtr->tkwin), TextWidgetCmd,
377              (ClientData) textPtr, TextCmdDeletedProc);              (ClientData) textPtr, TextCmdDeletedProc);
378      textPtr->tree = TkBTreeCreate(textPtr);      textPtr->tree = TkBTreeCreate(textPtr);
379      Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);      Tcl_InitHashTable(&textPtr->tagTable, TCL_STRING_KEYS);
380      textPtr->numTags = 0;      textPtr->numTags = 0;
381      Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);      Tcl_InitHashTable(&textPtr->markTable, TCL_STRING_KEYS);
382      Tcl_InitHashTable(&textPtr->windowTable, TCL_STRING_KEYS);      Tcl_InitHashTable(&textPtr->windowTable, TCL_STRING_KEYS);
383      Tcl_InitHashTable(&textPtr->imageTable, TCL_STRING_KEYS);      Tcl_InitHashTable(&textPtr->imageTable, TCL_STRING_KEYS);
384      textPtr->state = TK_STATE_NORMAL;      textPtr->state = TK_STATE_NORMAL;
385      textPtr->border = NULL;      textPtr->border = NULL;
386      textPtr->borderWidth = 0;      textPtr->borderWidth = 0;
387      textPtr->padX = 0;      textPtr->padX = 0;
388      textPtr->padY = 0;      textPtr->padY = 0;
389      textPtr->relief = TK_RELIEF_FLAT;      textPtr->relief = TK_RELIEF_FLAT;
390      textPtr->highlightWidth = 0;      textPtr->highlightWidth = 0;
391      textPtr->highlightBgColorPtr = NULL;      textPtr->highlightBgColorPtr = NULL;
392      textPtr->highlightColorPtr = NULL;      textPtr->highlightColorPtr = NULL;
393      textPtr->cursor = None;      textPtr->cursor = None;
394      textPtr->fgColor = NULL;      textPtr->fgColor = NULL;
395      textPtr->tkfont = NULL;      textPtr->tkfont = NULL;
396      textPtr->charWidth = 1;      textPtr->charWidth = 1;
397      textPtr->spacing1 = 0;      textPtr->spacing1 = 0;
398      textPtr->spacing2 = 0;      textPtr->spacing2 = 0;
399      textPtr->spacing3 = 0;      textPtr->spacing3 = 0;
400      textPtr->tabOptionString = NULL;      textPtr->tabOptionString = NULL;
401      textPtr->tabArrayPtr = NULL;      textPtr->tabArrayPtr = NULL;
402      textPtr->wrapMode = TEXT_WRAPMODE_CHAR;      textPtr->wrapMode = TEXT_WRAPMODE_CHAR;
403      textPtr->width = 0;      textPtr->width = 0;
404      textPtr->height = 0;      textPtr->height = 0;
405      textPtr->setGrid = 0;      textPtr->setGrid = 0;
406      textPtr->prevWidth = Tk_Width(new);      textPtr->prevWidth = Tk_Width(new);
407      textPtr->prevHeight = Tk_Height(new);      textPtr->prevHeight = Tk_Height(new);
408      TkTextCreateDInfo(textPtr);      TkTextCreateDInfo(textPtr);
409      TkTextMakeByteIndex(textPtr->tree, 0, 0, &startIndex);      TkTextMakeByteIndex(textPtr->tree, 0, 0, &startIndex);
410      TkTextSetYView(textPtr, &startIndex, 0);      TkTextSetYView(textPtr, &startIndex, 0);
411      textPtr->selTagPtr = NULL;      textPtr->selTagPtr = NULL;
412      textPtr->selBorder = NULL;      textPtr->selBorder = NULL;
413      textPtr->selBdString = NULL;      textPtr->selBdString = NULL;
414      textPtr->selFgColorPtr = NULL;      textPtr->selFgColorPtr = NULL;
415      textPtr->exportSelection = 1;      textPtr->exportSelection = 1;
416      textPtr->abortSelections = 0;      textPtr->abortSelections = 0;
417      textPtr->insertMarkPtr = NULL;      textPtr->insertMarkPtr = NULL;
418      textPtr->insertBorder = NULL;      textPtr->insertBorder = NULL;
419      textPtr->insertWidth = 0;      textPtr->insertWidth = 0;
420      textPtr->insertBorderWidth = 0;      textPtr->insertBorderWidth = 0;
421      textPtr->insertOnTime = 0;      textPtr->insertOnTime = 0;
422      textPtr->insertOffTime = 0;      textPtr->insertOffTime = 0;
423      textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;      textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
424      textPtr->bindingTable = NULL;      textPtr->bindingTable = NULL;
425      textPtr->currentMarkPtr = NULL;      textPtr->currentMarkPtr = NULL;
426      textPtr->pickEvent.type = LeaveNotify;      textPtr->pickEvent.type = LeaveNotify;
427      textPtr->pickEvent.xcrossing.x = 0;      textPtr->pickEvent.xcrossing.x = 0;
428      textPtr->pickEvent.xcrossing.y = 0;      textPtr->pickEvent.xcrossing.y = 0;
429      textPtr->numCurTags = 0;      textPtr->numCurTags = 0;
430      textPtr->curTagArrayPtr = NULL;      textPtr->curTagArrayPtr = NULL;
431      textPtr->takeFocus = NULL;      textPtr->takeFocus = NULL;
432      textPtr->xScrollCmd = NULL;      textPtr->xScrollCmd = NULL;
433      textPtr->yScrollCmd = NULL;      textPtr->yScrollCmd = NULL;
434      textPtr->flags = 0;      textPtr->flags = 0;
435    
436      /*      /*
437       * Create the "sel" tag and the "current" and "insert" marks.       * Create the "sel" tag and the "current" and "insert" marks.
438       */       */
439    
440      textPtr->selTagPtr = TkTextCreateTag(textPtr, "sel");      textPtr->selTagPtr = TkTextCreateTag(textPtr, "sel");
441      textPtr->selTagPtr->reliefString =      textPtr->selTagPtr->reliefString =
442              (char *) ckalloc(sizeof(DEF_TEXT_SELECT_RELIEF));              (char *) ckalloc(sizeof(DEF_TEXT_SELECT_RELIEF));
443      strcpy(textPtr->selTagPtr->reliefString, DEF_TEXT_SELECT_RELIEF);      strcpy(textPtr->selTagPtr->reliefString, DEF_TEXT_SELECT_RELIEF);
444      textPtr->selTagPtr->relief = TK_RELIEF_RAISED;      textPtr->selTagPtr->relief = TK_RELIEF_RAISED;
445      textPtr->currentMarkPtr = TkTextSetMark(textPtr, "current", &startIndex);      textPtr->currentMarkPtr = TkTextSetMark(textPtr, "current", &startIndex);
446      textPtr->insertMarkPtr = TkTextSetMark(textPtr, "insert", &startIndex);      textPtr->insertMarkPtr = TkTextSetMark(textPtr, "insert", &startIndex);
447    
448      Tk_SetClass(textPtr->tkwin, "Text");      Tk_SetClass(textPtr->tkwin, "Text");
449      TkSetClassProcs(textPtr->tkwin, &textClass, (ClientData) textPtr);      TkSetClassProcs(textPtr->tkwin, &textClass, (ClientData) textPtr);
450      Tk_CreateEventHandler(textPtr->tkwin,      Tk_CreateEventHandler(textPtr->tkwin,
451              ExposureMask|StructureNotifyMask|FocusChangeMask,              ExposureMask|StructureNotifyMask|FocusChangeMask,
452              TextEventProc, (ClientData) textPtr);              TextEventProc, (ClientData) textPtr);
453      Tk_CreateEventHandler(textPtr->tkwin, KeyPressMask|KeyReleaseMask      Tk_CreateEventHandler(textPtr->tkwin, KeyPressMask|KeyReleaseMask
454              |ButtonPressMask|ButtonReleaseMask|EnterWindowMask              |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
455              |LeaveWindowMask|PointerMotionMask|VirtualEventMask,              |LeaveWindowMask|PointerMotionMask|VirtualEventMask,
456              TkTextBindProc, (ClientData) textPtr);              TkTextBindProc, (ClientData) textPtr);
457      Tk_CreateSelHandler(textPtr->tkwin, XA_PRIMARY, XA_STRING,      Tk_CreateSelHandler(textPtr->tkwin, XA_PRIMARY, XA_STRING,
458              TextFetchSelection, (ClientData) textPtr, XA_STRING);              TextFetchSelection, (ClientData) textPtr, XA_STRING);
459      if (ConfigureText(interp, textPtr, argc-2, argv+2, 0) != TCL_OK) {      if (ConfigureText(interp, textPtr, argc-2, argv+2, 0) != TCL_OK) {
460          Tk_DestroyWindow(textPtr->tkwin);          Tk_DestroyWindow(textPtr->tkwin);
461          return TCL_ERROR;          return TCL_ERROR;
462      }      }
463      Tcl_SetResult(interp, Tk_PathName(textPtr->tkwin), TCL_STATIC);      Tcl_SetResult(interp, Tk_PathName(textPtr->tkwin), TCL_STATIC);
464    
465      return TCL_OK;      return TCL_OK;
466  }  }
467    
468  /*  /*
469   *--------------------------------------------------------------   *--------------------------------------------------------------
470   *   *
471   * TextWidgetCmd --   * TextWidgetCmd --
472   *   *
473   *      This procedure is invoked to process the Tcl command   *      This procedure is invoked to process the Tcl command
474   *      that corresponds to a text widget.  See the user   *      that corresponds to a text widget.  See the user
475   *      documentation for details on what it does.   *      documentation for details on what it does.
476   *   *
477   * Results:   * Results:
478   *      A standard Tcl result.   *      A standard Tcl result.
479   *   *
480   * Side effects:   * Side effects:
481   *      See the user documentation.   *      See the user documentation.
482   *   *
483   *--------------------------------------------------------------   *--------------------------------------------------------------
484   */   */
485    
486  static int  static int
487  TextWidgetCmd(clientData, interp, argc, argv)  TextWidgetCmd(clientData, interp, argc, argv)
488      ClientData clientData;      /* Information about text widget. */      ClientData clientData;      /* Information about text widget. */
489      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
490      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
491      char **argv;                /* Argument strings. */      char **argv;                /* Argument strings. */
492  {  {
493      register TkText *textPtr = (TkText *) clientData;      register TkText *textPtr = (TkText *) clientData;
494      int result = TCL_OK;      int result = TCL_OK;
495      size_t length;      size_t length;
496      int c;      int c;
497      TkTextIndex index1, index2;      TkTextIndex index1, index2;
498    
499      if (argc < 2) {      if (argc < 2) {
500          Tcl_AppendResult(interp, "wrong # args: should be \"",          Tcl_AppendResult(interp, "wrong # args: should be \"",
501                  argv[0], " option ?arg arg ...?\"", (char *) NULL);                  argv[0], " option ?arg arg ...?\"", (char *) NULL);
502          return TCL_ERROR;          return TCL_ERROR;
503      }      }
504      Tcl_Preserve((ClientData) textPtr);      Tcl_Preserve((ClientData) textPtr);
505      c = argv[1][0];      c = argv[1][0];
506      length = strlen(argv[1]);      length = strlen(argv[1]);
507      if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {      if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
508          int x, y, width, height;          int x, y, width, height;
509    
510          if (argc != 3) {          if (argc != 3) {
511              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
512                      argv[0], " bbox index\"", (char *) NULL);                      argv[0], " bbox index\"", (char *) NULL);
513              result = TCL_ERROR;              result = TCL_ERROR;
514              goto done;              goto done;
515          }          }
516          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
517              result = TCL_ERROR;              result = TCL_ERROR;
518              goto done;              goto done;
519          }          }
520          if (TkTextCharBbox(textPtr, &index1, &x, &y, &width, &height) == 0) {          if (TkTextCharBbox(textPtr, &index1, &x, &y, &width, &height) == 0) {
521              char buf[TCL_INTEGER_SPACE * 4];              char buf[TCL_INTEGER_SPACE * 4];
522                            
523              sprintf(buf, "%d %d %d %d", x, y, width, height);              sprintf(buf, "%d %d %d %d", x, y, width, height);
524              Tcl_SetResult(interp, buf, TCL_VOLATILE);              Tcl_SetResult(interp, buf, TCL_VOLATILE);
525          }          }
526      } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)      } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
527              && (length >= 2)) {              && (length >= 2)) {
528          if (argc != 3) {          if (argc != 3) {
529              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
530                      argv[0], " cget option\"",                      argv[0], " cget option\"",
531                      (char *) NULL);                      (char *) NULL);
532              result = TCL_ERROR;              result = TCL_ERROR;
533              goto done;              goto done;
534          }          }
535          result = Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,          result = Tk_ConfigureValue(interp, textPtr->tkwin, configSpecs,
536                  (char *) textPtr, argv[2], 0);                  (char *) textPtr, argv[2], 0);
537      } else if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)      } else if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)
538              && (length >= 3)) {              && (length >= 3)) {
539          int relation, value;          int relation, value;
540          char *p;          char *p;
541    
542          if (argc != 5) {          if (argc != 5) {
543              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
544                      argv[0], " compare index1 op index2\"", (char *) NULL);                      argv[0], " compare index1 op index2\"", (char *) NULL);
545              result = TCL_ERROR;              result = TCL_ERROR;
546              goto done;              goto done;
547          }          }
548          if ((TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK)          if ((TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK)
549                  || (TkTextGetIndex(interp, textPtr, argv[4], &index2)                  || (TkTextGetIndex(interp, textPtr, argv[4], &index2)
550                  != TCL_OK)) {                  != TCL_OK)) {
551              result = TCL_ERROR;              result = TCL_ERROR;
552              goto done;              goto done;
553          }          }
554          relation = TkTextIndexCmp(&index1, &index2);          relation = TkTextIndexCmp(&index1, &index2);
555          p = argv[3];          p = argv[3];
556          if (p[0] == '<') {          if (p[0] == '<') {
557                  value = (relation < 0);                  value = (relation < 0);
558              if ((p[1] == '=') && (p[2] == 0)) {              if ((p[1] == '=') && (p[2] == 0)) {
559                  value = (relation <= 0);                  value = (relation <= 0);
560              } else if (p[1] != 0) {              } else if (p[1] != 0) {
561                  compareError:                  compareError:
562                  Tcl_AppendResult(interp, "bad comparison operator \"",                  Tcl_AppendResult(interp, "bad comparison operator \"",
563                          argv[3], "\": must be <, <=, ==, >=, >, or !=",                          argv[3], "\": must be <, <=, ==, >=, >, or !=",
564                          (char *) NULL);                          (char *) NULL);
565                  result = TCL_ERROR;                  result = TCL_ERROR;
566                  goto done;                  goto done;
567              }              }
568          } else if (p[0] == '>') {          } else if (p[0] == '>') {
569                  value = (relation > 0);                  value = (relation > 0);
570              if ((p[1] == '=') && (p[2] == 0)) {              if ((p[1] == '=') && (p[2] == 0)) {
571                  value = (relation >= 0);                  value = (relation >= 0);
572              } else if (p[1] != 0) {              } else if (p[1] != 0) {
573                  goto compareError;                  goto compareError;
574              }              }
575          } else if ((p[0] == '=') && (p[1] == '=') && (p[2] == 0)) {          } else if ((p[0] == '=') && (p[1] == '=') && (p[2] == 0)) {
576              value = (relation == 0);              value = (relation == 0);
577          } else if ((p[0] == '!') && (p[1] == '=') && (p[2] == 0)) {          } else if ((p[0] == '!') && (p[1] == '=') && (p[2] == 0)) {
578              value = (relation != 0);              value = (relation != 0);
579          } else {          } else {
580              goto compareError;              goto compareError;
581          }          }
582          Tcl_SetResult(interp, ((value) ? "1" : "0"), TCL_STATIC);          Tcl_SetResult(interp, ((value) ? "1" : "0"), TCL_STATIC);
583      } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)      } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
584              && (length >= 3)) {              && (length >= 3)) {
585          if (argc == 2) {          if (argc == 2) {
586              result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,              result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
587                      (char *) textPtr, (char *) NULL, 0);                      (char *) textPtr, (char *) NULL, 0);
588          } else if (argc == 3) {          } else if (argc == 3) {
589              result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,              result = Tk_ConfigureInfo(interp, textPtr->tkwin, configSpecs,
590                      (char *) textPtr, argv[2], 0);                      (char *) textPtr, argv[2], 0);
591          } else {          } else {
592              result = ConfigureText(interp, textPtr, argc-2, argv+2,              result = ConfigureText(interp, textPtr, argc-2, argv+2,
593                      TK_CONFIG_ARGV_ONLY);                      TK_CONFIG_ARGV_ONLY);
594          }          }
595      } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)      } else if ((c == 'd') && (strncmp(argv[1], "debug", length) == 0)
596              && (length >= 3)) {              && (length >= 3)) {
597          if (argc > 3) {          if (argc > 3) {
598              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
599                      argv[0], " debug boolean\"", (char *) NULL);                      argv[0], " debug boolean\"", (char *) NULL);
600              result = TCL_ERROR;              result = TCL_ERROR;
601              goto done;              goto done;
602          }          }
603          if (argc == 2) {          if (argc == 2) {
604              Tcl_SetResult(interp, ((tkBTreeDebug) ? "1" : "0"), TCL_STATIC);              Tcl_SetResult(interp, ((tkBTreeDebug) ? "1" : "0"), TCL_STATIC);
605          } else {          } else {
606              if (Tcl_GetBoolean(interp, argv[2], &tkBTreeDebug) != TCL_OK) {              if (Tcl_GetBoolean(interp, argv[2], &tkBTreeDebug) != TCL_OK) {
607                  result = TCL_ERROR;                  result = TCL_ERROR;
608                  goto done;                  goto done;
609              }              }
610              tkTextDebug = tkBTreeDebug;              tkTextDebug = tkBTreeDebug;
611          }          }
612      } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)      } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
613              && (length >= 3)) {              && (length >= 3)) {
614          if ((argc != 3) && (argc != 4)) {          if ((argc != 3) && (argc != 4)) {
615              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
616                      argv[0], " delete index1 ?index2?\"", (char *) NULL);                      argv[0], " delete index1 ?index2?\"", (char *) NULL);
617              result = TCL_ERROR;              result = TCL_ERROR;
618              goto done;              goto done;
619          }          }
620          if (textPtr->state == TK_STATE_NORMAL) {          if (textPtr->state == TK_STATE_NORMAL) {
621              result = DeleteChars(textPtr, argv[2],              result = DeleteChars(textPtr, argv[2],
622                      (argc == 4) ? argv[3] : (char *) NULL);                      (argc == 4) ? argv[3] : (char *) NULL);
623          }          }
624      } else if ((c == 'd') && (strncmp(argv[1], "dlineinfo", length) == 0)      } else if ((c == 'd') && (strncmp(argv[1], "dlineinfo", length) == 0)
625              && (length >= 2)) {              && (length >= 2)) {
626          int x, y, width, height, base;          int x, y, width, height, base;
627    
628          if (argc != 3) {          if (argc != 3) {
629              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
630                      argv[0], " dlineinfo index\"", (char *) NULL);                      argv[0], " dlineinfo index\"", (char *) NULL);
631              result = TCL_ERROR;              result = TCL_ERROR;
632              goto done;              goto done;
633          }          }
634          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
635              result = TCL_ERROR;              result = TCL_ERROR;
636              goto done;              goto done;
637          }          }
638          if (TkTextDLineInfo(textPtr, &index1, &x, &y, &width, &height, &base)          if (TkTextDLineInfo(textPtr, &index1, &x, &y, &width, &height, &base)
639                  == 0) {                  == 0) {
640              char buf[TCL_INTEGER_SPACE * 5];              char buf[TCL_INTEGER_SPACE * 5];
641                            
642              sprintf(buf, "%d %d %d %d %d", x, y, width, height, base);              sprintf(buf, "%d %d %d %d %d", x, y, width, height, base);
643              Tcl_SetResult(interp, buf, TCL_VOLATILE);              Tcl_SetResult(interp, buf, TCL_VOLATILE);
644          }          }
645      } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {      } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
646          if ((argc != 3) && (argc != 4)) {          if ((argc != 3) && (argc != 4)) {
647              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
648                      argv[0], " get index1 ?index2?\"", (char *) NULL);                      argv[0], " get index1 ?index2?\"", (char *) NULL);
649              result = TCL_ERROR;              result = TCL_ERROR;
650              goto done;              goto done;
651          }          }
652          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
653              result = TCL_ERROR;              result = TCL_ERROR;
654              goto done;              goto done;
655          }          }
656          if (argc == 3) {          if (argc == 3) {
657              index2 = index1;              index2 = index1;
658              TkTextIndexForwChars(&index2, 1, &index2);              TkTextIndexForwChars(&index2, 1, &index2);
659          } else if (TkTextGetIndex(interp, textPtr, argv[3], &index2)          } else if (TkTextGetIndex(interp, textPtr, argv[3], &index2)
660                  != TCL_OK) {                  != TCL_OK) {
661              result = TCL_ERROR;              result = TCL_ERROR;
662              goto done;              goto done;
663          }          }
664          if (TkTextIndexCmp(&index1, &index2) >= 0) {          if (TkTextIndexCmp(&index1, &index2) >= 0) {
665              goto done;              goto done;
666          }          }
667          while (1) {          while (1) {
668              int offset, last, savedChar;              int offset, last, savedChar;
669              TkTextSegment *segPtr;              TkTextSegment *segPtr;
670    
671              segPtr = TkTextIndexToSeg(&index1, &offset);              segPtr = TkTextIndexToSeg(&index1, &offset);
672              last = segPtr->size;              last = segPtr->size;
673              if (index1.linePtr == index2.linePtr) {              if (index1.linePtr == index2.linePtr) {
674                  int last2;                  int last2;
675    
676                  if (index2.byteIndex == index1.byteIndex) {                  if (index2.byteIndex == index1.byteIndex) {
677                      break;                      break;
678                  }                  }
679                  last2 = index2.byteIndex - index1.byteIndex + offset;                  last2 = index2.byteIndex - index1.byteIndex + offset;
680                  if (last2 < last) {                  if (last2 < last) {
681                      last = last2;                      last = last2;
682                  }                  }
683              }              }
684              if (segPtr->typePtr == &tkTextCharType) {              if (segPtr->typePtr == &tkTextCharType) {
685                  savedChar = segPtr->body.chars[last];                  savedChar = segPtr->body.chars[last];
686                  segPtr->body.chars[last] = 0;                  segPtr->body.chars[last] = 0;
687                  Tcl_AppendResult(interp, segPtr->body.chars + offset,                  Tcl_AppendResult(interp, segPtr->body.chars + offset,
688                          (char *) NULL);                          (char *) NULL);
689                  segPtr->body.chars[last] = savedChar;                  segPtr->body.chars[last] = savedChar;
690              }              }
691              TkTextIndexForwBytes(&index1, last-offset, &index1);              TkTextIndexForwBytes(&index1, last-offset, &index1);
692          }          }
693      } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)      } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
694              && (length >= 3)) {              && (length >= 3)) {
695          char buf[200];          char buf[200];
696    
697          if (argc != 3) {          if (argc != 3) {
698              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
699                      argv[0], " index index\"",                      argv[0], " index index\"",
700                      (char *) NULL);                      (char *) NULL);
701              result = TCL_ERROR;              result = TCL_ERROR;
702              goto done;              goto done;
703          }          }
704          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
705              result = TCL_ERROR;              result = TCL_ERROR;
706              goto done;              goto done;
707          }          }
708          TkTextPrintIndex(&index1, buf);          TkTextPrintIndex(&index1, buf);
709          Tcl_SetResult(interp, buf, TCL_VOLATILE);          Tcl_SetResult(interp, buf, TCL_VOLATILE);
710      } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)      } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
711              && (length >= 3)) {              && (length >= 3)) {
712          int i, j, numTags;          int i, j, numTags;
713          char **tagNames;          char **tagNames;
714          TkTextTag **oldTagArrayPtr;          TkTextTag **oldTagArrayPtr;
715    
716          if (argc < 4) {          if (argc < 4) {
717              Tcl_AppendResult(interp, "wrong # args: should be \"",              Tcl_AppendResult(interp, "wrong # args: should be \"",
718                      argv[0],                      argv[0],
719                      " insert index chars ?tagList chars tagList ...?\"",                      " insert index chars ?tagList chars tagList ...?\"",
720                      (char *) NULL);                      (char *) NULL);
721              result = TCL_ERROR;              result = TCL_ERROR;
722              goto done;              goto done;
723          }          }
724          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[2], &index1) != TCL_OK) {
725              result = TCL_ERROR;              result = TCL_ERROR;
726              goto done;              goto done;
727          }          }
728          if (textPtr->state == TK_STATE_NORMAL) {          if (textPtr->state == TK_STATE_NORMAL) {
729              for (j = 3;  j < argc; j += 2) {              for (j = 3;  j < argc; j += 2) {
730                  InsertChars(textPtr, &index1, argv[j]);                  InsertChars(textPtr, &index1, argv[j]);
731                  if (argc > (j+1)) {                  if (argc > (j+1)) {
732                      TkTextIndexForwBytes(&index1, (int) strlen(argv[j]),                      TkTextIndexForwBytes(&index1, (int) strlen(argv[j]),
733                              &index2);                              &index2);
734                      oldTagArrayPtr = TkBTreeGetTags(&index1, &numTags);                      oldTagArrayPtr = TkBTreeGetTags(&index1, &numTags);
735                      if (oldTagArrayPtr != NULL) {                      if (oldTagArrayPtr != NULL) {
736                          for (i = 0; i < numTags; i++) {                          for (i = 0; i < numTags; i++) {
737                              TkBTreeTag(&index1, &index2, oldTagArrayPtr[i], 0);                              TkBTreeTag(&index1, &index2, oldTagArrayPtr[i], 0);
738                          }                          }
739                          ckfree((char *) oldTagArrayPtr);                          ckfree((char *) oldTagArrayPtr);
740                      }                      }
741                      if (Tcl_SplitList(interp, argv[j+1], &numTags, &tagNames)                      if (Tcl_SplitList(interp, argv[j+1], &numTags, &tagNames)
742                              != TCL_OK) {                              != TCL_OK) {
743                          result = TCL_ERROR;                          result = TCL_ERROR;
744                          goto done;                          goto done;
745                      }                      }
746                      for (i = 0; i < numTags; i++) {                      for (i = 0; i < numTags; i++) {
747                          TkBTreeTag(&index1, &index2,                          TkBTreeTag(&index1, &index2,
748                                  TkTextCreateTag(textPtr, tagNames[i]), 1);                                  TkTextCreateTag(textPtr, tagNames[i]), 1);
749                      }                      }
750                      ckfree((char *) tagNames);                      ckfree((char *) tagNames);
751                      index1 = index2;                      index1 = index2;
752                  }                  }
753              }              }
754          }          }
755      } else if ((c == 'd') && (strncmp(argv[1], "dump", length) == 0)) {      } else if ((c == 'd') && (strncmp(argv[1], "dump", length) == 0)) {
756          result = TextDumpCmd(textPtr, interp, argc, argv);          result = TextDumpCmd(textPtr, interp, argc, argv);
757      } else if ((c == 'i') && (strncmp(argv[1], "image", length) == 0)) {      } else if ((c == 'i') && (strncmp(argv[1], "image", length) == 0)) {
758          result = TkTextImageCmd(textPtr, interp, argc, argv);          result = TkTextImageCmd(textPtr, interp, argc, argv);
759      } else if ((c == 'm') && (strncmp(argv[1], "mark", length) == 0)) {      } else if ((c == 'm') && (strncmp(argv[1], "mark", length) == 0)) {
760          result = TkTextMarkCmd(textPtr, interp, argc, argv);          result = TkTextMarkCmd(textPtr, interp, argc, argv);
761      } else if ((c == 's') && (strcmp(argv[1], "scan") == 0) && (length >= 2)) {      } else if ((c == 's') && (strcmp(argv[1], "scan") == 0) && (length >= 2)) {
762          result = TkTextScanCmd(textPtr, interp, argc, argv);          result = TkTextScanCmd(textPtr, interp, argc, argv);
763      } else if ((c == 's') && (strcmp(argv[1], "search") == 0)      } else if ((c == 's') && (strcmp(argv[1], "search") == 0)
764              && (length >= 3)) {              && (length >= 3)) {
765          result = TextSearchCmd(textPtr, interp, argc, argv);          result = TextSearchCmd(textPtr, interp, argc, argv);
766      } else if ((c == 's') && (strcmp(argv[1], "see") == 0) && (length >= 3)) {      } else if ((c == 's') && (strcmp(argv[1], "see") == 0) && (length >= 3)) {
767          result = TkTextSeeCmd(textPtr, interp, argc, argv);          result = TkTextSeeCmd(textPtr, interp, argc, argv);
768      } else if ((c == 't') && (strcmp(argv[1], "tag") == 0)) {      } else if ((c == 't') && (strcmp(argv[1], "tag") == 0)) {
769          result = TkTextTagCmd(textPtr, interp, argc, argv);          result = TkTextTagCmd(textPtr, interp, argc, argv);
770      } else if ((c == 'w') && (strncmp(argv[1], "window", length) == 0)) {      } else if ((c == 'w') && (strncmp(argv[1], "window", length) == 0)) {
771          result = TkTextWindowCmd(textPtr, interp, argc, argv);          result = TkTextWindowCmd(textPtr, interp, argc, argv);
772      } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {      } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
773          result = TkTextXviewCmd(textPtr, interp, argc, argv);          result = TkTextXviewCmd(textPtr, interp, argc, argv);
774      } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)      } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)
775              && (length >= 2)) {              && (length >= 2)) {
776          result = TkTextYviewCmd(textPtr, interp, argc, argv);          result = TkTextYviewCmd(textPtr, interp, argc, argv);
777      } else {      } else {
778          Tcl_AppendResult(interp, "bad option \"", argv[1],          Tcl_AppendResult(interp, "bad option \"", argv[1],
779                  "\": must be bbox, cget, compare, configure, debug, delete, ",                  "\": must be bbox, cget, compare, configure, debug, delete, ",
780                  "dlineinfo, dump, get, image, index, insert, mark, scan, ",                  "dlineinfo, dump, get, image, index, insert, mark, scan, ",
781                  "search, see, tag, window, xview, or yview",                  "search, see, tag, window, xview, or yview",
782                  (char *) NULL);                  (char *) NULL);
783          result = TCL_ERROR;          result = TCL_ERROR;
784      }      }
785    
786      done:      done:
787      Tcl_Release((ClientData) textPtr);      Tcl_Release((ClientData) textPtr);
788      return result;      return result;
789  }  }
790    
791  /*  /*
792   *----------------------------------------------------------------------   *----------------------------------------------------------------------
793   *   *
794   * DestroyText --   * DestroyText --
795   *   *
796   *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release   *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
797   *      to clean up the internal structure of a text at a safe time   *      to clean up the internal structure of a text at a safe time
798   *      (when no-one is using it anymore).   *      (when no-one is using it anymore).
799   *   *
800   * Results:   * Results:
801   *      None.   *      None.
802   *   *
803   * Side effects:   * Side effects:
804   *      Everything associated with the text is freed up.   *      Everything associated with the text is freed up.
805   *   *
806   *----------------------------------------------------------------------   *----------------------------------------------------------------------
807   */   */
808    
809  static void  static void
810  DestroyText(memPtr)  DestroyText(memPtr)
811      char *memPtr;               /* Info about text widget. */      char *memPtr;               /* Info about text widget. */
812  {  {
813      register TkText *textPtr = (TkText *) memPtr;      register TkText *textPtr = (TkText *) memPtr;
814      Tcl_HashSearch search;      Tcl_HashSearch search;
815      Tcl_HashEntry *hPtr;      Tcl_HashEntry *hPtr;
816      TkTextTag *tagPtr;      TkTextTag *tagPtr;
817    
818      /*      /*
819       * Free up all the stuff that requires special handling, then       * Free up all the stuff that requires special handling, then
820       * let Tk_FreeOptions handle all the standard option-related       * let Tk_FreeOptions handle all the standard option-related
821       * stuff.  Special note:  free up display-related information       * stuff.  Special note:  free up display-related information
822       * before deleting the B-tree, since display-related stuff       * before deleting the B-tree, since display-related stuff
823       * may refer to stuff in the B-tree.       * may refer to stuff in the B-tree.
824       */       */
825    
826      TkTextFreeDInfo(textPtr);      TkTextFreeDInfo(textPtr);
827      TkBTreeDestroy(textPtr->tree);      TkBTreeDestroy(textPtr->tree);
828      for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);      for (hPtr = Tcl_FirstHashEntry(&textPtr->tagTable, &search);
829              hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {              hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
830          tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);          tagPtr = (TkTextTag *) Tcl_GetHashValue(hPtr);
831          TkTextFreeTag(textPtr, tagPtr);          TkTextFreeTag(textPtr, tagPtr);
832      }      }
833      Tcl_DeleteHashTable(&textPtr->tagTable);      Tcl_DeleteHashTable(&textPtr->tagTable);
834      for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);      for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
835              hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {              hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
836          ckfree((char *) Tcl_GetHashValue(hPtr));          ckfree((char *) Tcl_GetHashValue(hPtr));
837      }      }
838      Tcl_DeleteHashTable(&textPtr->markTable);      Tcl_DeleteHashTable(&textPtr->markTable);
839      if (textPtr->tabArrayPtr != NULL) {      if (textPtr->tabArrayPtr != NULL) {
840          ckfree((char *) textPtr->tabArrayPtr);          ckfree((char *) textPtr->tabArrayPtr);
841      }      }
842      if (textPtr->insertBlinkHandler != NULL) {      if (textPtr->insertBlinkHandler != NULL) {
843          Tcl_DeleteTimerHandler(textPtr->insertBlinkHandler);          Tcl_DeleteTimerHandler(textPtr->insertBlinkHandler);
844      }      }
845      if (textPtr->bindingTable != NULL) {      if (textPtr->bindingTable != NULL) {
846          Tk_DeleteBindingTable(textPtr->bindingTable);          Tk_DeleteBindingTable(textPtr->bindingTable);
847      }      }
848    
849      /*      /*
850       * NOTE: do NOT free up selBorder, selBdString, or selFgColorPtr:       * NOTE: do NOT free up selBorder, selBdString, or selFgColorPtr:
851       * they are duplicates of information in the "sel" tag, which was       * they are duplicates of information in the "sel" tag, which was
852       * freed up as part of deleting the tags above.       * freed up as part of deleting the tags above.
853       */       */
854    
855      textPtr->selBorder = NULL;      textPtr->selBorder = NULL;
856      textPtr->selBdString = NULL;      textPtr->selBdString = NULL;
857      textPtr->selFgColorPtr = NULL;      textPtr->selFgColorPtr = NULL;
858      Tk_FreeOptions(configSpecs, (char *) textPtr, textPtr->display, 0);      Tk_FreeOptions(configSpecs, (char *) textPtr, textPtr->display, 0);
859      ckfree((char *) textPtr);      ckfree((char *) textPtr);
860  }  }
861    
862  /*  /*
863   *----------------------------------------------------------------------   *----------------------------------------------------------------------
864   *   *
865   * ConfigureText --   * ConfigureText --
866   *   *
867   *      This procedure is called to process an argv/argc list, plus   *      This procedure is called to process an argv/argc list, plus
868   *      the Tk option database, in order to configure (or   *      the Tk option database, in order to configure (or
869   *      reconfigure) a text widget.   *      reconfigure) a text widget.
870   *   *
871   * Results:   * Results:
872   *      The return value is a standard Tcl result.  If TCL_ERROR is   *      The return value is a standard Tcl result.  If TCL_ERROR is
873   *      returned, then the interp's result contains an error message.   *      returned, then the interp's result contains an error message.
874   *   *
875   * Side effects:   * Side effects:
876   *      Configuration information, such as text string, colors, font,   *      Configuration information, such as text string, colors, font,
877   *      etc. get set for textPtr;  old resources get freed, if there   *      etc. get set for textPtr;  old resources get freed, if there
878   *      were any.   *      were any.
879   *   *
880   *----------------------------------------------------------------------   *----------------------------------------------------------------------
881   */   */
882    
883  static int  static int
884  ConfigureText(interp, textPtr, argc, argv, flags)  ConfigureText(interp, textPtr, argc, argv, flags)
885      Tcl_Interp *interp;         /* Used for error reporting. */      Tcl_Interp *interp;         /* Used for error reporting. */
886      register TkText *textPtr;   /* Information about widget;  may or may      register TkText *textPtr;   /* Information about widget;  may or may
887                                   * not already have values for some fields. */                                   * not already have values for some fields. */
888      int argc;                   /* Number of valid entries in argv. */      int argc;                   /* Number of valid entries in argv. */
889      char **argv;                /* Arguments. */      char **argv;                /* Arguments. */
890      int flags;                  /* Flags to pass to Tk_ConfigureWidget. */      int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
891  {  {
892      int oldExport = textPtr->exportSelection;      int oldExport = textPtr->exportSelection;
893    
894      if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs,      if (Tk_ConfigureWidget(interp, textPtr->tkwin, configSpecs,
895              argc, argv, (char *) textPtr, flags) != TCL_OK) {              argc, argv, (char *) textPtr, flags) != TCL_OK) {
896          return TCL_ERROR;          return TCL_ERROR;
897      }      }
898    
899      /*      /*
900       * A few other options also need special processing, such as parsing       * A few other options also need special processing, such as parsing
901       * the geometry and setting the background from a 3-D border.       * the geometry and setting the background from a 3-D border.
902       */       */
903    
904      Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);      Tk_SetBackgroundFromBorder(textPtr->tkwin, textPtr->border);
905    
906      /*      /*
907       * Don't allow negative spacings.       * Don't allow negative spacings.
908       */       */
909    
910      if (textPtr->spacing1 < 0) {      if (textPtr->spacing1 < 0) {
911          textPtr->spacing1 = 0;          textPtr->spacing1 = 0;
912      }      }
913      if (textPtr->spacing2 < 0) {      if (textPtr->spacing2 < 0) {
914          textPtr->spacing2 = 0;          textPtr->spacing2 = 0;
915      }      }
916      if (textPtr->spacing3 < 0) {      if (textPtr->spacing3 < 0) {
917          textPtr->spacing3 = 0;          textPtr->spacing3 = 0;
918      }      }
919    
920      /*      /*
921       * Parse tab stops.       * Parse tab stops.
922       */       */
923    
924      if (textPtr->tabArrayPtr != NULL) {      if (textPtr->tabArrayPtr != NULL) {
925          ckfree((char *) textPtr->tabArrayPtr);          ckfree((char *) textPtr->tabArrayPtr);
926          textPtr->tabArrayPtr = NULL;          textPtr->tabArrayPtr = NULL;
927      }      }
928      if (textPtr->tabOptionString != NULL) {      if (textPtr->tabOptionString != NULL) {
929          textPtr->tabArrayPtr = TkTextGetTabs(interp, textPtr->tkwin,          textPtr->tabArrayPtr = TkTextGetTabs(interp, textPtr->tkwin,
930                  textPtr->tabOptionString);                  textPtr->tabOptionString);
931          if (textPtr->tabArrayPtr == NULL) {          if (textPtr->tabArrayPtr == NULL) {
932              Tcl_AddErrorInfo(interp,"\n    (while processing -tabs option)");              Tcl_AddErrorInfo(interp,"\n    (while processing -tabs option)");
933              return TCL_ERROR;              return TCL_ERROR;
934          }          }
935      }      }
936    
937      /*      /*
938       * Make sure that configuration options are properly mirrored       * Make sure that configuration options are properly mirrored
939       * between the widget record and the "sel" tags.  NOTE: we don't       * between the widget record and the "sel" tags.  NOTE: we don't
940       * have to free up information during the mirroring;  old       * have to free up information during the mirroring;  old
941       * information was freed when it was replaced in the widget       * information was freed when it was replaced in the widget
942       * record.       * record.
943       */       */
944    
945      textPtr->selTagPtr->border = textPtr->selBorder;      textPtr->selTagPtr->border = textPtr->selBorder;
946      if (textPtr->selTagPtr->bdString != textPtr->selBdString) {      if (textPtr->selTagPtr->bdString != textPtr->selBdString) {
947          textPtr->selTagPtr->bdString = textPtr->selBdString;          textPtr->selTagPtr->bdString = textPtr->selBdString;
948          if (textPtr->selBdString != NULL) {          if (textPtr->selBdString != NULL) {
949              if (Tk_GetPixels(interp, textPtr->tkwin, textPtr->selBdString,              if (Tk_GetPixels(interp, textPtr->tkwin, textPtr->selBdString,
950                      &textPtr->selTagPtr->borderWidth) != TCL_OK) {                      &textPtr->selTagPtr->borderWidth) != TCL_OK) {
951                  return TCL_ERROR;                  return TCL_ERROR;
952              }              }
953              if (textPtr->selTagPtr->borderWidth < 0) {              if (textPtr->selTagPtr->borderWidth < 0) {
954                  textPtr->selTagPtr->borderWidth = 0;                  textPtr->selTagPtr->borderWidth = 0;
955              }              }
956          }          }
957      }      }
958      textPtr->selTagPtr->fgColor = textPtr->selFgColorPtr;      textPtr->selTagPtr->fgColor = textPtr->selFgColorPtr;
959      textPtr->selTagPtr->affectsDisplay = 0;      textPtr->selTagPtr->affectsDisplay = 0;
960      if ((textPtr->selTagPtr->border != NULL)      if ((textPtr->selTagPtr->border != NULL)
961              || (textPtr->selTagPtr->bdString != NULL)              || (textPtr->selTagPtr->bdString != NULL)
962              || (textPtr->selTagPtr->reliefString != NULL)              || (textPtr->selTagPtr->reliefString != NULL)
963              || (textPtr->selTagPtr->bgStipple != None)              || (textPtr->selTagPtr->bgStipple != None)
964              || (textPtr->selTagPtr->fgColor != NULL)              || (textPtr->selTagPtr->fgColor != NULL)
965              || (textPtr->selTagPtr->tkfont != None)              || (textPtr->selTagPtr->tkfont != None)
966              || (textPtr->selTagPtr->fgStipple != None)              || (textPtr->selTagPtr->fgStipple != None)
967              || (textPtr->selTagPtr->justifyString != NULL)              || (textPtr->selTagPtr->justifyString != NULL)
968              || (textPtr->selTagPtr->lMargin1String != NULL)              || (textPtr->selTagPtr->lMargin1String != NULL)
969              || (textPtr->selTagPtr->lMargin2String != NULL)              || (textPtr->selTagPtr->lMargin2String != NULL)
970              || (textPtr->selTagPtr->offsetString != NULL)              || (textPtr->selTagPtr->offsetString != NULL)
971              || (textPtr->selTagPtr->overstrikeString != NULL)              || (textPtr->selTagPtr->overstrikeString != NULL)
972              || (textPtr->selTagPtr->rMarginString != NULL)              || (textPtr->selTagPtr->rMarginString != NULL)
973              || (textPtr->selTagPtr->spacing1String != NULL)              || (textPtr->selTagPtr->spacing1String != NULL)
974              || (textPtr->selTagPtr->spacing2String != NULL)              || (textPtr->selTagPtr->spacing2String != NULL)
975              || (textPtr->selTagPtr->spacing3String != NULL)              || (textPtr->selTagPtr->spacing3String != NULL)
976              || (textPtr->selTagPtr->tabString != NULL)              || (textPtr->selTagPtr->tabString != NULL)
977              || (textPtr->selTagPtr->underlineString != NULL)              || (textPtr->selTagPtr->underlineString != NULL)
978              || (textPtr->selTagPtr->elideString != NULL)              || (textPtr->selTagPtr->elideString != NULL)
979              || (textPtr->selTagPtr->wrapMode != TEXT_WRAPMODE_NULL)) {              || (textPtr->selTagPtr->wrapMode != TEXT_WRAPMODE_NULL)) {
980          textPtr->selTagPtr->affectsDisplay = 1;          textPtr->selTagPtr->affectsDisplay = 1;
981      }      }
982      TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,      TkTextRedrawTag(textPtr, (TkTextIndex *) NULL, (TkTextIndex *) NULL,
983              textPtr->selTagPtr, 1);              textPtr->selTagPtr, 1);
984    
985      /*      /*
986       * Claim the selection if we've suddenly started exporting it and there       * Claim the selection if we've suddenly started exporting it and there
987       * are tagged characters.       * are tagged characters.
988       */       */
989    
990      if (textPtr->exportSelection && (!oldExport)) {      if (textPtr->exportSelection && (!oldExport)) {
991          TkTextSearch search;          TkTextSearch search;
992          TkTextIndex first, last;          TkTextIndex first, last;
993    
994          TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);          TkTextMakeByteIndex(textPtr->tree, 0, 0, &first);
995          TkTextMakeByteIndex(textPtr->tree,          TkTextMakeByteIndex(textPtr->tree,
996                  TkBTreeNumLines(textPtr->tree), 0, &last);                  TkBTreeNumLines(textPtr->tree), 0, &last);
997          TkBTreeStartSearch(&first, &last, textPtr->selTagPtr, &search);          TkBTreeStartSearch(&first, &last, textPtr->selTagPtr, &search);
998          if (TkBTreeCharTagged(&first, textPtr->selTagPtr)          if (TkBTreeCharTagged(&first, textPtr->selTagPtr)
999                  || TkBTreeNextTag(&search)) {                  || TkBTreeNextTag(&search)) {
1000              Tk_OwnSelection(textPtr->tkwin, XA_PRIMARY, TkTextLostSelection,              Tk_OwnSelection(textPtr->tkwin, XA_PRIMARY, TkTextLostSelection,
1001                      (ClientData) textPtr);                      (ClientData) textPtr);
1002              textPtr->flags |= GOT_SELECTION;              textPtr->flags |= GOT_SELECTION;
1003          }          }
1004      }      }
1005    
1006      /*      /*
1007       * Register the desired geometry for the window, and arrange for       * Register the desired geometry for the window, and arrange for
1008       * the window to be redisplayed.       * the window to be redisplayed.
1009       */       */
1010    
1011      if (textPtr->width <= 0) {      if (textPtr->width <= 0) {
1012          textPtr->width = 1;          textPtr->width = 1;
1013      }      }
1014      if (textPtr->height <= 0) {      if (textPtr->height <= 0) {
1015          textPtr->height = 1;          textPtr->height = 1;
1016      }      }
1017      TextWorldChanged((ClientData) textPtr);      TextWorldChanged((ClientData) textPtr);
1018      return TCL_OK;      return TCL_OK;
1019  }  }
1020    
1021  /*  /*
1022   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
1023   *   *
1024   * TextWorldChanged --   * TextWorldChanged --
1025   *   *
1026   *      This procedure is called when the world has changed in some   *      This procedure is called when the world has changed in some
1027   *      way and the widget needs to recompute all its graphics contexts   *      way and the widget needs to recompute all its graphics contexts
1028   *      and determine its new geometry.   *      and determine its new geometry.
1029   *   *
1030   * Results:   * Results:
1031   *      None.   *      None.
1032   *   *
1033   * Side effects:   * Side effects:
1034   *      Configures all tags in the Text with a empty argc/argv, for   *      Configures all tags in the Text with a empty argc/argv, for
1035   *      the side effect of causing all the items to recompute their   *      the side effect of causing all the items to recompute their
1036   *      geometry and to be redisplayed.   *      geometry and to be redisplayed.
1037   *   *
1038   *---------------------------------------------------------------------------   *---------------------------------------------------------------------------
1039   */   */
1040    
1041  static void  static void
1042  TextWorldChanged(instanceData)  TextWorldChanged(instanceData)
1043      ClientData instanceData;    /* Information about widget. */      ClientData instanceData;    /* Information about widget. */
1044  {  {
1045      TkText *textPtr;      TkText *textPtr;
1046      Tk_FontMetrics fm;      Tk_FontMetrics fm;
1047    
1048      textPtr = (TkText *) instanceData;      textPtr = (TkText *) instanceData;
1049    
1050      textPtr->charWidth = Tk_TextWidth(textPtr->tkfont, "0", 1);      textPtr->charWidth = Tk_TextWidth(textPtr->tkfont, "0", 1);
1051      if (textPtr->charWidth <= 0) {      if (textPtr->charWidth <= 0) {
1052          textPtr->charWidth = 1;          textPtr->charWidth = 1;
1053      }      }
1054      Tk_GetFontMetrics(textPtr->tkfont, &fm);      Tk_GetFontMetrics(textPtr->tkfont, &fm);
1055      Tk_GeometryRequest(textPtr->tkwin,      Tk_GeometryRequest(textPtr->tkwin,
1056              textPtr->width * textPtr->charWidth + 2*textPtr->borderWidth              textPtr->width * textPtr->charWidth + 2*textPtr->borderWidth
1057                      + 2*textPtr->padX + 2*textPtr->highlightWidth,                      + 2*textPtr->padX + 2*textPtr->highlightWidth,
1058              textPtr->height * (fm.linespace + textPtr->spacing1              textPtr->height * (fm.linespace + textPtr->spacing1
1059                      + textPtr->spacing3) + 2*textPtr->borderWidth                      + textPtr->spacing3) + 2*textPtr->borderWidth
1060                      + 2*textPtr->padY + 2*textPtr->highlightWidth);                      + 2*textPtr->padY + 2*textPtr->highlightWidth);
1061      Tk_SetInternalBorder(textPtr->tkwin,      Tk_SetInternalBorder(textPtr->tkwin,
1062              textPtr->borderWidth + textPtr->highlightWidth);              textPtr->borderWidth + textPtr->highlightWidth);
1063      if (textPtr->setGrid) {      if (textPtr->setGrid) {
1064          Tk_SetGrid(textPtr->tkwin, textPtr->width, textPtr->height,          Tk_SetGrid(textPtr->tkwin, textPtr->width, textPtr->height,
1065                  textPtr->charWidth, fm.linespace);                  textPtr->charWidth, fm.linespace);
1066      } else {      } else {
1067          Tk_UnsetGrid(textPtr->tkwin);          Tk_UnsetGrid(textPtr->tkwin);
1068      }      }
1069    
1070      TkTextRelayoutWindow(textPtr);      TkTextRelayoutWindow(textPtr);
1071  }  }
1072    
1073  /*  /*
1074   *--------------------------------------------------------------   *--------------------------------------------------------------
1075   *   *
1076   * TextEventProc --   * TextEventProc --
1077   *   *
1078   *      This procedure is invoked by the Tk dispatcher on   *      This procedure is invoked by the Tk dispatcher on
1079   *      structure changes to a text.  For texts with 3D   *      structure changes to a text.  For texts with 3D
1080   *      borders, this procedure is also invoked for exposures.   *      borders, this procedure is also invoked for exposures.
1081   *   *
1082   * Results:   * Results:
1083   *      None.   *      None.
1084   *   *
1085   * Side effects:   * Side effects:
1086   *      When the window gets deleted, internal structures get   *      When the window gets deleted, internal structures get
1087   *      cleaned up.  When it gets exposed, it is redisplayed.   *      cleaned up.  When it gets exposed, it is redisplayed.
1088   *   *
1089   *--------------------------------------------------------------   *--------------------------------------------------------------
1090   */   */
1091    
1092  static void  static void
1093  TextEventProc(clientData, eventPtr)  TextEventProc(clientData, eventPtr)
1094      ClientData clientData;      /* Information about window. */      ClientData clientData;      /* Information about window. */
1095      register XEvent *eventPtr;  /* Information about event. */      register XEvent *eventPtr;  /* Information about event. */
1096  {  {
1097      register TkText *textPtr = (TkText *) clientData;      register TkText *textPtr = (TkText *) clientData;
1098      TkTextIndex index, index2;      TkTextIndex index, index2;
1099    
1100      if (eventPtr->type == Expose) {      if (eventPtr->type == Expose) {
1101          TkTextRedrawRegion(textPtr, eventPtr->xexpose.x,          TkTextRedrawRegion(textPtr, eventPtr->xexpose.x,
1102                  eventPtr->xexpose.y, eventPtr->xexpose.width,                  eventPtr->xexpose.y, eventPtr->xexpose.width,
1103                  eventPtr->xexpose.height);                  eventPtr->xexpose.height);
1104      } else if (eventPtr->type == ConfigureNotify) {      } else if (eventPtr->type == ConfigureNotify) {
1105          if ((textPtr->prevWidth != Tk_Width(textPtr->tkwin))          if ((textPtr->prevWidth != Tk_Width(textPtr->tkwin))
1106                  || (textPtr->prevHeight != Tk_Height(textPtr->tkwin))) {                  || (textPtr->prevHeight != Tk_Height(textPtr->tkwin))) {
1107              TkTextRelayoutWindow(textPtr);              TkTextRelayoutWindow(textPtr);
1108              textPtr->prevWidth = Tk_Width(textPtr->tkwin);              textPtr->prevWidth = Tk_Width(textPtr->tkwin);
1109              textPtr->prevHeight = Tk_Height(textPtr->tkwin);              textPtr->prevHeight = Tk_Height(textPtr->tkwin);
1110          }          }
1111      } else if (eventPtr->type == DestroyNotify) {      } else if (eventPtr->type == DestroyNotify) {
1112          if (textPtr->tkwin != NULL) {          if (textPtr->tkwin != NULL) {
1113              if (textPtr->setGrid) {              if (textPtr->setGrid) {
1114                  Tk_UnsetGrid(textPtr->tkwin);                  Tk_UnsetGrid(textPtr->tkwin);
1115              }              }
1116              textPtr->tkwin = NULL;              textPtr->tkwin = NULL;
1117              Tcl_DeleteCommandFromToken(textPtr->interp,              Tcl_DeleteCommandFromToken(textPtr->interp,
1118                      textPtr->widgetCmd);                      textPtr->widgetCmd);
1119          }          }
1120          Tcl_EventuallyFree((ClientData) textPtr, DestroyText);          Tcl_EventuallyFree((ClientData) textPtr, DestroyText);
1121      } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {      } else if ((eventPtr->type == FocusIn) || (eventPtr->type == FocusOut)) {
1122          if (eventPtr->xfocus.detail != NotifyInferior) {          if (eventPtr->xfocus.detail != NotifyInferior) {
1123              Tcl_DeleteTimerHandler(textPtr->insertBlinkHandler);              Tcl_DeleteTimerHandler(textPtr->insertBlinkHandler);
1124              if (eventPtr->type == FocusIn) {              if (eventPtr->type == FocusIn) {
1125                  textPtr->flags |= GOT_FOCUS | INSERT_ON;                  textPtr->flags |= GOT_FOCUS | INSERT_ON;
1126                  if (textPtr->insertOffTime != 0) {                  if (textPtr->insertOffTime != 0) {
1127                      textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(                      textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
1128                              textPtr->insertOnTime, TextBlinkProc,                              textPtr->insertOnTime, TextBlinkProc,
1129                              (ClientData) textPtr);                              (ClientData) textPtr);
1130                  }                  }
1131              } else {              } else {
1132                  textPtr->flags &= ~(GOT_FOCUS | INSERT_ON);                  textPtr->flags &= ~(GOT_FOCUS | INSERT_ON);
1133                  textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;                  textPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
1134              }              }
1135  #ifndef ALWAYS_SHOW_SELECTION  #ifndef ALWAYS_SHOW_SELECTION
1136              TkTextRedrawTag(textPtr, NULL, NULL, textPtr->selTagPtr, 1);              TkTextRedrawTag(textPtr, NULL, NULL, textPtr->selTagPtr, 1);
1137  #endif  #endif
1138              TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);              TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
1139              TkTextIndexForwChars(&index, 1, &index2);              TkTextIndexForwChars(&index, 1, &index2);
1140              TkTextChanged(textPtr, &index, &index2);              TkTextChanged(textPtr, &index, &index2);
1141              if (textPtr->highlightWidth > 0) {              if (textPtr->highlightWidth > 0) {
1142                  TkTextRedrawRegion(textPtr, 0, 0, textPtr->highlightWidth,                  TkTextRedrawRegion(textPtr, 0, 0, textPtr->highlightWidth,
1143                          textPtr->highlightWidth);                          textPtr->highlightWidth);
1144              }              }
1145          }          }
1146      }      }
1147  }  }
1148    
1149  /*  /*
1150   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1151   *   *
1152   * TextCmdDeletedProc --   * TextCmdDeletedProc --
1153   *   *
1154   *      This procedure is invoked when a widget command is deleted.  If   *      This procedure is invoked when a widget command is deleted.  If
1155   *      the widget isn't already in the process of being destroyed,   *      the widget isn't already in the process of being destroyed,
1156   *      this command destroys it.   *      this command destroys it.
1157   *   *
1158   * Results:   * Results:
1159   *      None.   *      None.
1160   *   *
1161   * Side effects:   * Side effects:
1162   *      The widget is destroyed.   *      The widget is destroyed.
1163   *   *
1164   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1165   */   */
1166    
1167  static void  static void
1168  TextCmdDeletedProc(clientData)  TextCmdDeletedProc(clientData)
1169      ClientData clientData;      /* Pointer to widget record for widget. */      ClientData clientData;      /* Pointer to widget record for widget. */
1170  {  {
1171      TkText *textPtr = (TkText *) clientData;      TkText *textPtr = (TkText *) clientData;
1172      Tk_Window tkwin = textPtr->tkwin;      Tk_Window tkwin = textPtr->tkwin;
1173    
1174      /*      /*
1175       * This procedure could be invoked either because the window was       * This procedure could be invoked either because the window was
1176       * destroyed and the command was then deleted (in which case tkwin       * destroyed and the command was then deleted (in which case tkwin
1177       * is NULL) or because the command was deleted, and then this procedure       * is NULL) or because the command was deleted, and then this procedure
1178       * destroys the widget.       * destroys the widget.
1179       */       */
1180    
1181      if (tkwin != NULL) {      if (tkwin != NULL) {
1182          if (textPtr->setGrid) {          if (textPtr->setGrid) {
1183              Tk_UnsetGrid(textPtr->tkwin);              Tk_UnsetGrid(textPtr->tkwin);
1184          }          }
1185          textPtr->tkwin = NULL;          textPtr->tkwin = NULL;
1186          Tk_DestroyWindow(tkwin);          Tk_DestroyWindow(tkwin);
1187      }      }
1188  }  }
1189    
1190  /*  /*
1191   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1192   *   *
1193   * InsertChars --   * InsertChars --
1194   *   *
1195   *      This procedure implements most of the functionality of the   *      This procedure implements most of the functionality of the
1196   *      "insert" widget command.   *      "insert" widget command.
1197   *   *
1198   * Results:   * Results:
1199   *      None.   *      None.
1200   *   *
1201   * Side effects:   * Side effects:
1202   *      The characters in "string" get added to the text just before   *      The characters in "string" get added to the text just before
1203   *      the character indicated by "indexPtr".   *      the character indicated by "indexPtr".
1204   *   *
1205   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1206   */   */
1207    
1208  static void  static void
1209  InsertChars(textPtr, indexPtr, string)  InsertChars(textPtr, indexPtr, string)
1210      TkText *textPtr;            /* Overall information about text widget. */      TkText *textPtr;            /* Overall information about text widget. */
1211      TkTextIndex *indexPtr;      /* Where to insert new characters.  May be      TkTextIndex *indexPtr;      /* Where to insert new characters.  May be
1212                                   * modified and/or invalidated. */                                   * modified and/or invalidated. */
1213      char *string;               /* Null-terminated string containing new      char *string;               /* Null-terminated string containing new
1214                                   * information to add to text. */                                   * information to add to text. */
1215  {  {
1216      int lineIndex, resetView, offset;      int lineIndex, resetView, offset;
1217      TkTextIndex newTop;      TkTextIndex newTop;
1218    
1219      /*      /*
1220       * Don't allow insertions on the last (dummy) line of the text.       * Don't allow insertions on the last (dummy) line of the text.
1221       */       */
1222    
1223      lineIndex = TkBTreeLineIndex(indexPtr->linePtr);      lineIndex = TkBTreeLineIndex(indexPtr->linePtr);
1224      if (lineIndex == TkBTreeNumLines(textPtr->tree)) {      if (lineIndex == TkBTreeNumLines(textPtr->tree)) {
1225          lineIndex--;          lineIndex--;
1226          TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, indexPtr);          TkTextMakeByteIndex(textPtr->tree, lineIndex, 1000000, indexPtr);
1227      }      }
1228    
1229      /*      /*
1230       * Notify the display module that lines are about to change, then do       * Notify the display module that lines are about to change, then do
1231       * the insertion.  If the insertion occurs on the top line of the       * the insertion.  If the insertion occurs on the top line of the
1232       * widget (textPtr->topIndex), then we have to recompute topIndex       * widget (textPtr->topIndex), then we have to recompute topIndex
1233       * after the insertion, since the insertion could invalidate it.       * after the insertion, since the insertion could invalidate it.
1234       */       */
1235    
1236      resetView = offset = 0;      resetView = offset = 0;
1237      if (indexPtr->linePtr == textPtr->topIndex.linePtr) {      if (indexPtr->linePtr == textPtr->topIndex.linePtr) {
1238          resetView = 1;          resetView = 1;
1239          offset = textPtr->topIndex.byteIndex;          offset = textPtr->topIndex.byteIndex;
1240          if (offset > indexPtr->byteIndex) {          if (offset > indexPtr->byteIndex) {
1241              offset += strlen(string);              offset += strlen(string);
1242          }          }
1243      }      }
1244      TkTextChanged(textPtr, indexPtr, indexPtr);      TkTextChanged(textPtr, indexPtr, indexPtr);
1245      TkBTreeInsertChars(indexPtr, string);      TkBTreeInsertChars(indexPtr, string);
1246      if (resetView) {      if (resetView) {
1247          TkTextMakeByteIndex(textPtr->tree, lineIndex, 0, &newTop);          TkTextMakeByteIndex(textPtr->tree, lineIndex, 0, &newTop);
1248          TkTextIndexForwBytes(&newTop, offset, &newTop);          TkTextIndexForwBytes(&newTop, offset, &newTop);
1249          TkTextSetYView(textPtr, &newTop, 0);          TkTextSetYView(textPtr, &newTop, 0);
1250      }      }
1251    
1252      /*      /*
1253       * Invalidate any selection retrievals in progress.       * Invalidate any selection retrievals in progress.
1254       */       */
1255    
1256      textPtr->abortSelections = 1;      textPtr->abortSelections = 1;
1257  }  }
1258    
1259  /*  /*
1260   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1261   *   *
1262   * DeleteChars --   * DeleteChars --
1263   *   *
1264   *      This procedure implements most of the functionality of the   *      This procedure implements most of the functionality of the
1265   *      "delete" widget command.   *      "delete" widget command.
1266   *   *
1267   * Results:   * Results:
1268   *      Returns a standard Tcl result, and leaves an error message   *      Returns a standard Tcl result, and leaves an error message
1269   *      in textPtr->interp if there is an error.   *      in textPtr->interp if there is an error.
1270   *   *
1271   * Side effects:   * Side effects:
1272   *      Characters get deleted from the text.   *      Characters get deleted from the text.
1273   *   *
1274   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1275   */   */
1276    
1277  static int  static int
1278  DeleteChars(textPtr, index1String, index2String)  DeleteChars(textPtr, index1String, index2String)
1279      TkText *textPtr;            /* Overall information about text widget. */      TkText *textPtr;            /* Overall information about text widget. */
1280      char *index1String;         /* String describing location of first      char *index1String;         /* String describing location of first
1281                                   * character to delete. */                                   * character to delete. */
1282      char *index2String;         /* String describing location of last      char *index2String;         /* String describing location of last
1283                                   * character to delete.  NULL means just                                   * character to delete.  NULL means just
1284                                   * delete the one character given by                                   * delete the one character given by
1285                                   * index1String. */                                   * index1String. */
1286  {  {
1287      int line1, line2, line, byteIndex, resetView;      int line1, line2, line, byteIndex, resetView;
1288      TkTextIndex index1, index2;      TkTextIndex index1, index2;
1289    
1290      /*      /*
1291       * Parse the starting and stopping indices.       * Parse the starting and stopping indices.
1292       */       */
1293    
1294      if (TkTextGetIndex(textPtr->interp, textPtr, index1String, &index1)      if (TkTextGetIndex(textPtr->interp, textPtr, index1String, &index1)
1295              != TCL_OK) {              != TCL_OK) {
1296          return TCL_ERROR;          return TCL_ERROR;
1297      }      }
1298      if (index2String != NULL) {      if (index2String != NULL) {
1299          if (TkTextGetIndex(textPtr->interp, textPtr, index2String, &index2)          if (TkTextGetIndex(textPtr->interp, textPtr, index2String, &index2)
1300                  != TCL_OK) {                  != TCL_OK) {
1301              return TCL_ERROR;              return TCL_ERROR;
1302          }          }
1303      } else {      } else {
1304          index2 = index1;          index2 = index1;
1305          TkTextIndexForwChars(&index2, 1, &index2);          TkTextIndexForwChars(&index2, 1, &index2);
1306      }      }
1307    
1308      /*      /*
1309       * Make sure there's really something to delete.       * Make sure there's really something to delete.
1310       */       */
1311    
1312      if (TkTextIndexCmp(&index1, &index2) >= 0) {      if (TkTextIndexCmp(&index1, &index2) >= 0) {
1313          return TCL_OK;          return TCL_OK;
1314      }      }
1315    
1316      /*      /*
1317       * The code below is ugly, but it's needed to make sure there       * The code below is ugly, but it's needed to make sure there
1318       * is always a dummy empty line at the end of the text.  If the       * is always a dummy empty line at the end of the text.  If the
1319       * final newline of the file (just before the dummy line) is being       * final newline of the file (just before the dummy line) is being
1320       * deleted, then back up index to just before the newline.  If       * deleted, then back up index to just before the newline.  If
1321       * there is a newline just before the first character being deleted,       * there is a newline just before the first character being deleted,
1322       * then back up the first index too, so that an even number of lines       * then back up the first index too, so that an even number of lines
1323       * gets deleted.  Furthermore, remove any tags that are present on       * gets deleted.  Furthermore, remove any tags that are present on
1324       * the newline that isn't going to be deleted after all (this simulates       * the newline that isn't going to be deleted after all (this simulates
1325       * deleting the newline and then adding a "clean" one back again).       * deleting the newline and then adding a "clean" one back again).
1326       */       */
1327    
1328      line1 = TkBTreeLineIndex(index1.linePtr);      line1 = TkBTreeLineIndex(index1.linePtr);
1329      line2 = TkBTreeLineIndex(index2.linePtr);      line2 = TkBTreeLineIndex(index2.linePtr);
1330      if (line2 == TkBTreeNumLines(textPtr->tree)) {      if (line2 == TkBTreeNumLines(textPtr->tree)) {
1331          TkTextTag **arrayPtr;          TkTextTag **arrayPtr;
1332          int arraySize, i;          int arraySize, i;
1333          TkTextIndex oldIndex2;          TkTextIndex oldIndex2;
1334    
1335          oldIndex2 = index2;          oldIndex2 = index2;
1336          TkTextIndexBackChars(&oldIndex2, 1, &index2);          TkTextIndexBackChars(&oldIndex2, 1, &index2);
1337          line2--;          line2--;
1338          if ((index1.byteIndex == 0) && (line1 != 0)) {          if ((index1.byteIndex == 0) && (line1 != 0)) {
1339              TkTextIndexBackChars(&index1, 1, &index1);              TkTextIndexBackChars(&index1, 1, &index1);
1340              line1--;              line1--;
1341          }          }
1342          arrayPtr = TkBTreeGetTags(&index2, &arraySize);          arrayPtr = TkBTreeGetTags(&index2, &arraySize);
1343          if (arrayPtr != NULL) {          if (arrayPtr != NULL) {
1344              for (i = 0; i < arraySize; i++) {              for (i = 0; i < arraySize; i++) {
1345                  TkBTreeTag(&index2, &oldIndex2, arrayPtr[i], 0);                  TkBTreeTag(&index2, &oldIndex2, arrayPtr[i], 0);
1346              }              }
1347              ckfree((char *) arrayPtr);              ckfree((char *) arrayPtr);
1348          }          }
1349      }      }
1350    
1351      /*      /*
1352       * Tell the display what's about to happen so it can discard       * Tell the display what's about to happen so it can discard
1353       * obsolete display information, then do the deletion.  Also,       * obsolete display information, then do the deletion.  Also,
1354       * if the deletion involves the top line on the screen, then       * if the deletion involves the top line on the screen, then
1355       * we have to reset the view (the deletion will invalidate       * we have to reset the view (the deletion will invalidate
1356       * textPtr->topIndex).  Compute what the new first character       * textPtr->topIndex).  Compute what the new first character
1357       * will be, then do the deletion, then reset the view.       * will be, then do the deletion, then reset the view.
1358       */       */
1359    
1360      TkTextChanged(textPtr, &index1, &index2);      TkTextChanged(textPtr, &index1, &index2);
1361      resetView = 0;      resetView = 0;
1362      line = 0;      line = 0;
1363      byteIndex = 0;      byteIndex = 0;
1364      if (TkTextIndexCmp(&index2, &textPtr->topIndex) >= 0) {      if (TkTextIndexCmp(&index2, &textPtr->topIndex) >= 0) {
1365          if (TkTextIndexCmp(&index1, &textPtr->topIndex) <= 0) {          if (TkTextIndexCmp(&index1, &textPtr->topIndex) <= 0) {
1366              /*              /*
1367               * Deletion range straddles topIndex: use the beginning               * Deletion range straddles topIndex: use the beginning
1368               * of the range as the new topIndex.               * of the range as the new topIndex.
1369               */               */
1370    
1371              resetView = 1;              resetView = 1;
1372              line = line1;              line = line1;
1373              byteIndex = index1.byteIndex;              byteIndex = index1.byteIndex;
1374          } else if (index1.linePtr == textPtr->topIndex.linePtr) {          } else if (index1.linePtr == textPtr->topIndex.linePtr) {
1375              /*              /*
1376               * Deletion range starts on top line but after topIndex.               * Deletion range starts on top line but after topIndex.
1377               * Use the current topIndex as the new one.               * Use the current topIndex as the new one.
1378               */               */
1379    
1380              resetView = 1;              resetView = 1;
1381              line = line1;              line = line1;
1382              byteIndex = textPtr->topIndex.byteIndex;              byteIndex = textPtr->topIndex.byteIndex;
1383          }          }
1384      } else if (index2.linePtr == textPtr->topIndex.linePtr) {      } else if (index2.linePtr == textPtr->topIndex.linePtr) {
1385          /*          /*
1386           * Deletion range ends on top line but before topIndex.           * Deletion range ends on top line but before topIndex.
1387           * Figure out what will be the new character index for           * Figure out what will be the new character index for
1388           * the character currently pointed to by topIndex.           * the character currently pointed to by topIndex.
1389           */           */
1390    
1391          resetView = 1;          resetView = 1;
1392          line = line2;          line = line2;
1393          byteIndex = textPtr->topIndex.byteIndex;          byteIndex = textPtr->topIndex.byteIndex;
1394          if (index1.linePtr != index2.linePtr) {          if (index1.linePtr != index2.linePtr) {
1395              byteIndex -= index2.byteIndex;              byteIndex -= index2.byteIndex;
1396          } else {          } else {
1397              byteIndex -= (index2.byteIndex - index1.byteIndex);              byteIndex -= (index2.byteIndex - index1.byteIndex);
1398          }          }
1399      }      }
1400      TkBTreeDeleteChars(&index1, &index2);      TkBTreeDeleteChars(&index1, &index2);
1401      if (resetView) {      if (resetView) {
1402          TkTextMakeByteIndex(textPtr->tree, line, byteIndex, &index1);          TkTextMakeByteIndex(textPtr->tree, line, byteIndex, &index1);
1403          TkTextSetYView(textPtr, &index1, 0);          TkTextSetYView(textPtr, &index1, 0);
1404      }      }
1405    
1406      /*      /*
1407       * Invalidate any selection retrievals in progress.       * Invalidate any selection retrievals in progress.
1408       */       */
1409    
1410      textPtr->abortSelections = 1;      textPtr->abortSelections = 1;
1411    
1412      return TCL_OK;      return TCL_OK;
1413  }  }
1414    
1415  /*  /*
1416   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1417   *   *
1418   * TextFetchSelection --   * TextFetchSelection --
1419   *   *
1420   *      This procedure is called back by Tk when the selection is   *      This procedure is called back by Tk when the selection is
1421   *      requested by someone.  It returns part or all of the selection   *      requested by someone.  It returns part or all of the selection
1422   *      in a buffer provided by the caller.   *      in a buffer provided by the caller.
1423   *   *
1424   * Results:   * Results:
1425   *      The return value is the number of non-NULL bytes stored   *      The return value is the number of non-NULL bytes stored
1426   *      at buffer.  Buffer is filled (or partially filled) with a   *      at buffer.  Buffer is filled (or partially filled) with a
1427   *      NULL-terminated string containing part or all of the selection,   *      NULL-terminated string containing part or all of the selection,
1428   *      as given by offset and maxBytes.   *      as given by offset and maxBytes.
1429   *   *
1430   * Side effects:   * Side effects:
1431   *      None.   *      None.
1432   *   *
1433   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1434   */   */
1435    
1436  static int  static int
1437  TextFetchSelection(clientData, offset, buffer, maxBytes)  TextFetchSelection(clientData, offset, buffer, maxBytes)
1438      ClientData clientData;              /* Information about text widget. */      ClientData clientData;              /* Information about text widget. */
1439      int offset;                         /* Offset within selection of first      int offset;                         /* Offset within selection of first
1440                                           * character to be returned. */                                           * character to be returned. */
1441      char *buffer;                       /* Location in which to place      char *buffer;                       /* Location in which to place
1442                                           * selection. */                                           * selection. */
1443      int maxBytes;                       /* Maximum number of bytes to place      int maxBytes;                       /* Maximum number of bytes to place
1444                                           * at buffer, not including terminating                                           * at buffer, not including terminating
1445                                           * NULL character. */                                           * NULL character. */
1446  {  {
1447      register TkText *textPtr = (TkText *) clientData;      register TkText *textPtr = (TkText *) clientData;
1448      TkTextIndex eof;      TkTextIndex eof;
1449      int count, chunkSize, offsetInSeg;      int count, chunkSize, offsetInSeg;
1450      TkTextSearch search;      TkTextSearch search;
1451      TkTextSegment *segPtr;      TkTextSegment *segPtr;
1452    
1453      if (!textPtr->exportSelection) {      if (!textPtr->exportSelection) {
1454          return -1;          return -1;
1455      }      }
1456    
1457      /*      /*
1458       * Find the beginning of the next range of selected text.  Note:  if       * Find the beginning of the next range of selected text.  Note:  if
1459       * the selection is being retrieved in multiple pieces (offset != 0)       * the selection is being retrieved in multiple pieces (offset != 0)
1460       * and some modification has been made to the text that affects the       * and some modification has been made to the text that affects the
1461       * selection then reject the selection request (make 'em start over       * selection then reject the selection request (make 'em start over
1462       * again).       * again).
1463       */       */
1464    
1465      if (offset == 0) {      if (offset == 0) {
1466          TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->selIndex);          TkTextMakeByteIndex(textPtr->tree, 0, 0, &textPtr->selIndex);
1467          textPtr->abortSelections = 0;          textPtr->abortSelections = 0;
1468      } else if (textPtr->abortSelections) {      } else if (textPtr->abortSelections) {
1469          return 0;          return 0;
1470      }      }
1471      TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &eof);      TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &eof);
1472      TkBTreeStartSearch(&textPtr->selIndex, &eof, textPtr->selTagPtr, &search);      TkBTreeStartSearch(&textPtr->selIndex, &eof, textPtr->selTagPtr, &search);
1473      if (!TkBTreeCharTagged(&textPtr->selIndex, textPtr->selTagPtr)) {      if (!TkBTreeCharTagged(&textPtr->selIndex, textPtr->selTagPtr)) {
1474          if (!TkBTreeNextTag(&search)) {          if (!TkBTreeNextTag(&search)) {
1475              if (offset == 0) {              if (offset == 0) {
1476                  return -1;                  return -1;
1477              } else {              } else {
1478                  return 0;                  return 0;
1479              }              }
1480          }          }
1481          textPtr->selIndex = search.curIndex;          textPtr->selIndex = search.curIndex;
1482      }      }
1483    
1484      /*      /*
1485       * Each iteration through the outer loop below scans one selected range.       * Each iteration through the outer loop below scans one selected range.
1486       * Each iteration through the inner loop scans one segment in the       * Each iteration through the inner loop scans one segment in the
1487       * selected range.       * selected range.
1488       */       */
1489    
1490      count = 0;      count = 0;
1491      while (1) {      while (1) {
1492          /*          /*
1493           * Find the end of the current range of selected text.           * Find the end of the current range of selected text.
1494           */           */
1495    
1496          if (!TkBTreeNextTag(&search)) {          if (!TkBTreeNextTag(&search)) {
1497              panic("TextFetchSelection couldn't find end of range");              panic("TextFetchSelection couldn't find end of range");
1498          }          }
1499    
1500          /*          /*
1501           * Copy information from character segments into the buffer           * Copy information from character segments into the buffer
1502           * until either we run out of space in the buffer or we get           * until either we run out of space in the buffer or we get
1503           * to the end of this range of text.           * to the end of this range of text.
1504           */           */
1505    
1506          while (1) {          while (1) {
1507              if (maxBytes == 0) {              if (maxBytes == 0) {
1508                  goto done;                  goto done;
1509              }              }
1510              segPtr = TkTextIndexToSeg(&textPtr->selIndex, &offsetInSeg);              segPtr = TkTextIndexToSeg(&textPtr->selIndex, &offsetInSeg);
1511              chunkSize = segPtr->size - offsetInSeg;              chunkSize = segPtr->size - offsetInSeg;
1512              if (chunkSize > maxBytes) {              if (chunkSize > maxBytes) {
1513                  chunkSize = maxBytes;                  chunkSize = maxBytes;
1514              }              }
1515              if (textPtr->selIndex.linePtr == search.curIndex.linePtr) {              if (textPtr->selIndex.linePtr == search.curIndex.linePtr) {
1516                  int leftInRange;                  int leftInRange;
1517    
1518                  leftInRange = search.curIndex.byteIndex                  leftInRange = search.curIndex.byteIndex
1519                          - textPtr->selIndex.byteIndex;                          - textPtr->selIndex.byteIndex;
1520                  if (leftInRange < chunkSize) {                  if (leftInRange < chunkSize) {
1521                      chunkSize = leftInRange;                      chunkSize = leftInRange;
1522                      if (chunkSize <= 0) {                      if (chunkSize <= 0) {
1523                          break;                          break;
1524                      }                      }
1525                  }                  }
1526              }              }
1527              if ((segPtr->typePtr == &tkTextCharType)              if ((segPtr->typePtr == &tkTextCharType)
1528                      && !TkTextIsElided(textPtr, &textPtr->selIndex)) {                      && !TkTextIsElided(textPtr, &textPtr->selIndex)) {
1529                  memcpy((VOID *) buffer, (VOID *) (segPtr->body.chars                  memcpy((VOID *) buffer, (VOID *) (segPtr->body.chars
1530                          + offsetInSeg), (size_t) chunkSize);                          + offsetInSeg), (size_t) chunkSize);
1531                  buffer += chunkSize;                  buffer += chunkSize;
1532                  maxBytes -= chunkSize;                  maxBytes -= chunkSize;
1533                  count += chunkSize;                  count += chunkSize;
1534              }              }
1535              TkTextIndexForwBytes(&textPtr->selIndex, chunkSize,              TkTextIndexForwBytes(&textPtr->selIndex, chunkSize,
1536                      &textPtr->selIndex);                      &textPtr->selIndex);
1537          }          }
1538    
1539          /*          /*
1540           * Find the beginning of the next range of selected text.           * Find the beginning of the next range of selected text.
1541           */           */
1542    
1543          if (!TkBTreeNextTag(&search)) {          if (!TkBTreeNextTag(&search)) {
1544              break;              break;
1545          }          }
1546          textPtr->selIndex = search.curIndex;          textPtr->selIndex = search.curIndex;
1547      }      }
1548    
1549      done:      done:
1550      *buffer = 0;      *buffer = 0;
1551      return count;      return count;
1552  }  }
1553    
1554  /*  /*
1555   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1556   *   *
1557   * TkTextLostSelection --   * TkTextLostSelection --
1558   *   *
1559   *      This procedure is called back by Tk when the selection is   *      This procedure is called back by Tk when the selection is
1560   *      grabbed away from a text widget.  On Windows and Mac systems, we   *      grabbed away from a text widget.  On Windows and Mac systems, we
1561   *      want to remember the selection for the next time the focus   *      want to remember the selection for the next time the focus
1562   *      enters the window.  On Unix, just remove the "sel" tag from   *      enters the window.  On Unix, just remove the "sel" tag from
1563   *      everything in the widget.   *      everything in the widget.
1564   *   *
1565   * Results:   * Results:
1566   *      None.   *      None.
1567   *   *
1568   * Side effects:   * Side effects:
1569   *      The "sel" tag is cleared from the window.   *      The "sel" tag is cleared from the window.
1570   *   *
1571   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1572   */   */
1573    
1574  void  void
1575  TkTextLostSelection(clientData)  TkTextLostSelection(clientData)
1576      ClientData clientData;              /* Information about text widget. */      ClientData clientData;              /* Information about text widget. */
1577  {  {
1578      register TkText *textPtr = (TkText *) clientData;      register TkText *textPtr = (TkText *) clientData;
1579  #ifdef ALWAYS_SHOW_SELECTION  #ifdef ALWAYS_SHOW_SELECTION
1580      TkTextIndex start, end;      TkTextIndex start, end;
1581    
1582      if (!textPtr->exportSelection) {      if (!textPtr->exportSelection) {
1583          return;          return;
1584      }      }
1585    
1586      /*      /*
1587       * On Windows and Mac systems, we want to remember the selection       * On Windows and Mac systems, we want to remember the selection
1588       * for the next time the focus enters the window.  On Unix,       * for the next time the focus enters the window.  On Unix,
1589       * just remove the "sel" tag from everything in the widget.       * just remove the "sel" tag from everything in the widget.
1590       */       */
1591    
1592      TkTextMakeByteIndex(textPtr->tree, 0, 0, &start);      TkTextMakeByteIndex(textPtr->tree, 0, 0, &start);
1593      TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &end);      TkTextMakeByteIndex(textPtr->tree, TkBTreeNumLines(textPtr->tree), 0, &end);
1594      TkTextRedrawTag(textPtr, &start, &end, textPtr->selTagPtr, 1);      TkTextRedrawTag(textPtr, &start, &end, textPtr->selTagPtr, 1);
1595      TkBTreeTag(&start, &end, textPtr->selTagPtr, 0);      TkBTreeTag(&start, &end, textPtr->selTagPtr, 0);
1596  #endif  #endif
1597      textPtr->flags &= ~GOT_SELECTION;      textPtr->flags &= ~GOT_SELECTION;
1598  }  }
1599    
1600  /*  /*
1601   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1602   *   *
1603   * TextBlinkProc --   * TextBlinkProc --
1604   *   *
1605   *      This procedure is called as a timer handler to blink the   *      This procedure is called as a timer handler to blink the
1606   *      insertion cursor off and on.   *      insertion cursor off and on.
1607   *   *
1608   * Results:   * Results:
1609   *      None.   *      None.
1610   *   *
1611   * Side effects:   * Side effects:
1612   *      The cursor gets turned on or off, redisplay gets invoked,   *      The cursor gets turned on or off, redisplay gets invoked,
1613   *      and this procedure reschedules itself.   *      and this procedure reschedules itself.
1614   *   *
1615   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1616   */   */
1617    
1618  static void  static void
1619  TextBlinkProc(clientData)  TextBlinkProc(clientData)
1620      ClientData clientData;      /* Pointer to record describing text. */      ClientData clientData;      /* Pointer to record describing text. */
1621  {  {
1622      register TkText *textPtr = (TkText *) clientData;      register TkText *textPtr = (TkText *) clientData;
1623      TkTextIndex index;      TkTextIndex index;
1624      int x, y, w, h;      int x, y, w, h;
1625    
1626      if ((textPtr->state == TK_STATE_DISABLED) ||      if ((textPtr->state == TK_STATE_DISABLED) ||
1627              !(textPtr->flags & GOT_FOCUS) || (textPtr->insertOffTime == 0)) {              !(textPtr->flags & GOT_FOCUS) || (textPtr->insertOffTime == 0)) {
1628          return;          return;
1629      }      }
1630      if (textPtr->flags & INSERT_ON) {      if (textPtr->flags & INSERT_ON) {
1631          textPtr->flags &= ~INSERT_ON;          textPtr->flags &= ~INSERT_ON;
1632          textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(          textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
1633                  textPtr->insertOffTime, TextBlinkProc, (ClientData) textPtr);                  textPtr->insertOffTime, TextBlinkProc, (ClientData) textPtr);
1634      } else {      } else {
1635          textPtr->flags |= INSERT_ON;          textPtr->flags |= INSERT_ON;
1636          textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(          textPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
1637                  textPtr->insertOnTime, TextBlinkProc, (ClientData) textPtr);                  textPtr->insertOnTime, TextBlinkProc, (ClientData) textPtr);
1638      }      }
1639      TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);      TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
1640      TkTextCharBbox(textPtr, &index, &x, &y, &w, &h);      TkTextCharBbox(textPtr, &index, &x, &y, &w, &h);
1641      TkTextRedrawRegion(textPtr, x - textPtr->insertWidth / 2, y,      TkTextRedrawRegion(textPtr, x - textPtr->insertWidth / 2, y,
1642              textPtr->insertWidth, h);              textPtr->insertWidth, h);
1643  }  }
1644    
1645  /*  /*
1646   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1647   *   *
1648   * TextSearchCmd --   * TextSearchCmd --
1649   *   *
1650   *      This procedure is invoked to process the "search" widget command   *      This procedure is invoked to process the "search" widget command
1651   *      for text widgets.  See the user documentation for details on what   *      for text widgets.  See the user documentation for details on what
1652   *      it does.   *      it does.
1653   *   *
1654   * Results:   * Results:
1655   *      A standard Tcl result.   *      A standard Tcl result.
1656   *   *
1657   * Side effects:   * Side effects:
1658   *      See the user documentation.   *      See the user documentation.
1659   *   *
1660   *----------------------------------------------------------------------   *----------------------------------------------------------------------
1661   */   */
1662    
1663  static int  static int
1664  TextSearchCmd(textPtr, interp, argc, argv)  TextSearchCmd(textPtr, interp, argc, argv)
1665      TkText *textPtr;            /* Information about text widget. */      TkText *textPtr;            /* Information about text widget. */
1666      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
1667      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
1668      char **argv;                /* Argument strings. */      char **argv;                /* Argument strings. */
1669  {  {
1670      int backwards, exact, searchElide, c, i, argsLeft, noCase, leftToScan;      int backwards, exact, searchElide, c, i, argsLeft, noCase, leftToScan;
1671      size_t length;      size_t length;
1672      int numLines, startingLine, startingByte, lineNum, firstByte, lastByte;      int numLines, startingLine, startingByte, lineNum, firstByte, lastByte;
1673      int code, matchLength, matchByte, passes, stopLine, searchWholeText;      int code, matchLength, matchByte, passes, stopLine, searchWholeText;
1674      int patLength;      int patLength;
1675      char *arg, *pattern, *varName, *p, *startOfLine;      char *arg, *pattern, *varName, *p, *startOfLine;
1676      char buffer[20];      char buffer[20];
1677      TkTextIndex index, stopIndex;      TkTextIndex index, stopIndex;
1678      Tcl_DString line, patDString;      Tcl_DString line, patDString;
1679      TkTextSegment *segPtr;      TkTextSegment *segPtr;
1680      TkTextLine *linePtr;      TkTextLine *linePtr;
1681      TkTextIndex curIndex;      TkTextIndex curIndex;
1682      Tcl_RegExp regexp = NULL;           /* Initialization needed only to      Tcl_RegExp regexp = NULL;           /* Initialization needed only to
1683                                           * prevent compiler warning. */                                           * prevent compiler warning. */
1684    
1685      /*      /*
1686       * Parse switches and other arguments.       * Parse switches and other arguments.
1687       */       */
1688    
1689      exact = 1;      exact = 1;
1690      searchElide = 0;      searchElide = 0;
1691      curIndex.tree = textPtr->tree;      curIndex.tree = textPtr->tree;
1692      backwards = 0;      backwards = 0;
1693      noCase = 0;      noCase = 0;
1694      varName = NULL;      varName = NULL;
1695      for (i = 2; i < argc; i++) {      for (i = 2; i < argc; i++) {
1696          arg = argv[i];          arg = argv[i];
1697          if (arg[0] != '-') {          if (arg[0] != '-') {
1698              break;              break;
1699          }          }
1700          length = strlen(arg);          length = strlen(arg);
1701          if (length < 2) {          if (length < 2) {
1702              badSwitch:              badSwitch:
1703              Tcl_AppendResult(interp, "bad switch \"", arg,              Tcl_AppendResult(interp, "bad switch \"", arg,
1704                      "\": must be --, -backward, -count, -elide, -exact, ",                      "\": must be --, -backward, -count, -elide, -exact, ",
1705                      "-forward, -nocase, or -regexp", (char *) NULL);                      "-forward, -nocase, or -regexp", (char *) NULL);
1706              return TCL_ERROR;              return TCL_ERROR;
1707          }          }
1708          c = arg[1];          c = arg[1];
1709          if ((c == 'b') && (strncmp(argv[i], "-backwards", length) == 0)) {          if ((c == 'b') && (strncmp(argv[i], "-backwards", length) == 0)) {
1710              backwards = 1;              backwards = 1;
1711          } else if ((c == 'c') && (strncmp(argv[i], "-count", length) == 0)) {          } else if ((c == 'c') && (strncmp(argv[i], "-count", length) == 0)) {
1712              if (i >= (argc-1)) {              if (i >= (argc-1)) {
1713                  Tcl_SetResult(interp, "no value given for \"-count\" option",                  Tcl_SetResult(interp, "no value given for \"-count\" option",
1714                          TCL_STATIC);                          TCL_STATIC);
1715                  return TCL_ERROR;                  return TCL_ERROR;
1716              }              }
1717              i++;              i++;
1718              varName = argv[i];              varName = argv[i];
1719          } else if ((c == 'e') && (length > 2)          } else if ((c == 'e') && (length > 2)
1720                  && (strncmp(argv[i], "-exact", length) == 0)) {                  && (strncmp(argv[i], "-exact", length) == 0)) {
1721              exact = 1;              exact = 1;
1722          } else if ((c == 'e') && (length > 2)          } else if ((c == 'e') && (length > 2)
1723                  && (strncmp(argv[i], "-elide", length) == 0)) {                  && (strncmp(argv[i], "-elide", length) == 0)) {
1724              searchElide = 1;              searchElide = 1;
1725          } else if ((c == 'h') && (strncmp(argv[i], "-hidden", length) == 0)) {          } else if ((c == 'h') && (strncmp(argv[i], "-hidden", length) == 0)) {
1726              /*              /*
1727               * -hidden is kept around for backwards compatibility with               * -hidden is kept around for backwards compatibility with
1728               * the dash patch, but -elide is the official option               * the dash patch, but -elide is the official option
1729               */               */
1730              searchElide = 1;              searchElide = 1;
1731          } else if ((c == 'f') && (strncmp(argv[i], "-forwards", length) == 0)) {          } else if ((c == 'f') && (strncmp(argv[i], "-forwards", length) == 0)) {
1732              backwards = 0;              backwards = 0;
1733          } else if ((c == 'n') && (strncmp(argv[i], "-nocase", length) == 0)) {          } else if ((c == 'n') && (strncmp(argv[i], "-nocase", length) == 0)) {
1734              noCase = 1;              noCase = 1;
1735          } else if ((c == 'r') && (strncmp(argv[i], "-regexp", length) == 0)) {          } else if ((c == 'r') && (strncmp(argv[i], "-regexp", length) == 0)) {
1736              exact = 0;              exact = 0;
1737          } else if ((c == '-') && (strncmp(argv[i], "--", length) == 0)) {          } else if ((c == '-') && (strncmp(argv[i], "--", length) == 0)) {
1738              i++;              i++;
1739              break;              break;
1740          } else {          } else {
1741              goto badSwitch;              goto badSwitch;
1742          }          }
1743      }      }
1744      argsLeft = argc - (i+2);      argsLeft = argc - (i+2);
1745      if ((argsLeft != 0) && (argsLeft != 1)) {      if ((argsLeft != 0) && (argsLeft != 1)) {
1746          Tcl_AppendResult(interp, "wrong # args: should be \"",          Tcl_AppendResult(interp, "wrong # args: should be \"",
1747                  argv[0], " search ?switches? pattern index ?stopIndex?\"",                  argv[0], " search ?switches? pattern index ?stopIndex?\"",
1748                  (char *) NULL);                  (char *) NULL);
1749          return TCL_ERROR;          return TCL_ERROR;
1750      }      }
1751      pattern = argv[i];      pattern = argv[i];
1752    
1753      /*      /*
1754       * Convert the pattern to lower-case if we're supposed to ignore case.       * Convert the pattern to lower-case if we're supposed to ignore case.
1755       */       */
1756    
1757      if (noCase) {      if (noCase) {
1758          Tcl_DStringInit(&patDString);          Tcl_DStringInit(&patDString);
1759          Tcl_DStringAppend(&patDString, pattern, -1);          Tcl_DStringAppend(&patDString, pattern, -1);
1760          pattern = Tcl_DStringValue(&patDString);          pattern = Tcl_DStringValue(&patDString);
1761          Tcl_UtfToLower(pattern);          Tcl_UtfToLower(pattern);
1762      }      }
1763    
1764      Tcl_DStringInit(&line);      Tcl_DStringInit(&line);
1765      if (TkTextGetIndex(interp, textPtr, argv[i+1], &index) != TCL_OK) {      if (TkTextGetIndex(interp, textPtr, argv[i+1], &index) != TCL_OK) {
1766          code = TCL_ERROR;          code = TCL_ERROR;
1767          goto done;          goto done;
1768      }      }
1769      numLines = TkBTreeNumLines(textPtr->tree);      numLines = TkBTreeNumLines(textPtr->tree);
1770      startingLine = TkBTreeLineIndex(index.linePtr);      startingLine = TkBTreeLineIndex(index.linePtr);
1771      startingByte = index.byteIndex;      startingByte = index.byteIndex;
1772      if (startingLine >= numLines) {      if (startingLine >= numLines) {
1773          if (backwards) {          if (backwards) {
1774              startingLine = TkBTreeNumLines(textPtr->tree) - 1;              startingLine = TkBTreeNumLines(textPtr->tree) - 1;
1775              startingByte = TkBTreeBytesInLine(TkBTreeFindLine(textPtr->tree,              startingByte = TkBTreeBytesInLine(TkBTreeFindLine(textPtr->tree,
1776                      startingLine));                      startingLine));
1777          } else {          } else {
1778              startingLine = 0;              startingLine = 0;
1779              startingByte = 0;              startingByte = 0;
1780          }          }
1781      }      }
1782      if (argsLeft == 1) {      if (argsLeft == 1) {
1783          if (TkTextGetIndex(interp, textPtr, argv[i+2], &stopIndex) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[i+2], &stopIndex) != TCL_OK) {
1784              code = TCL_ERROR;              code = TCL_ERROR;
1785              goto done;              goto done;
1786          }          }
1787          stopLine = TkBTreeLineIndex(stopIndex.linePtr);          stopLine = TkBTreeLineIndex(stopIndex.linePtr);
1788          if (!backwards && (stopLine == numLines)) {          if (!backwards && (stopLine == numLines)) {
1789              stopLine = numLines-1;              stopLine = numLines-1;
1790          }          }
1791          searchWholeText = 0;          searchWholeText = 0;
1792      } else {      } else {
1793          stopLine = 0;          stopLine = 0;
1794          searchWholeText = 1;          searchWholeText = 1;
1795      }      }
1796    
1797      /*      /*
1798       * Scan through all of the lines of the text circularly, starting       * Scan through all of the lines of the text circularly, starting
1799       * at the given index.       * at the given index.
1800       */       */
1801    
1802      matchLength = patLength = 0;        /* Only needed to prevent compiler      matchLength = patLength = 0;        /* Only needed to prevent compiler
1803                                           * warnings. */                                           * warnings. */
1804      if (exact) {      if (exact) {
1805          patLength = strlen(pattern);          patLength = strlen(pattern);
1806      } else {      } else {
1807          regexp = Tcl_RegExpCompile(interp, pattern);          regexp = Tcl_RegExpCompile(interp, pattern);
1808          if (regexp == NULL) {          if (regexp == NULL) {
1809              code = TCL_ERROR;              code = TCL_ERROR;
1810              goto done;              goto done;
1811          }          }
1812      }      }
1813      lineNum = startingLine;      lineNum = startingLine;
1814      code = TCL_OK;      code = TCL_OK;
1815      for (passes = 0; passes < 2; ) {      for (passes = 0; passes < 2; ) {
1816          if (lineNum >= numLines) {          if (lineNum >= numLines) {
1817              /*              /*
1818               * Don't search the dummy last line of the text.               * Don't search the dummy last line of the text.
1819               */               */
1820    
1821              goto nextLine;              goto nextLine;
1822          }          }
1823    
1824          /*          /*
1825           * Extract the text from the line.  If we're doing regular           * Extract the text from the line.  If we're doing regular
1826           * expression matching, drop the newline from the line, so           * expression matching, drop the newline from the line, so
1827           * that "$" can be used to match the end of the line.           * that "$" can be used to match the end of the line.
1828           */           */
1829    
1830          linePtr = TkBTreeFindLine(textPtr->tree, lineNum);          linePtr = TkBTreeFindLine(textPtr->tree, lineNum);
1831          curIndex.linePtr = linePtr; curIndex.byteIndex = 0;          curIndex.linePtr = linePtr; curIndex.byteIndex = 0;
1832          for (segPtr = linePtr->segPtr; segPtr != NULL;          for (segPtr = linePtr->segPtr; segPtr != NULL;
1833                  curIndex.byteIndex += segPtr->size, segPtr = segPtr->nextPtr) {                  curIndex.byteIndex += segPtr->size, segPtr = segPtr->nextPtr) {
1834              if ((segPtr->typePtr != &tkTextCharType)              if ((segPtr->typePtr != &tkTextCharType)
1835                      || (!searchElide && TkTextIsElided(textPtr, &curIndex))) {                      || (!searchElide && TkTextIsElided(textPtr, &curIndex))) {
1836                  continue;                  continue;
1837              }              }
1838              Tcl_DStringAppend(&line, segPtr->body.chars, segPtr->size);              Tcl_DStringAppend(&line, segPtr->body.chars, segPtr->size);
1839          }          }
1840          if (!exact) {          if (!exact) {
1841              Tcl_DStringSetLength(&line, Tcl_DStringLength(&line)-1);              Tcl_DStringSetLength(&line, Tcl_DStringLength(&line)-1);
1842          }          }
1843          startOfLine = Tcl_DStringValue(&line);          startOfLine = Tcl_DStringValue(&line);
1844    
1845          /*          /*
1846           * If we're ignoring case, convert the line to lower case.           * If we're ignoring case, convert the line to lower case.
1847           */           */
1848    
1849          if (noCase) {          if (noCase) {
1850              Tcl_DStringSetLength(&line,              Tcl_DStringSetLength(&line,
1851                      Tcl_UtfToLower(Tcl_DStringValue(&line)));                      Tcl_UtfToLower(Tcl_DStringValue(&line)));
1852          }          }
1853    
1854          /*          /*
1855           * Check for matches within the current line.  If so, and if we're           * Check for matches within the current line.  If so, and if we're
1856           * searching backwards, repeat the search to find the last match           * searching backwards, repeat the search to find the last match
1857           * in the line.  (Note: The lastByte should include the NULL char           * in the line.  (Note: The lastByte should include the NULL char
1858           * so we can handle searching for end of line easier.)           * so we can handle searching for end of line easier.)
1859           */           */
1860    
1861          matchByte = -1;          matchByte = -1;
1862          firstByte = 0;          firstByte = 0;
1863          lastByte = Tcl_DStringLength(&line) + 1;          lastByte = Tcl_DStringLength(&line) + 1;
1864          if (lineNum == startingLine) {          if (lineNum == startingLine) {
1865              int indexInDString;              int indexInDString;
1866    
1867              /*              /*
1868               * The starting line is tricky: the first time we see it               * The starting line is tricky: the first time we see it
1869               * we check one part of the line, and the second pass through               * we check one part of the line, and the second pass through
1870               * we check the other part of the line.  We have to be very               * we check the other part of the line.  We have to be very
1871               * careful here because there could be embedded windows or               * careful here because there could be embedded windows or
1872               * other things that are not in the extracted line.  Rescan               * other things that are not in the extracted line.  Rescan
1873               * the original line to compute the index in it of the first               * the original line to compute the index in it of the first
1874               * character.               * character.
1875               */               */
1876    
1877              indexInDString = startingByte;              indexInDString = startingByte;
1878              for (segPtr = linePtr->segPtr, leftToScan = startingByte;              for (segPtr = linePtr->segPtr, leftToScan = startingByte;
1879                      leftToScan > 0; segPtr = segPtr->nextPtr) {                      leftToScan > 0; segPtr = segPtr->nextPtr) {
1880                  if (segPtr->typePtr != &tkTextCharType) {                  if (segPtr->typePtr != &tkTextCharType) {
1881                      indexInDString -= segPtr->size;                      indexInDString -= segPtr->size;
1882                  }                  }
1883                  leftToScan -= segPtr->size;                  leftToScan -= segPtr->size;
1884              }              }
1885    
1886              passes++;              passes++;
1887              if ((passes == 1) ^ backwards) {              if ((passes == 1) ^ backwards) {
1888                  /*                  /*
1889                   * Only use the last part of the line.                   * Only use the last part of the line.
1890                   */                   */
1891    
1892                  firstByte = indexInDString;                  firstByte = indexInDString;
1893                  if ((firstByte >= Tcl_DStringLength(&line))                  if ((firstByte >= Tcl_DStringLength(&line))
1894                          && !((Tcl_DStringLength(&line) == 0) && !exact)) {                          && !((Tcl_DStringLength(&line) == 0) && !exact)) {
1895                      goto nextLine;                      goto nextLine;
1896                  }                  }
1897              } else {              } else {
1898                  /*                  /*
1899                   * Use only the first part of the line.                   * Use only the first part of the line.
1900                   */                   */
1901    
1902                  lastByte = indexInDString;                  lastByte = indexInDString;
1903              }              }
1904          }          }
1905          do {          do {
1906              int thisLength;              int thisLength;
1907              Tcl_UniChar ch;              Tcl_UniChar ch;
1908    
1909              if (exact) {              if (exact) {
1910                  p = strstr(startOfLine + firstByte,     /* INTL: Native. */                  p = strstr(startOfLine + firstByte,     /* INTL: Native. */
1911                          pattern);                          pattern);
1912                  if (p == NULL) {                  if (p == NULL) {
1913                      break;                      break;
1914                  }                  }
1915                  i = p - startOfLine;                  i = p - startOfLine;
1916                  thisLength = patLength;                  thisLength = patLength;
1917              } else {              } else {
1918                  char *start, *end;                  char *start, *end;
1919                  int match;                  int match;
1920    
1921                  match = Tcl_RegExpExec(interp, regexp,                  match = Tcl_RegExpExec(interp, regexp,
1922                          startOfLine + firstByte, startOfLine);                          startOfLine + firstByte, startOfLine);
1923                  if (match < 0) {                  if (match < 0) {
1924                      code = TCL_ERROR;                      code = TCL_ERROR;
1925                      goto done;                      goto done;
1926                  }                  }
1927                  if (!match) {                  if (!match) {
1928                      break;                      break;
1929                  }                  }
1930                  Tcl_RegExpRange(regexp, 0, &start, &end);                  Tcl_RegExpRange(regexp, 0, &start, &end);
1931                  i = start - startOfLine;                  i = start - startOfLine;
1932                  thisLength = end - start;                  thisLength = end - start;
1933              }              }
1934              if (i >= lastByte) {              if (i >= lastByte) {
1935                  break;                  break;
1936              }              }
1937              matchByte = i;              matchByte = i;
1938              matchLength = thisLength;              matchLength = thisLength;
1939              firstByte = i + Tcl_UtfToUniChar(startOfLine + matchByte, &ch);              firstByte = i + Tcl_UtfToUniChar(startOfLine + matchByte, &ch);
1940          } while (backwards);          } while (backwards);
1941    
1942          /*          /*
1943           * If we found a match then we're done.  Make sure that           * If we found a match then we're done.  Make sure that
1944           * the match occurred before the stopping index, if one was           * the match occurred before the stopping index, if one was
1945           * specified.           * specified.
1946           */           */
1947    
1948          if (matchByte >= 0) {          if (matchByte >= 0) {
1949              int numChars;              int numChars;
1950    
1951              /*              /*
1952               * Convert the byte length to a character count.               * Convert the byte length to a character count.
1953               */               */
1954    
1955              numChars = Tcl_NumUtfChars(startOfLine + matchByte,              numChars = Tcl_NumUtfChars(startOfLine + matchByte,
1956                      matchLength);                      matchLength);
1957    
1958              /*              /*
1959               * The index information returned by the regular expression               * The index information returned by the regular expression
1960               * parser only considers textual information:  it doesn't               * parser only considers textual information:  it doesn't
1961               * account for embedded windows or any other non-textual info.               * account for embedded windows or any other non-textual info.
1962               * Scan through the line's segments again to adjust both               * Scan through the line's segments again to adjust both
1963               * matchChar and matchCount.               * matchChar and matchCount.
1964               */               */
1965    
1966              for (segPtr = linePtr->segPtr, leftToScan = matchByte;              for (segPtr = linePtr->segPtr, leftToScan = matchByte;
1967                      leftToScan >= 0; segPtr = segPtr->nextPtr) {                      leftToScan >= 0; segPtr = segPtr->nextPtr) {
1968                  if (segPtr->typePtr != &tkTextCharType) {                  if (segPtr->typePtr != &tkTextCharType) {
1969                      matchByte += segPtr->size;                      matchByte += segPtr->size;
1970                      continue;                      continue;
1971                  }                  }
1972                  leftToScan -= segPtr->size;                  leftToScan -= segPtr->size;
1973              }              }
1974              for (leftToScan += matchLength; leftToScan > 0;              for (leftToScan += matchLength; leftToScan > 0;
1975                      segPtr = segPtr->nextPtr) {                      segPtr = segPtr->nextPtr) {
1976                  if (segPtr->typePtr != &tkTextCharType) {                  if (segPtr->typePtr != &tkTextCharType) {
1977                      numChars += segPtr->size;                      numChars += segPtr->size;
1978                      continue;                      continue;
1979                  }                  }
1980                  leftToScan -= segPtr->size;                  leftToScan -= segPtr->size;
1981              }              }
1982              TkTextMakeByteIndex(textPtr->tree, lineNum, matchByte, &index);              TkTextMakeByteIndex(textPtr->tree, lineNum, matchByte, &index);
1983              if (!searchWholeText) {              if (!searchWholeText) {
1984                  if (!backwards && (TkTextIndexCmp(&index, &stopIndex) >= 0)) {                  if (!backwards && (TkTextIndexCmp(&index, &stopIndex) >= 0)) {
1985                      goto done;                      goto done;
1986                  }                  }
1987                  if (backwards && (TkTextIndexCmp(&index, &stopIndex) < 0)) {                  if (backwards && (TkTextIndexCmp(&index, &stopIndex) < 0)) {
1988                      goto done;                      goto done;
1989                  }                  }
1990              }              }
1991              if (varName != NULL) {              if (varName != NULL) {
1992                  sprintf(buffer, "%d", numChars);                  sprintf(buffer, "%d", numChars);
1993                  if (Tcl_SetVar(interp, varName, buffer, TCL_LEAVE_ERR_MSG)                  if (Tcl_SetVar(interp, varName, buffer, TCL_LEAVE_ERR_MSG)
1994                          == NULL) {                          == NULL) {
1995                      code = TCL_ERROR;                      code = TCL_ERROR;
1996                      goto done;                      goto done;
1997                  }                  }
1998              }              }
1999              TkTextPrintIndex(&index, buffer);              TkTextPrintIndex(&index, buffer);
2000              Tcl_SetResult(interp, buffer, TCL_VOLATILE);              Tcl_SetResult(interp, buffer, TCL_VOLATILE);
2001              goto done;              goto done;
2002          }          }
2003    
2004          /*          /*
2005           * Go to the next (or previous) line;           * Go to the next (or previous) line;
2006           */           */
2007    
2008          nextLine:          nextLine:
2009          if (backwards) {          if (backwards) {
2010              lineNum--;              lineNum--;
2011              if (!searchWholeText) {              if (!searchWholeText) {
2012                  if (lineNum < stopLine) {                  if (lineNum < stopLine) {
2013                      break;                      break;
2014                  }                  }
2015              } else if (lineNum < 0) {              } else if (lineNum < 0) {
2016                  lineNum = numLines-1;                  lineNum = numLines-1;
2017              }              }
2018          } else {          } else {
2019              lineNum++;              lineNum++;
2020              if (!searchWholeText) {              if (!searchWholeText) {
2021                  if (lineNum > stopLine) {                  if (lineNum > stopLine) {
2022                      break;                      break;
2023                  }                  }
2024              } else if (lineNum >= numLines) {              } else if (lineNum >= numLines) {
2025                  lineNum = 0;                  lineNum = 0;
2026              }              }
2027          }          }
2028          Tcl_DStringSetLength(&line, 0);          Tcl_DStringSetLength(&line, 0);
2029      }      }
2030      done:      done:
2031      Tcl_DStringFree(&line);      Tcl_DStringFree(&line);
2032      if (noCase) {      if (noCase) {
2033          Tcl_DStringFree(&patDString);          Tcl_DStringFree(&patDString);
2034      }      }
2035      return code;      return code;
2036  }  }
2037    
2038  /*  /*
2039   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2040   *   *
2041   * TkTextGetTabs --   * TkTextGetTabs --
2042   *   *
2043   *      Parses a string description of a set of tab stops.   *      Parses a string description of a set of tab stops.
2044   *   *
2045   * Results:   * Results:
2046   *      The return value is a pointer to a malloc'ed structure holding   *      The return value is a pointer to a malloc'ed structure holding
2047   *      parsed information about the tab stops.  If an error occurred   *      parsed information about the tab stops.  If an error occurred
2048   *      then the return value is NULL and an error message is left in   *      then the return value is NULL and an error message is left in
2049   *      the interp's result.   *      the interp's result.
2050   *   *
2051   * Side effects:   * Side effects:
2052   *      Memory is allocated for the structure that is returned.  It is   *      Memory is allocated for the structure that is returned.  It is
2053   *      up to the caller to free this structure when it is no longer   *      up to the caller to free this structure when it is no longer
2054   *      needed.   *      needed.
2055   *   *
2056   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2057   */   */
2058    
2059  TkTextTabArray *  TkTextTabArray *
2060  TkTextGetTabs(interp, tkwin, string)  TkTextGetTabs(interp, tkwin, string)
2061      Tcl_Interp *interp;                 /* Used for error reporting. */      Tcl_Interp *interp;                 /* Used for error reporting. */
2062      Tk_Window tkwin;                    /* Window in which the tabs will be      Tk_Window tkwin;                    /* Window in which the tabs will be
2063                                           * used. */                                           * used. */
2064      char *string;                       /* Description of the tab stops.  See      char *string;                       /* Description of the tab stops.  See
2065                                           * the text manual entry for details. */                                           * the text manual entry for details. */
2066  {  {
2067      int argc, i, count, c;      int argc, i, count, c;
2068      char **argv;      char **argv;
2069      TkTextTabArray *tabArrayPtr;      TkTextTabArray *tabArrayPtr;
2070      TkTextTab *tabPtr;      TkTextTab *tabPtr;
2071      Tcl_UniChar ch;      Tcl_UniChar ch;
2072    
2073      if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {      if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
2074          return NULL;          return NULL;
2075      }      }
2076    
2077      /*      /*
2078       * First find out how many entries we need to allocate in the       * First find out how many entries we need to allocate in the
2079       * tab array.       * tab array.
2080       */       */
2081    
2082      count = 0;      count = 0;
2083      for (i = 0; i < argc; i++) {      for (i = 0; i < argc; i++) {
2084          c = argv[i][0];          c = argv[i][0];
2085          if ((c != 'l') && (c != 'r') && (c != 'c') && (c != 'n')) {          if ((c != 'l') && (c != 'r') && (c != 'c') && (c != 'n')) {
2086              count++;              count++;
2087          }          }
2088      }      }
2089    
2090      /*      /*
2091       * Parse the elements of the list one at a time to fill in the       * Parse the elements of the list one at a time to fill in the
2092       * array.       * array.
2093       */       */
2094    
2095      tabArrayPtr = (TkTextTabArray *) ckalloc((unsigned)      tabArrayPtr = (TkTextTabArray *) ckalloc((unsigned)
2096              (sizeof(TkTextTabArray) + (count-1)*sizeof(TkTextTab)));              (sizeof(TkTextTabArray) + (count-1)*sizeof(TkTextTab)));
2097      tabArrayPtr->numTabs = 0;      tabArrayPtr->numTabs = 0;
2098      for (i = 0, tabPtr = &tabArrayPtr->tabs[0]; i  < argc; i++, tabPtr++) {      for (i = 0, tabPtr = &tabArrayPtr->tabs[0]; i  < argc; i++, tabPtr++) {
2099          if (Tk_GetPixels(interp, tkwin, argv[i], &tabPtr->location)          if (Tk_GetPixels(interp, tkwin, argv[i], &tabPtr->location)
2100                  != TCL_OK) {                  != TCL_OK) {
2101              goto error;              goto error;
2102          }          }
2103          tabArrayPtr->numTabs++;          tabArrayPtr->numTabs++;
2104    
2105          /*          /*
2106           * See if there is an explicit alignment in the next list           * See if there is an explicit alignment in the next list
2107           * element.  Otherwise just use "left".           * element.  Otherwise just use "left".
2108           */           */
2109    
2110          tabPtr->alignment = LEFT;          tabPtr->alignment = LEFT;
2111          if ((i+1) == argc) {          if ((i+1) == argc) {
2112              continue;              continue;
2113          }          }
2114          Tcl_UtfToUniChar(argv[i+1], &ch);          Tcl_UtfToUniChar(argv[i+1], &ch);
2115          if (!Tcl_UniCharIsAlpha(ch)) {          if (!Tcl_UniCharIsAlpha(ch)) {
2116              continue;              continue;
2117          }          }
2118          i += 1;          i += 1;
2119          c = argv[i][0];          c = argv[i][0];
2120          if ((c == 'l') && (strncmp(argv[i], "left",          if ((c == 'l') && (strncmp(argv[i], "left",
2121                  strlen(argv[i])) == 0)) {                  strlen(argv[i])) == 0)) {
2122              tabPtr->alignment = LEFT;              tabPtr->alignment = LEFT;
2123          } else if ((c == 'r') && (strncmp(argv[i], "right",          } else if ((c == 'r') && (strncmp(argv[i], "right",
2124                  strlen(argv[i])) == 0)) {                  strlen(argv[i])) == 0)) {
2125              tabPtr->alignment = RIGHT;              tabPtr->alignment = RIGHT;
2126          } else if ((c == 'c') && (strncmp(argv[i], "center",          } else if ((c == 'c') && (strncmp(argv[i], "center",
2127                  strlen(argv[i])) == 0)) {                  strlen(argv[i])) == 0)) {
2128              tabPtr->alignment = CENTER;              tabPtr->alignment = CENTER;
2129          } else if ((c == 'n') && (strncmp(argv[i],          } else if ((c == 'n') && (strncmp(argv[i],
2130                  "numeric", strlen(argv[i])) == 0)) {                  "numeric", strlen(argv[i])) == 0)) {
2131              tabPtr->alignment = NUMERIC;              tabPtr->alignment = NUMERIC;
2132          } else {          } else {
2133              Tcl_AppendResult(interp, "bad tab alignment \"",              Tcl_AppendResult(interp, "bad tab alignment \"",
2134                      argv[i], "\": must be left, right, center, or numeric",                      argv[i], "\": must be left, right, center, or numeric",
2135                      (char *) NULL);                      (char *) NULL);
2136              goto error;              goto error;
2137          }          }
2138      }      }
2139      ckfree((char *) argv);      ckfree((char *) argv);
2140      return tabArrayPtr;      return tabArrayPtr;
2141    
2142      error:      error:
2143      ckfree((char *) tabArrayPtr);      ckfree((char *) tabArrayPtr);
2144      ckfree((char *) argv);      ckfree((char *) argv);
2145      return NULL;      return NULL;
2146  }  }
2147    
2148  /*  /*
2149   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2150   *   *
2151   * TextDumpCmd --   * TextDumpCmd --
2152   *   *
2153   *      Return information about the text, tags, marks, and embedded windows   *      Return information about the text, tags, marks, and embedded windows
2154   *      and images in a text widget.  See the man page for the description   *      and images in a text widget.  See the man page for the description
2155   *      of the text dump operation for all the details.   *      of the text dump operation for all the details.
2156   *   *
2157   * Results:   * Results:
2158   *      A standard Tcl result.   *      A standard Tcl result.
2159   *   *
2160   * Side effects:   * Side effects:
2161   *      Memory is allocated for the result, if needed (standard Tcl result   *      Memory is allocated for the result, if needed (standard Tcl result
2162   *      side effects).   *      side effects).
2163   *   *
2164   *----------------------------------------------------------------------   *----------------------------------------------------------------------
2165   */   */
2166    
2167  static int  static int
2168  TextDumpCmd(textPtr, interp, argc, argv)  TextDumpCmd(textPtr, interp, argc, argv)
2169      register TkText *textPtr;   /* Information about text widget. */      register TkText *textPtr;   /* Information about text widget. */
2170      Tcl_Interp *interp;         /* Current interpreter. */      Tcl_Interp *interp;         /* Current interpreter. */
2171      int argc;                   /* Number of arguments. */      int argc;                   /* Number of arguments. */
2172      char **argv;                /* Argument strings.  Someone else has already      char **argv;                /* Argument strings.  Someone else has already
2173                                   * parsed this command enough to know that                                   * parsed this command enough to know that
2174                                   * argv[1] is "dump". */                                   * argv[1] is "dump". */
2175  {  {
2176      TkTextIndex index1, index2;      TkTextIndex index1, index2;
2177      int arg;      int arg;
2178      int lineno;                 /* Current line number */      int lineno;                 /* Current line number */
2179      int what = 0;               /* bitfield to select segment types */      int what = 0;               /* bitfield to select segment types */
2180      int atEnd;                  /* True if dumping up to logical end */      int atEnd;                  /* True if dumping up to logical end */
2181      TkTextLine *linePtr;      TkTextLine *linePtr;
2182      char *command = NULL;       /* Script callback to apply to segments */      char *command = NULL;       /* Script callback to apply to segments */
2183  #define TK_DUMP_TEXT    0x1  #define TK_DUMP_TEXT    0x1
2184  #define TK_DUMP_MARK    0x2  #define TK_DUMP_MARK    0x2
2185  #define TK_DUMP_TAG     0x4  #define TK_DUMP_TAG     0x4
2186  #define TK_DUMP_WIN     0x8  #define TK_DUMP_WIN     0x8
2187  #define TK_DUMP_IMG     0x10  #define TK_DUMP_IMG     0x10
2188  #define TK_DUMP_ALL     (TK_DUMP_TEXT|TK_DUMP_MARK|TK_DUMP_TAG| \  #define TK_DUMP_ALL     (TK_DUMP_TEXT|TK_DUMP_MARK|TK_DUMP_TAG| \
2189          TK_DUMP_WIN|TK_DUMP_IMG)          TK_DUMP_WIN|TK_DUMP_IMG)
2190    
2191      for (arg=2 ; argv[arg] != (char *) NULL ; arg++) {      for (arg=2 ; argv[arg] != (char *) NULL ; arg++) {
2192          size_t len;          size_t len;
2193          if (argv[arg][0] != '-') {          if (argv[arg][0] != '-') {
2194              break;              break;
2195          }          }
2196          len = strlen(argv[arg]);          len = strlen(argv[arg]);
2197          if (strncmp("-all", argv[arg], len) == 0) {          if (strncmp("-all", argv[arg], len) == 0) {
2198              what = TK_DUMP_ALL;              what = TK_DUMP_ALL;
2199          } else if (strncmp("-text", argv[arg], len) == 0) {          } else if (strncmp("-text", argv[arg], len) == 0) {
2200              what |= TK_DUMP_TEXT;              what |= TK_DUMP_TEXT;
2201          } else if (strncmp("-tag", argv[arg], len) == 0) {          } else if (strncmp("-tag", argv[arg], len) == 0) {
2202              what |= TK_DUMP_TAG;              what |= TK_DUMP_TAG;
2203          } else if (strncmp("-mark", argv[arg], len) == 0) {          } else if (strncmp("-mark", argv[arg], len) == 0) {
2204              what |= TK_DUMP_MARK;              what |= TK_DUMP_MARK;
2205          } else if (strncmp("-image", argv[arg], len) == 0) {          } else if (strncmp("-image", argv[arg], len) == 0) {
2206              what |= TK_DUMP_IMG;              what |= TK_DUMP_IMG;
2207          } else if (strncmp("-window", argv[arg], len) == 0) {          } else if (strncmp("-window", argv[arg], len) == 0) {
2208              what |= TK_DUMP_WIN;              what |= TK_DUMP_WIN;
2209          } else if (strncmp("-command", argv[arg], len) == 0) {          } else if (strncmp("-command", argv[arg], len) == 0) {
2210              arg++;              arg++;
2211              if (arg >= argc) {              if (arg >= argc) {
2212                  Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);                  Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);
2213                  return TCL_ERROR;                  return TCL_ERROR;
2214              }              }
2215              command = argv[arg];              command = argv[arg];
2216          } else {          } else {
2217              Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);              Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);
2218              return TCL_ERROR;              return TCL_ERROR;
2219          }          }
2220      }      }
2221      if (arg >= argc) {      if (arg >= argc) {
2222          Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);          Tcl_AppendResult(interp, "Usage: ", argv[0], " dump ?-all -image -text -mark -tag -window? ?-command script? index ?index2?", NULL);
2223          return TCL_ERROR;          return TCL_ERROR;
2224      }      }
2225      if (what == 0) {      if (what == 0) {
2226          what = TK_DUMP_ALL;          what = TK_DUMP_ALL;
2227      }      }
2228      if (TkTextGetIndex(interp, textPtr, argv[arg], &index1) != TCL_OK) {      if (TkTextGetIndex(interp, textPtr, argv[arg], &index1) != TCL_OK) {
2229          return TCL_ERROR;          return TCL_ERROR;
2230      }      }
2231      lineno = TkBTreeLineIndex(index1.linePtr);      lineno = TkBTreeLineIndex(index1.linePtr);
2232      arg++;      arg++;
2233      atEnd = 0;      atEnd = 0;
2234      if (argc == arg) {      if (argc == arg) {
2235          TkTextIndexForwChars(&index1, 1, &index2);          TkTextIndexForwChars(&index1, 1, &index2);
2236      } else {      } else {
2237          if (TkTextGetIndex(interp, textPtr, argv[arg], &index2) != TCL_OK) {          if (TkTextGetIndex(interp, textPtr, argv[arg], &index2) != TCL_OK) {
2238              return TCL_ERROR;              return TCL_ERROR;
2239          }          }
2240          if (strncmp(argv[arg], "end", strlen(argv[arg])) == 0) {          if (strncmp(argv[arg], "end", strlen(argv[arg])) == 0) {
2241              atEnd = 1;              atEnd = 1;
2242          }          }
2243      }      }
2244      if (TkTextIndexCmp(&index1, &index2) >= 0) {      if (TkTextIndexCmp(&index1, &index2) >= 0) {
2245          return TCL_OK;          return TCL_OK;
2246      }      }
2247      if (index1.linePtr == index2.linePtr) {      if (index1.linePtr == index2.linePtr) {
2248          DumpLine(interp, textPtr, what, index1.linePtr,          DumpLine(interp, textPtr, what, index1.linePtr,
2249              index1.byteIndex, index2.byteIndex, lineno, command);              index1.byteIndex, index2.byteIndex, lineno, command);
2250      } else {      } else {
2251          DumpLine(interp, textPtr, what, index1.linePtr,          DumpLine(interp, textPtr, what, index1.linePtr,
2252                  index1.byteIndex, 32000000, lineno, command);                  index1.byteIndex, 32000000, lineno, command);
2253          linePtr = index1.linePtr;          linePtr = index1.linePtr;
2254          while ((linePtr = TkBTreeNextLine(linePtr)) != (TkTextLine *)NULL) {          while ((linePtr = TkBTreeNextLine(linePtr)) != (TkTextLine *)NULL) {
2255              lineno++;              lineno++;
2256              if (linePtr == index2.linePtr) {              if (linePtr == index2.linePtr) {
2257                  break;                  break;
2258              }              }
2259              DumpLine(interp, textPtr, what, linePtr, 0, 32000000,              DumpLine(interp, textPtr, what, linePtr, 0, 32000000,
2260                      lineno, command);                      lineno, command);
2261          }          }
2262          DumpLine(interp, textPtr, what, index2.linePtr, 0,          DumpLine(interp, textPtr, what, index2.linePtr, 0,
2263                  index2.byteIndex, lineno, command);                  index2.byteIndex, lineno, command);
2264      }      }
2265      /*      /*
2266       * Special case to get the leftovers hiding at the end mark.       * Special case to get the leftovers hiding at the end mark.
2267       */       */
2268      if (atEnd) {      if (atEnd) {
2269          DumpLine(interp, textPtr, what & ~TK_DUMP_TEXT, index2.linePtr,          DumpLine(interp, textPtr, what & ~TK_DUMP_TEXT, index2.linePtr,
2270                  0, 1, lineno, command);                                      0, 1, lineno, command);                    
2271    
2272      }      }
2273      return TCL_OK;      return TCL_OK;
2274  }  }
2275    
2276  /*  /*
2277   * DumpLine   * DumpLine
2278   *      Return information about a given text line from character   *      Return information about a given text line from character
2279   *      position "start" up to, but not including, "end".   *      position "start" up to, but not including, "end".
2280   *   *
2281   * Results:   * Results:
2282   *      A standard Tcl result.   *      A standard Tcl result.
2283   *   *
2284   * Side effects:   * Side effects:
2285   *      None, but see DumpSegment.   *      None, but see DumpSegment.
2286   */   */
2287  static void  static void
2288  DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)  DumpLine(interp, textPtr, what, linePtr, startByte, endByte, lineno, command)
2289      Tcl_Interp *interp;      Tcl_Interp *interp;
2290      TkText *textPtr;      TkText *textPtr;
2291      int what;                   /* bit flags to select segment types */      int what;                   /* bit flags to select segment types */
2292      TkTextLine *linePtr;        /* The current line */      TkTextLine *linePtr;        /* The current line */
2293      int startByte, endByte;     /* Byte range to dump */      int startByte, endByte;     /* Byte range to dump */
2294      int lineno;                 /* Line number for indices dump */      int lineno;                 /* Line number for indices dump */
2295      char *command;              /* Script to apply to the segment */      char *command;              /* Script to apply to the segment */
2296  {  {
2297      int offset;      int offset;
2298      TkTextSegment *segPtr;      TkTextSegment *segPtr;
2299      TkTextIndex index;      TkTextIndex index;
2300      /*      /*
2301       * Must loop through line looking at its segments.       * Must loop through line looking at its segments.
2302       * character       * character
2303       * toggleOn, toggleOff       * toggleOn, toggleOff
2304       * mark       * mark
2305       * image       * image
2306       * window       * window
2307       */       */
2308    
2309      for (offset = 0, segPtr = linePtr->segPtr ;      for (offset = 0, segPtr = linePtr->segPtr ;
2310              (offset < endByte) && (segPtr != (TkTextSegment *)NULL) ;              (offset < endByte) && (segPtr != (TkTextSegment *)NULL) ;
2311              offset += segPtr->size, segPtr = segPtr->nextPtr) {              offset += segPtr->size, segPtr = segPtr->nextPtr) {
2312          if ((what & TK_DUMP_TEXT) && (segPtr->typePtr == &tkTextCharType) &&          if ((what & TK_DUMP_TEXT) && (segPtr->typePtr == &tkTextCharType) &&
2313                  (offset + segPtr->size > startByte)) {                  (offset + segPtr->size > startByte)) {
2314              char savedChar;                     /* Last char used in the seg */              char savedChar;                     /* Last char used in the seg */
2315              int last = segPtr->size;            /* Index of savedChar */              int last = segPtr->size;            /* Index of savedChar */
2316              int first = 0;                      /* Index of first char in seg */              int first = 0;                      /* Index of first char in seg */
2317              if (offset + segPtr->size > endByte) {              if (offset + segPtr->size > endByte) {
2318                  last = endByte - offset;                  last = endByte - offset;
2319              }              }
2320              if (startByte > offset) {              if (startByte > offset) {
2321                  first = startByte - offset;                  first = startByte - offset;
2322              }              }
2323              savedChar = segPtr->body.chars[last];              savedChar = segPtr->body.chars[last];
2324              segPtr->body.chars[last] = '\0';              segPtr->body.chars[last] = '\0';
2325                            
2326              TkTextMakeByteIndex(textPtr->tree, lineno, offset + first, &index);              TkTextMakeByteIndex(textPtr->tree, lineno, offset + first, &index);
2327              DumpSegment(interp, "text", segPtr->body.chars + first,              DumpSegment(interp, "text", segPtr->body.chars + first,
2328                      command, &index, what);                      command, &index, what);
2329              segPtr->body.chars[last] = savedChar;              segPtr->body.chars[last] = savedChar;
2330          } else if ((offset >= startByte)) {          } else if ((offset >= startByte)) {
2331              if ((what & TK_DUMP_MARK) && (segPtr->typePtr->name[0] == 'm')) {              if ((what & TK_DUMP_MARK) && (segPtr->typePtr->name[0] == 'm')) {
2332                  TkTextMark *markPtr = (TkTextMark *)&segPtr->body;                  TkTextMark *markPtr = (TkTextMark *)&segPtr->body;
2333                  char *name = Tcl_GetHashKey(&textPtr->markTable, markPtr->hPtr);                  char *name = Tcl_GetHashKey(&textPtr->markTable, markPtr->hPtr);
2334    
2335                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
2336                  DumpSegment(interp, "mark", name, command, &index, what);                  DumpSegment(interp, "mark", name, command, &index, what);
2337              } else if ((what & TK_DUMP_TAG) &&              } else if ((what & TK_DUMP_TAG) &&
2338                          (segPtr->typePtr == &tkTextToggleOnType)) {                          (segPtr->typePtr == &tkTextToggleOnType)) {
2339                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
2340                  DumpSegment(interp, "tagon",                  DumpSegment(interp, "tagon",
2341                          segPtr->body.toggle.tagPtr->name,                          segPtr->body.toggle.tagPtr->name,
2342                          command, &index, what);                          command, &index, what);
2343              } else if ((what & TK_DUMP_TAG) &&              } else if ((what & TK_DUMP_TAG) &&
2344                          (segPtr->typePtr == &tkTextToggleOffType)) {                          (segPtr->typePtr == &tkTextToggleOffType)) {
2345                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
2346                  DumpSegment(interp, "tagoff",                  DumpSegment(interp, "tagoff",
2347                          segPtr->body.toggle.tagPtr->name,                          segPtr->body.toggle.tagPtr->name,
2348                          command, &index, what);                          command, &index, what);
2349              } else if ((what & TK_DUMP_IMG) &&              } else if ((what & TK_DUMP_IMG) &&
2350                          (segPtr->typePtr->name[0] == 'i')) {                          (segPtr->typePtr->name[0] == 'i')) {
2351                  TkTextEmbImage *eiPtr = (TkTextEmbImage *)&segPtr->body;                  TkTextEmbImage *eiPtr = (TkTextEmbImage *)&segPtr->body;
2352                  char *name = (eiPtr->name ==  NULL) ? "" : eiPtr->name;                  char *name = (eiPtr->name ==  NULL) ? "" : eiPtr->name;
2353                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
2354                  DumpSegment(interp, "image", name,                  DumpSegment(interp, "image", name,
2355                          command, &index, what);                          command, &index, what);
2356              } else if ((what & TK_DUMP_WIN) &&              } else if ((what & TK_DUMP_WIN) &&
2357                          (segPtr->typePtr->name[0] == 'w')) {                          (segPtr->typePtr->name[0] == 'w')) {
2358                  TkTextEmbWindow *ewPtr = (TkTextEmbWindow *)&segPtr->body;                  TkTextEmbWindow *ewPtr = (TkTextEmbWindow *)&segPtr->body;
2359                  char *pathname;                  char *pathname;
2360                  if (ewPtr->tkwin == (Tk_Window) NULL) {                  if (ewPtr->tkwin == (Tk_Window) NULL) {
2361                      pathname = "";                      pathname = "";
2362                  } else {                  } else {
2363                      pathname = Tk_PathName(ewPtr->tkwin);                      pathname = Tk_PathName(ewPtr->tkwin);
2364                  }                  }
2365                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);                  TkTextMakeByteIndex(textPtr->tree, lineno, offset, &index);
2366                  DumpSegment(interp, "window", pathname,                  DumpSegment(interp, "window", pathname,
2367                          command, &index, what);                          command, &index, what);
2368              }              }
2369          }          }
2370      }      }
2371  }  }
2372    
2373  /*  /*
2374   * DumpSegment   * DumpSegment
2375   *      Either append information about the current segment to the result,   *      Either append information about the current segment to the result,
2376   *      or make a script callback with that information as arguments.   *      or make a script callback with that information as arguments.
2377   *   *
2378   * Results:   * Results:
2379   *      None   *      None
2380   *   *
2381   * Side effects:   * Side effects:
2382   *      Either evals the callback or appends elements to the result string.   *      Either evals the callback or appends elements to the result string.
2383   */   */
2384  static int  static int
2385  DumpSegment(interp, key, value, command, index, what)  DumpSegment(interp, key, value, command, index, what)
2386      Tcl_Interp *interp;      Tcl_Interp *interp;
2387      char *key;                  /* Segment type key */      char *key;                  /* Segment type key */
2388      char *value;                /* Segment value */      char *value;                /* Segment value */
2389      char *command;              /* Script callback */      char *command;              /* Script callback */
2390      TkTextIndex *index;         /* index with line/byte position info */      TkTextIndex *index;         /* index with line/byte position info */
2391      int what;                   /* Look for TK_DUMP_INDEX bit */      int what;                   /* Look for TK_DUMP_INDEX bit */
2392  {  {
2393      char buffer[TCL_INTEGER_SPACE*2];      char buffer[TCL_INTEGER_SPACE*2];
2394      TkTextPrintIndex(index, buffer);      TkTextPrintIndex(index, buffer);
2395      if (command == (char *) NULL) {      if (command == (char *) NULL) {
2396          Tcl_AppendElement(interp, key);          Tcl_AppendElement(interp, key);
2397          Tcl_AppendElement(interp, value);          Tcl_AppendElement(interp, value);
2398          Tcl_AppendElement(interp, buffer);          Tcl_AppendElement(interp, buffer);
2399          return TCL_OK;          return TCL_OK;
2400      } else {      } else {
2401          char *argv[4];          char *argv[4];
2402          char *list;          char *list;
2403          int result;          int result;
2404          argv[0] = key;          argv[0] = key;
2405          argv[1] = value;          argv[1] = value;
2406          argv[2] = buffer;          argv[2] = buffer;
2407          argv[3] = (char *) NULL;          argv[3] = (char *) NULL;
2408          list = Tcl_Merge(3, argv);          list = Tcl_Merge(3, argv);
2409          result = Tcl_VarEval(interp, command, " ", list, (char *) NULL);          result = Tcl_VarEval(interp, command, " ", list, (char *) NULL);
2410          ckfree(list);          ckfree(list);
2411          return result;          return result;
2412      }      }
2413  }  }
2414    
2415  /* End of tktext.c */  /* End of tktext.c */

Legend:
Removed from v.70  
changed lines
  Added in v.71

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25