/[dtapublic]/projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkentry.c
ViewVC logotype

Annotation of /projs/trunk/shared_source/c_tk_base_7_5_w_mods/tkentry.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations) (download)
Fri Oct 14 01:50:00 2016 UTC (7 years, 5 months ago) by dashley
Original Path: projs/trunk/shared_source/tk_base/tkentry.c
File MIME type: text/plain
File size: 94108 byte(s)
Move shared source code to commonize.
1 dashley 25 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkentry.c,v 1.1.1.1 2001/06/13 04:59:57 dtashley Exp $ */
2    
3     /*
4     * tkEntry.c --
5     *
6     * This module implements entry widgets for the Tk
7     * toolkit. An entry displays a string and allows
8     * the string to be edited.
9     *
10     * Copyright (c) 1990-1994 The Regents of the University of California.
11     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12     *
13     * See the file "license.terms" for information on usage and redistribution
14     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15     *
16     * RCS: @(#) $Id: tkentry.c,v 1.1.1.1 2001/06/13 04:59:57 dtashley Exp $
17     */
18    
19     #include "tkInt.h"
20     #include "default.h"
21    
22     /*
23     * A data structure of the following type is kept for each entry
24     * widget managed by this file:
25     */
26    
27     typedef struct {
28     Tk_Window tkwin; /* Window that embodies the entry. NULL
29     * means that the window has been destroyed
30     * but the data structures haven't yet been
31     * cleaned up.*/
32     Display *display; /* Display containing widget. Used, among
33     * other things, so that resources can be
34     * freed even after tkwin has gone away. */
35     Tcl_Interp *interp; /* Interpreter associated with entry. */
36     Tcl_Command widgetCmd; /* Token for entry's widget command. */
37     Tk_OptionTable optionTable; /* Table that defines configuration options
38     * available for this widget. */
39    
40    
41     /*
42     * Fields that are set by widget commands other than "configure".
43     */
44    
45     char *string; /* Pointer to storage for string;
46     * NULL-terminated; malloc-ed. */
47     int insertPos; /* Character index before which next typed
48     * character will be inserted. */
49    
50     /*
51     * Information about what's selected, if any.
52     */
53    
54     int selectFirst; /* Character index of first selected
55     * character (-1 means nothing selected. */
56     int selectLast; /* Character index just after last selected
57     * character (-1 means nothing selected. */
58     int selectAnchor; /* Fixed end of selection (i.e. "select to"
59     * operation will use this as one end of the
60     * selection). */
61    
62     /*
63     * Information for scanning:
64     */
65    
66     int scanMarkX; /* X-position at which scan started (e.g.
67     * button was pressed here). */
68     int scanMarkIndex; /* Character index of character that was at
69     * left of window when scan started. */
70    
71     /*
72     * Configuration settings that are updated by Tk_ConfigureWidget.
73     */
74    
75     Tk_3DBorder normalBorder; /* Used for drawing border around whole
76     * window, plus used for background. */
77     int borderWidth; /* Width of 3-D border around window. */
78     Tk_Cursor cursor; /* Current cursor for window, or None. */
79     int exportSelection; /* Non-zero means tie internal entry selection
80     * to X selection. */
81     Tk_Font tkfont; /* Information about text font, or NULL. */
82     XColor *fgColorPtr; /* Text color in normal mode. */
83     XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
84     * area when highlight is off. */
85     XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
86     int highlightWidth; /* Width in pixels of highlight to draw
87     * around widget when it has the focus.
88     * <= 0 means don't draw a highlight. */
89     Tk_3DBorder insertBorder; /* Used to draw vertical bar for insertion
90     * cursor. */
91     int insertBorderWidth; /* Width of 3-D border around insert cursor. */
92     int insertOffTime; /* Number of milliseconds cursor should spend
93     * in "off" state for each blink. */
94     int insertOnTime; /* Number of milliseconds cursor should spend
95     * in "on" state for each blink. */
96     int insertWidth; /* Total width of insert cursor. */
97     Tk_Justify justify; /* Justification to use for text within
98     * window. */
99     int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
100     Tk_3DBorder selBorder; /* Border and background for selected
101     * characters. */
102     int selBorderWidth; /* Width of border around selection. */
103     XColor *selFgColorPtr; /* Foreground color for selected text. */
104     char *showChar; /* Value of -show option. If non-NULL, first
105     * character is used for displaying all
106     * characters in entry. Malloc'ed. */
107     int state; /* Normal or disabled. Entry is read-only
108     * when disabled. */
109     char *textVarName; /* Name of variable (malloc'ed) or NULL.
110     * If non-NULL, entry's string tracks the
111     * contents of this variable and vice versa. */
112     char *takeFocus; /* Value of -takefocus option; not used in
113     * the C code, but used by keyboard traversal
114     * scripts. Malloc'ed, but may be NULL. */
115     int prefWidth; /* Desired width of window, measured in
116     * average characters. */
117     char *scrollCmd; /* Command prefix for communicating with
118     * scrollbar(s). Malloc'ed. NULL means
119     * no command to issue. */
120    
121     /*
122     * Fields whose values are derived from the current values of the
123     * configuration settings above.
124     */
125    
126     int numBytes; /* Length of string in bytes. */
127     int numChars; /* Length of string in characters. Both
128     * string and displayString have the same
129     * character length, but may have different
130     * byte lengths due to being made from
131     * different UTF-8 characters. */
132     char *displayString; /* String to use when displaying. This may
133     * be a pointer to string, or a pointer to
134     * malloced memory with the same character
135     * length as string but whose characters
136     * are all equal to showChar. */
137     int numDisplayBytes; /* Length of displayString in bytes. */
138     int inset; /* Number of pixels on the left and right
139     * sides that are taken up by XPAD, borderWidth
140     * (if any), and highlightWidth (if any). */
141     Tk_TextLayout textLayout; /* Cached text layout information. */
142     int layoutX, layoutY; /* Origin for layout. */
143     int leftX; /* X position at which character at leftIndex
144     * is drawn (varies depending on justify). */
145     int leftIndex; /* Character index of left-most character
146     * visible in window. */
147     Tcl_TimerToken insertBlinkHandler;
148     /* Timer handler used to blink cursor on and
149     * off. */
150     GC textGC; /* For drawing normal text. */
151     GC selTextGC; /* For drawing selected text. */
152     GC highlightGC; /* For drawing traversal highlight. */
153     int avgWidth; /* Width of average character. */
154     int flags; /* Miscellaneous flags; see below for
155     * definitions. */
156     Tk_TSOffset tsoffset;
157    
158     char *validateCmd; /* Command prefix to use when invoking
159     * validate command. NULL means don't
160     * invoke commands. Malloc'ed. */
161     int validate; /* Non-zero means try to validate */
162     char *invalidCmd; /* Command called when a validation returns 0
163     * (successfully fails), defaults to {}. */
164     } Entry;
165    
166     /*
167     * Assigned bits of "flags" fields of Entry structures, and what those
168     * bits mean:
169     *
170     * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has
171     * already been queued to redisplay the entry.
172     * BORDER_NEEDED: Non-zero means 3-D border must be redrawn
173     * around window during redisplay. Normally
174     * only text portion needs to be redrawn.
175     * CURSOR_ON: Non-zero means insert cursor is displayed at
176     * present. 0 means it isn't displayed.
177     * GOT_FOCUS: Non-zero means this window has the input
178     * focus.
179     * UPDATE_SCROLLBAR: Non-zero means scrollbar should be updated
180     * during next redisplay operation.
181     * GOT_SELECTION: Non-zero means we've claimed the selection.
182     * ENTRY_DELETED: This entry has been effectively destroyed.
183     * VALIDATING: Non-zero means we are in a validateCmd
184     * VALIDATE_VAR: Non-zero means we are attempting to validate
185     * the entry's textvariable with validateCmd
186     * VALIDATE_ABORT: Non-zero if validatecommand signals an abort
187     * for current procedure and make no changes
188     */
189    
190     #define REDRAW_PENDING 1
191     #define BORDER_NEEDED 2
192     #define CURSOR_ON 4
193     #define GOT_FOCUS 8
194     #define UPDATE_SCROLLBAR 0x10
195     #define GOT_SELECTION 0x20
196     #define ENTRY_DELETED 0x40
197     #define VALIDATING 0x80
198     #define VALIDATE_VAR 0x100
199     #define VALIDATE_ABORT 0x200
200    
201     /*
202     * The following macro defines how many extra pixels to leave on each
203     * side of the text in the entry.
204     */
205    
206     #define XPAD 1
207     #define YPAD 1
208    
209     /*
210     * The following enum is used to define a type for the -state option
211     * of the Entry widget. These values are used as indices into the
212     * string table below.
213     */
214    
215     enum state {
216     STATE_DISABLED, STATE_NORMAL
217     };
218    
219     static char *stateStrings[] = {
220     "disabled", "normal", (char *) NULL
221     };
222    
223     /*
224     * Definitions for -validate option values:
225     */
226    
227     static char *validateStrings[] = {
228     "all", "key", "focus", "focusin", "focusout", "none", (char *) NULL
229     };
230     enum validateType {
231     VALIDATE_ALL, VALIDATE_KEY, VALIDATE_FOCUS,
232     VALIDATE_FOCUSIN, VALIDATE_FOCUSOUT, VALIDATE_NONE,
233     /*
234     * These extra enums are for use with EntryValidateChange
235     */
236     VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT
237     };
238     #define DEF_ENTRY_VALIDATE "none"
239     #define DEF_ENTRY_INVALIDCMD ""
240    
241     /*
242     * Information used for argv parsing.
243     */
244    
245     static Tk_OptionSpec optionSpecs[] = {
246     {TK_OPTION_BORDER, "-background", "background", "Background",
247     DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder),
248     0, (ClientData) DEF_ENTRY_BG_MONO, 0},
249     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
250     (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
251     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
252     (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
253     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
254     DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth),
255     0, 0, 0},
256     {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
257     DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor),
258     TK_OPTION_NULL_OK, 0, 0},
259     {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
260     "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1,
261     Tk_Offset(Entry, exportSelection), 0, 0, 0},
262     {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
263     (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
264     {TK_OPTION_FONT, "-font", "font", "Font",
265     DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0},
266     {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
267     DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0,
268     0, 0},
269     {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
270     "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
271     -1, Tk_Offset(Entry, highlightBgColorPtr),
272     0, 0, 0},
273     {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
274     DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr),
275     0, 0, 0},
276     {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
277     "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1,
278     Tk_Offset(Entry, highlightWidth), 0, 0, 0},
279     {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground",
280     DEF_ENTRY_INSERT_BG,
281     -1, Tk_Offset(Entry, insertBorder),
282     0, 0, 0},
283     {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth",
284     "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1,
285     Tk_Offset(Entry, insertBorderWidth), 0,
286     (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0},
287     {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime",
288     DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime),
289     0, 0, 0},
290     {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime",
291     DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime),
292     0, 0, 0},
293     {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
294     DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth),
295     0, 0, 0},
296     {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand",
297     DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd),
298     TK_OPTION_NULL_OK, 0, 0},
299     {TK_OPTION_SYNONYM, "-invcmd", (char *) NULL, (char *) NULL,
300     (char *) NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0},
301     {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
302     DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0},
303     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
304     DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief),
305     0, 0, 0},
306     {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
307     DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder),
308     0, (ClientData) DEF_ENTRY_SELECT_MONO, 0},
309     {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth",
310     "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1,
311     Tk_Offset(Entry, selBorderWidth),
312     0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0},
313     {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
314     DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr),
315     0, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0},
316     {TK_OPTION_STRING, "-show", "show", "Show",
317     DEF_ENTRY_SHOW, -1, Tk_Offset(Entry, showChar),
318     TK_OPTION_NULL_OK, 0, 0},
319     {TK_OPTION_STRING_TABLE, "-state", "state", "State",
320     DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state),
321     0, (ClientData) stateStrings, 0},
322     {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
323     DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus),
324     TK_OPTION_NULL_OK, 0, 0},
325     {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
326     DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName),
327     TK_OPTION_NULL_OK, 0, 0},
328     {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate",
329     DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate),
330     0, (ClientData) validateStrings, 0},
331     {TK_OPTION_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
332     (char *) NULL, -1, Tk_Offset(Entry, validateCmd),
333     TK_OPTION_NULL_OK, 0, 0},
334     {TK_OPTION_SYNONYM, "-vcmd", (char *) NULL, (char *) NULL,
335     (char *) NULL, 0, -1, 0, (ClientData) "-validatecommand", 0},
336     {TK_OPTION_INT, "-width", "width", "Width",
337     DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0},
338     {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
339     DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd),
340     TK_OPTION_NULL_OK, 0, 0},
341     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
342     (char *) NULL, 0, -1, 0, 0, 0}
343     };
344    
345     /*
346     * Flags for GetEntryIndex procedure:
347     */
348    
349     #define ZERO_OK 1
350     #define LAST_PLUS_ONE_OK 2
351    
352     /*
353     * The following tables define the entry widget commands (and sub-
354     * commands) and map the indexes into the string tables into
355     * enumerated types used to dispatch the entry widget command.
356     */
357    
358     static char *commandNames[] = {
359     "bbox", "cget", "configure", "delete", "get", "icursor", "index",
360     "insert", "scan", "selection", "validate", "xview", (char *) NULL
361     };
362    
363     enum command {
364     COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE,
365     COMMAND_GET, COMMAND_ICURSOR, COMMAND_INDEX, COMMAND_INSERT,
366     COMMAND_SCAN, COMMAND_SELECTION, COMMAND_VALIDATE, COMMAND_XVIEW
367     };
368    
369     static char *selCommandNames[] = {
370     "adjust", "clear", "from", "present", "range", "to", (char *) NULL
371     };
372    
373     enum selcommand {
374     SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM,
375     SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO
376     };
377    
378     /*
379     * Forward declarations for procedures defined later in this file:
380     */
381    
382     static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
383     Entry *entryPtr, int objc,
384     Tcl_Obj *CONST objv[], int flags));
385     static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
386     int count));
387     static void DestroyEntry _ANSI_ARGS_((char *memPtr));
388     static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
389     static void EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
390     static void EntryCmdDeletedProc _ANSI_ARGS_((
391     ClientData clientData));
392     static void EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
393     static void EntryEventProc _ANSI_ARGS_((ClientData clientData,
394     XEvent *eventPtr));
395     static void EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
396     int gotFocus));
397     static int EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
398     int offset, char *buffer, int maxBytes));
399     static void EntryLostSelection _ANSI_ARGS_((
400     ClientData clientData));
401     static void EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
402     static void EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
403     static void EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
404     char *value));
405     static void EntrySelectTo _ANSI_ARGS_((
406     Entry *entryPtr, int index));
407     static char * EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
408     Tcl_Interp *interp, char *name1, char *name2,
409     int flags));
410     static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
411     static int EntryValidate _ANSI_ARGS_((Entry *entryPtr,
412     char *cmd));
413     static int EntryValidateChange _ANSI_ARGS_((Entry *entryPtr,
414     char *change, char *new, int index, int type));
415     static void ExpandPercents _ANSI_ARGS_((Entry *entryPtr,
416     char *before, char *change, char *new,
417     int index, int type, Tcl_DString *dsPtr));
418     static void EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
419     static void EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
420     double *firstPtr, double *lastPtr));
421     static int EntryWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
422     Tcl_Interp *interp, int objc,
423     Tcl_Obj *CONST objv[]));
424     static void EntryWorldChanged _ANSI_ARGS_((
425     ClientData instanceData));
426     static int GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
427     Entry *entryPtr, char *string, int *indexPtr));
428     static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
429     char *string));
430    
431     /*
432     * The structure below defines entry class behavior by means of procedures
433     * that can be invoked from generic window code.
434     */
435    
436     static TkClassProcs entryClass = {
437     NULL, /* createProc. */
438     EntryWorldChanged, /* geometryProc. */
439     NULL /* modalProc. */
440     };
441    
442    
443     /*
444     *--------------------------------------------------------------
445     *
446     * Tk_EntryObjCmd --
447     *
448     * This procedure is invoked to process the "entry" Tcl
449     * command. See the user documentation for details on what
450     * it does.
451     *
452     * Results:
453     * A standard Tcl result.
454     *
455     * Side effects:
456     * See the user documentation.
457     *
458     *--------------------------------------------------------------
459     */
460    
461     int
462     Tk_EntryObjCmd(clientData, interp, objc, objv)
463     ClientData clientData; /* Either NULL or pointer to option table. */
464     Tcl_Interp *interp; /* Current interpreter. */
465     int objc; /* Number of arguments. */
466     Tcl_Obj *CONST objv[]; /* Argument objects. */
467     {
468     register Entry *entryPtr;
469     Tk_OptionTable optionTable;
470     Tk_Window tkwin;
471    
472     optionTable = (Tk_OptionTable) clientData;
473     if (optionTable == NULL) {
474     Tcl_CmdInfo info;
475     char *name;
476    
477     /*
478     * We haven't created the option table for this widget class
479     * yet. Do it now and save the table as the clientData for
480     * the command, so we'll have access to it in future
481     * invocations of the command.
482     */
483    
484     optionTable = Tk_CreateOptionTable(interp, optionSpecs);
485     name = Tcl_GetString(objv[0]);
486     Tcl_GetCommandInfo(interp, name, &info);
487     info.objClientData = (ClientData) optionTable;
488     Tcl_SetCommandInfo(interp, name, &info);
489     }
490    
491     if (objc < 2) {
492     Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
493     return TCL_ERROR;
494     }
495    
496     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
497     Tcl_GetString(objv[1]), (char *) NULL);
498     if (tkwin == NULL) {
499     return TCL_ERROR;
500     }
501    
502     /*
503     * Initialize the fields of the structure that won't be initialized
504     * by ConfigureEntry, or that ConfigureEntry requires to be
505     * initialized already (e.g. resource pointers).
506     */
507    
508     entryPtr = (Entry *) ckalloc(sizeof(Entry));
509     entryPtr->tkwin = tkwin;
510     entryPtr->display = Tk_Display(tkwin);
511     entryPtr->interp = interp;
512     entryPtr->widgetCmd = Tcl_CreateObjCommand(interp,
513     Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd,
514     (ClientData) entryPtr, EntryCmdDeletedProc);
515     entryPtr->optionTable = optionTable;
516     entryPtr->string = (char *) ckalloc(1);
517     entryPtr->string[0] = '\0';
518     entryPtr->insertPos = 0;
519     entryPtr->selectFirst = -1;
520     entryPtr->selectLast = -1;
521     entryPtr->selectAnchor = 0;
522     entryPtr->scanMarkX = 0;
523     entryPtr->scanMarkIndex = 0;
524    
525     entryPtr->normalBorder = NULL;
526     entryPtr->borderWidth = 0;
527     entryPtr->cursor = None;
528     entryPtr->exportSelection = 1;
529     entryPtr->tkfont = NULL;
530     entryPtr->fgColorPtr = NULL;
531     entryPtr->highlightBgColorPtr = NULL;
532     entryPtr->highlightColorPtr = NULL;
533     entryPtr->highlightWidth = 0;
534     entryPtr->insertBorder = NULL;
535     entryPtr->insertBorderWidth = 0;
536     entryPtr->insertOffTime = 0;
537     entryPtr->insertOnTime = 0;
538     entryPtr->insertWidth = 0;
539     entryPtr->justify = TK_JUSTIFY_LEFT;
540     entryPtr->relief = TK_RELIEF_FLAT;
541     entryPtr->selBorder = NULL;
542     entryPtr->selBorderWidth = 0;
543     entryPtr->selFgColorPtr = NULL;
544     entryPtr->showChar = NULL;
545     entryPtr->state = STATE_NORMAL;
546     entryPtr->textVarName = NULL;
547     entryPtr->takeFocus = NULL;
548     entryPtr->prefWidth = 0;
549     entryPtr->scrollCmd = NULL;
550     entryPtr->numBytes = 0;
551     entryPtr->numChars = 0;
552     entryPtr->displayString = entryPtr->string;
553     entryPtr->numDisplayBytes = 0;
554     entryPtr->inset = XPAD;
555     entryPtr->textLayout = NULL;
556     entryPtr->layoutX = 0;
557     entryPtr->layoutY = 0;
558     entryPtr->leftX = 0;
559     entryPtr->leftIndex = 0;
560     entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
561     entryPtr->textGC = None;
562     entryPtr->selTextGC = None;
563     entryPtr->highlightGC = None;
564     entryPtr->avgWidth = 1;
565     entryPtr->flags = 0;
566     entryPtr->validateCmd = NULL;
567     entryPtr->validate = VALIDATE_NONE;
568     entryPtr->invalidCmd = NULL;
569    
570     Tk_SetClass(entryPtr->tkwin, "Entry");
571     TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
572     Tk_CreateEventHandler(entryPtr->tkwin,
573     ExposureMask|StructureNotifyMask|FocusChangeMask,
574     EntryEventProc, (ClientData) entryPtr);
575     Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
576     EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
577    
578     if ((Tk_InitOptions(interp, (char *) entryPtr, optionTable, tkwin)
579     != TCL_OK) ||
580     (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK)) {
581     Tk_DestroyWindow(entryPtr->tkwin);
582     return TCL_ERROR;
583     }
584    
585     Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC);
586     return TCL_OK;
587     }
588    
589     /*
590     *--------------------------------------------------------------
591     *
592     * EntryWidgetObjCmd --
593     *
594     * This procedure is invoked to process the Tcl command
595     * that corresponds to a widget managed by this module.
596     * See the user documentation for details on what it does.
597     *
598     * Results:
599     * A standard Tcl result.
600     *
601     * Side effects:
602     * See the user documentation.
603     *
604     *--------------------------------------------------------------
605     */
606    
607     static int
608     EntryWidgetObjCmd(clientData, interp, objc, objv)
609     ClientData clientData; /* Information about entry widget. */
610     Tcl_Interp *interp; /* Current interpreter. */
611     int objc; /* Number of arguments. */
612     Tcl_Obj *CONST objv[]; /* Argument objects. */
613     {
614     Entry *entryPtr = (Entry *) clientData;
615     int cmdIndex, selIndex, result;
616     Tcl_Obj *objPtr;
617    
618     if (objc < 2) {
619     Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
620     return TCL_ERROR;
621     }
622     Tcl_Preserve((ClientData) entryPtr);
623    
624     /*
625     * Parse the widget command by looking up the second token in
626     * the list of valid command names.
627     */
628    
629     result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
630     "option", 0, &cmdIndex);
631     if (result != TCL_OK) {
632     return result;
633     }
634    
635     switch (cmdIndex) {
636     case COMMAND_BBOX: {
637     int index, x, y, width, height;
638     char buf[TCL_INTEGER_SPACE * 4];
639    
640     if (objc != 3) {
641     Tcl_WrongNumArgs(interp, 2, objv, "index");
642     goto error;
643     }
644     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
645     &index) != TCL_OK) {
646     goto error;
647     }
648     if ((index == entryPtr->numChars) && (index > 0)) {
649     index--;
650     }
651     Tk_CharBbox(entryPtr->textLayout, index, &x, &y,
652     &width, &height);
653     sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX,
654     y + entryPtr->layoutY, width, height);
655     Tcl_SetResult(interp, buf, TCL_VOLATILE);
656     break;
657     }
658    
659     case COMMAND_CGET: {
660     if (objc != 3) {
661     Tcl_WrongNumArgs(interp, 2, objv, "option");
662     goto error;
663     }
664    
665     objPtr = Tk_GetOptionValue(interp, (char *) entryPtr,
666     entryPtr->optionTable, objv[2], entryPtr->tkwin);
667     if (objPtr == NULL) {
668     goto error;
669     } else {
670     Tcl_SetObjResult(interp, objPtr);
671     }
672     break;
673     }
674    
675     case COMMAND_CONFIGURE: {
676     if (objc <= 3) {
677     objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr,
678     entryPtr->optionTable,
679     (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
680     entryPtr->tkwin);
681     if (objPtr == NULL) {
682     goto error;
683     } else {
684     Tcl_SetObjResult(interp, objPtr);
685     }
686     } else {
687     result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0);
688     }
689     break;
690     }
691    
692     case COMMAND_DELETE: {
693     int first, last;
694    
695     if ((objc < 3) || (objc > 4)) {
696     Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
697     goto error;
698     }
699     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
700     &first) != TCL_OK) {
701     goto error;
702     }
703     if (objc == 3) {
704     last = first + 1;
705     } else {
706     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]),
707     &last) != TCL_OK) {
708     goto error;
709     }
710     }
711     if ((last >= first) && (entryPtr->state == STATE_NORMAL)) {
712     DeleteChars(entryPtr, first, last - first);
713     }
714     break;
715     }
716    
717     case COMMAND_GET: {
718     if (objc != 2) {
719     Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
720     goto error;
721     }
722     Tcl_SetResult(interp, entryPtr->string, TCL_STATIC);
723     break;
724     }
725    
726     case COMMAND_ICURSOR: {
727     if (objc != 3) {
728     Tcl_WrongNumArgs(interp, 2, objv, "pos");
729     goto error;
730     }
731     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
732     &entryPtr->insertPos) != TCL_OK) {
733     goto error;
734     }
735     EventuallyRedraw(entryPtr);
736     break;
737     }
738    
739     case COMMAND_INDEX: {
740     int index;
741    
742     if (objc != 3) {
743     Tcl_WrongNumArgs(interp, 2, objv, "string");
744     goto error;
745     }
746     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
747     &index) != TCL_OK) {
748     goto error;
749     }
750     Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
751     break;
752     }
753    
754     case COMMAND_INSERT: {
755     int index;
756    
757     if (objc != 4) {
758     Tcl_WrongNumArgs(interp, 2, objv, "index text");
759     goto error;
760     }
761     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
762     &index) != TCL_OK) {
763     goto error;
764     }
765     if (entryPtr->state == STATE_NORMAL) {
766     InsertChars(entryPtr, index, Tcl_GetString(objv[3]));
767     }
768     break;
769     }
770    
771     case COMMAND_SCAN: {
772     int x;
773     char *minorCmd;
774    
775     if (objc != 4) {
776     Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x");
777     goto error;
778     }
779     if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
780     goto error;
781     }
782    
783     minorCmd = Tcl_GetString(objv[2]);
784     if (minorCmd[0] == 'm'
785     && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) {
786     entryPtr->scanMarkX = x;
787     entryPtr->scanMarkIndex = entryPtr->leftIndex;
788     } else if ((minorCmd[0] == 'd')
789     && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) {
790     EntryScanTo(entryPtr, x);
791     } else {
792     Tcl_AppendResult(interp, "bad scan option \"",
793     Tcl_GetString(objv[2]), "\": must be mark or dragto",
794     (char *) NULL);
795     goto error;
796     }
797     break;
798     }
799    
800     case COMMAND_SELECTION: {
801     int index, index2;
802    
803     if (objc < 3) {
804     Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
805     goto error;
806     }
807    
808     /*
809     * Parse the selection sub-command, using the command
810     * table "selCommandNames" defined above.
811     */
812    
813     result = Tcl_GetIndexFromObj(interp, objv[2], selCommandNames,
814     "selection option", 0, &selIndex);
815     if (result != TCL_OK) {
816     goto error;
817     }
818    
819     switch(selIndex) {
820     case SELECTION_ADJUST: {
821     if (objc != 4) {
822     Tcl_WrongNumArgs(interp, 3, objv, "index");
823     goto error;
824     }
825     if (GetEntryIndex(interp, entryPtr,
826     Tcl_GetString(objv[3]), &index) != TCL_OK) {
827     goto error;
828     }
829     if (entryPtr->selectFirst >= 0) {
830     int half1, half2;
831    
832     half1 = (entryPtr->selectFirst
833     + entryPtr->selectLast)/2;
834     half2 = (entryPtr->selectFirst
835     + entryPtr->selectLast + 1)/2;
836     if (index < half1) {
837     entryPtr->selectAnchor = entryPtr->selectLast;
838     } else if (index > half2) {
839     entryPtr->selectAnchor = entryPtr->selectFirst;
840     } else {
841     /*
842     * We're at about the halfway point in the
843     * selection; just keep the existing anchor.
844     */
845     }
846     }
847     EntrySelectTo(entryPtr, index);
848     break;
849     }
850    
851     case SELECTION_CLEAR: {
852     if (objc != 3) {
853     Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
854     goto error;
855     }
856     if (entryPtr->selectFirst >= 0) {
857     entryPtr->selectFirst = -1;
858     entryPtr->selectLast = -1;
859     EventuallyRedraw(entryPtr);
860     }
861     goto done;
862     }
863    
864     case SELECTION_FROM: {
865     if (objc != 4) {
866     Tcl_WrongNumArgs(interp, 3, objv, "index");
867     goto error;
868     }
869     if (GetEntryIndex(interp, entryPtr,
870     Tcl_GetString(objv[3]), &index) != TCL_OK) {
871     goto error;
872     }
873     entryPtr->selectAnchor = index;
874     break;
875     }
876    
877     case SELECTION_PRESENT: {
878     if (objc != 3) {
879     Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
880     goto error;
881     }
882     if (entryPtr->selectFirst < 0) {
883     Tcl_SetResult(interp, "0", TCL_STATIC);
884     } else {
885     Tcl_SetResult(interp, "1", TCL_STATIC);
886     }
887     goto done;
888     }
889    
890     case SELECTION_RANGE: {
891     if (objc != 5) {
892     Tcl_WrongNumArgs(interp, 3, objv, "start end");
893     goto error;
894     }
895     if (GetEntryIndex(interp, entryPtr,
896     Tcl_GetString(objv[3]), &index) != TCL_OK) {
897     goto error;
898     }
899     if (GetEntryIndex(interp, entryPtr,
900     Tcl_GetString(objv[4]),& index2) != TCL_OK) {
901     goto error;
902     }
903     if (index >= index2) {
904     entryPtr->selectFirst = -1;
905     entryPtr->selectLast = -1;
906     } else {
907     entryPtr->selectFirst = index;
908     entryPtr->selectLast = index2;
909     }
910     if (!(entryPtr->flags & GOT_SELECTION)
911     && (entryPtr->exportSelection)) {
912     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
913     EntryLostSelection, (ClientData) entryPtr);
914     entryPtr->flags |= GOT_SELECTION;
915     }
916     EventuallyRedraw(entryPtr);
917     break;
918     }
919    
920     case SELECTION_TO: {
921     if (objc != 4) {
922     Tcl_WrongNumArgs(interp, 3, objv, "index");
923     goto error;
924     }
925     if (GetEntryIndex(interp, entryPtr,
926     Tcl_GetString(objv[3]), &index) != TCL_OK) {
927     goto error;
928     }
929     EntrySelectTo(entryPtr, index);
930     break;
931     }
932     }
933     break;
934     }
935    
936     case COMMAND_VALIDATE: {
937     int code;
938    
939     if (objc != 2) {
940     Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
941     goto error;
942     }
943     selIndex = entryPtr->validate;
944     entryPtr->validate = VALIDATE_ALL;
945     code = EntryValidateChange(entryPtr, (char *) NULL,
946     entryPtr->string, -1, VALIDATE_FORCED);
947     if (entryPtr->validate != VALIDATE_NONE) {
948     entryPtr->validate = selIndex;
949     }
950     Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK)));
951     break;
952     }
953    
954     case COMMAND_XVIEW: {
955     int index;
956    
957     if (objc == 2) {
958     double first, last;
959     char buf[TCL_DOUBLE_SPACE * 2];
960    
961     EntryVisibleRange(entryPtr, &first, &last);
962     sprintf(buf, "%g %g", first, last);
963     Tcl_SetResult(interp, buf, TCL_VOLATILE);
964     goto done;
965     } else if (objc == 3) {
966     if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
967     &index) != TCL_OK) {
968     goto error;
969     }
970     } else {
971     double fraction;
972     int count;
973    
974     index = entryPtr->leftIndex;
975     switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction,
976     &count)) {
977     case TK_SCROLL_ERROR: {
978     goto error;
979     }
980     case TK_SCROLL_MOVETO: {
981     index = (int) ((fraction * entryPtr->numChars) + 0.5);
982     break;
983     }
984     case TK_SCROLL_PAGES: {
985     int charsPerPage;
986    
987     charsPerPage = ((Tk_Width(entryPtr->tkwin)
988     - 2 * entryPtr->inset)
989     / entryPtr->avgWidth) - 2;
990     if (charsPerPage < 1) {
991     charsPerPage = 1;
992     }
993     index += count * charsPerPage;
994     break;
995     }
996     case TK_SCROLL_UNITS: {
997     index += count;
998     break;
999     }
1000     }
1001     }
1002     if (index >= entryPtr->numChars) {
1003     index = entryPtr->numChars - 1;
1004     }
1005     if (index < 0) {
1006     index = 0;
1007     }
1008     entryPtr->leftIndex = index;
1009     entryPtr->flags |= UPDATE_SCROLLBAR;
1010     EntryComputeGeometry(entryPtr);
1011     EventuallyRedraw(entryPtr);
1012     break;
1013     }
1014     }
1015    
1016     done:
1017     Tcl_Release((ClientData) entryPtr);
1018     return result;
1019    
1020     error:
1021     Tcl_Release((ClientData) entryPtr);
1022     return TCL_ERROR;
1023     }
1024    
1025     /*
1026     *----------------------------------------------------------------------
1027     *
1028     * DestroyEntry --
1029     *
1030     * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1031     * to clean up the internal structure of an entry at a safe time
1032     * (when no-one is using it anymore).
1033     *
1034     * Results:
1035     * None.
1036     *
1037     * Side effects:
1038     * Everything associated with the entry is freed up.
1039     *
1040     *----------------------------------------------------------------------
1041     */
1042    
1043     static void
1044     DestroyEntry(memPtr)
1045     char *memPtr; /* Info about entry widget. */
1046     {
1047     Entry *entryPtr = (Entry *) memPtr;
1048     entryPtr->flags |= ENTRY_DELETED;
1049    
1050     Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
1051     if (entryPtr->flags & REDRAW_PENDING) {
1052     Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1053     }
1054    
1055     /*
1056     * Free up all the stuff that requires special handling, then
1057     * let Tk_FreeOptions handle all the standard option-related
1058     * stuff.
1059     */
1060    
1061     ckfree(entryPtr->string);
1062     if (entryPtr->textVarName != NULL) {
1063     Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
1064     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1065     EntryTextVarProc, (ClientData) entryPtr);
1066     }
1067     if (entryPtr->textGC != None) {
1068     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1069     }
1070     if (entryPtr->selTextGC != None) {
1071     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1072     }
1073     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
1074     if (entryPtr->displayString != entryPtr->string) {
1075     ckfree(entryPtr->displayString);
1076     }
1077     Tk_FreeTextLayout(entryPtr->textLayout);
1078     Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable,
1079     entryPtr->tkwin);
1080     entryPtr->tkwin = NULL;
1081     ckfree((char *) entryPtr);
1082     }
1083    
1084     /*
1085     *----------------------------------------------------------------------
1086     *
1087     * ConfigureEntry --
1088     *
1089     * This procedure is called to process an argv/argc list, plus
1090     * the Tk option database, in order to configure (or reconfigure)
1091     * an entry widget.
1092     *
1093     * Results:
1094     * The return value is a standard Tcl result. If TCL_ERROR is
1095     * returned, then the interp's result contains an error message.
1096     *
1097     * Side effects:
1098     * Configuration information, such as colors, border width,
1099     * etc. get set for entryPtr; old resources get freed,
1100     * if there were any.
1101     *
1102     *----------------------------------------------------------------------
1103     */
1104    
1105     static int
1106     ConfigureEntry(interp, entryPtr, objc, objv, flags)
1107     Tcl_Interp *interp; /* Used for error reporting. */
1108     Entry *entryPtr; /* Information about widget; may or may not
1109     * already have values for some fields. */
1110     int objc; /* Number of valid entries in argv. */
1111     Tcl_Obj *CONST objv[]; /* Argument objects. */
1112     int flags; /* Flags to pass to Tk_ConfigureWidget. */
1113     {
1114     Tk_SavedOptions savedOptions;
1115     Tcl_Obj *errorResult = NULL;
1116     int error;
1117     int oldExport;
1118    
1119     /*
1120     * Eliminate any existing trace on a variable monitored by the entry.
1121     */
1122    
1123     if (entryPtr->textVarName != NULL) {
1124     Tcl_UntraceVar(interp, entryPtr->textVarName,
1125     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1126     EntryTextVarProc, (ClientData) entryPtr);
1127     }
1128    
1129     oldExport = entryPtr->exportSelection;
1130    
1131     for (error = 0; error <= 1; error++) {
1132     if (!error) {
1133     /*
1134     * First pass: set options to new values.
1135     */
1136    
1137     if (Tk_SetOptions(interp, (char *) entryPtr,
1138     entryPtr->optionTable, objc, objv,
1139     entryPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {
1140     continue;
1141     }
1142     } else {
1143     /*
1144     * Second pass: restore options to old values.
1145     */
1146    
1147     errorResult = Tcl_GetObjResult(interp);
1148     Tcl_IncrRefCount(errorResult);
1149     Tk_RestoreSavedOptions(&savedOptions);
1150     }
1151    
1152     /*
1153     * A few other options also need special processing, such as parsing
1154     * the geometry and setting the background from a 3-D border.
1155     */
1156    
1157     Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
1158    
1159     if (entryPtr->insertWidth <= 0) {
1160     entryPtr->insertWidth = 2;
1161     }
1162     if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
1163     entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
1164     }
1165    
1166     /*
1167     * Restart the cursor timing sequence in case the on-time or
1168     * off-time just changed. Set validate temporarily to none,
1169     * so the configure doesn't cause it to be triggered.
1170     */
1171    
1172     if (entryPtr->flags & GOT_FOCUS) {
1173     int validate = entryPtr->validate;
1174     entryPtr->validate = VALIDATE_NONE;
1175     EntryFocusProc(entryPtr, 1);
1176     entryPtr->validate = validate;
1177     }
1178    
1179     /*
1180     * Claim the selection if we've suddenly started exporting it.
1181     */
1182    
1183     if (entryPtr->exportSelection && (!oldExport)
1184     && (entryPtr->selectFirst != -1)
1185     && !(entryPtr->flags & GOT_SELECTION)) {
1186     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
1187     (ClientData) entryPtr);
1188     entryPtr->flags |= GOT_SELECTION;
1189     }
1190    
1191     /*
1192     * Recompute the window's geometry and arrange for it to be
1193     * redisplayed.
1194     */
1195    
1196     Tk_SetInternalBorder(entryPtr->tkwin,
1197     entryPtr->borderWidth + entryPtr->highlightWidth);
1198     if (entryPtr->highlightWidth <= 0) {
1199     entryPtr->highlightWidth = 0;
1200     }
1201     entryPtr->inset = entryPtr->highlightWidth
1202     + entryPtr->borderWidth + XPAD;
1203     break;
1204     }
1205     if (!error) {
1206     Tk_FreeSavedOptions(&savedOptions);
1207     }
1208    
1209     /*
1210     * If the entry is tied to the value of a variable, then set up
1211     * a trace on the variable's value, create the variable if it doesn't
1212     * exist, and set the entry's value from the variable's value.
1213     */
1214    
1215     if (entryPtr->textVarName != NULL) {
1216     char *value;
1217    
1218     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
1219     if (value == NULL) {
1220     EntryValueChanged(entryPtr);
1221     } else {
1222     EntrySetValue(entryPtr, value);
1223     }
1224     Tcl_TraceVar(interp, entryPtr->textVarName,
1225     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1226     EntryTextVarProc, (ClientData) entryPtr);
1227     }
1228    
1229     EntryWorldChanged((ClientData) entryPtr);
1230     if (error) {
1231     Tcl_SetObjResult(interp, errorResult);
1232     Tcl_DecrRefCount(errorResult);
1233     return TCL_ERROR;
1234     } else {
1235     return TCL_OK;
1236     }
1237     }
1238    
1239     /*
1240     *---------------------------------------------------------------------------
1241     *
1242     * EntryWorldChanged --
1243     *
1244     * This procedure is called when the world has changed in some
1245     * way and the widget needs to recompute all its graphics contexts
1246     * and determine its new geometry.
1247     *
1248     * Results:
1249     * None.
1250     *
1251     * Side effects:
1252     * Entry will be relayed out and redisplayed.
1253     *
1254     *---------------------------------------------------------------------------
1255     */
1256    
1257     static void
1258     EntryWorldChanged(instanceData)
1259     ClientData instanceData; /* Information about widget. */
1260     {
1261     XGCValues gcValues;
1262     GC gc = None;
1263     unsigned long mask;
1264     Entry *entryPtr;
1265    
1266     entryPtr = (Entry *) instanceData;
1267    
1268     entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
1269     if (entryPtr->avgWidth == 0) {
1270     entryPtr->avgWidth = 1;
1271     }
1272    
1273     if (entryPtr->normalBorder != NULL) {
1274     Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
1275     }
1276    
1277     gcValues.foreground = entryPtr->fgColorPtr->pixel;
1278     gcValues.font = Tk_FontId(entryPtr->tkfont);
1279     gcValues.graphics_exposures = False;
1280     mask = GCForeground | GCFont | GCGraphicsExposures;
1281     gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1282     if (entryPtr->textGC != None) {
1283     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1284     }
1285     entryPtr->textGC = gc;
1286    
1287     gcValues.foreground = entryPtr->selFgColorPtr->pixel;
1288     gcValues.font = Tk_FontId(entryPtr->tkfont);
1289     mask = GCForeground | GCFont;
1290     gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1291     if (entryPtr->selTextGC != None) {
1292     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1293     }
1294     entryPtr->selTextGC = gc;
1295    
1296     /*
1297     * Recompute the window's geometry and arrange for it to be
1298     * redisplayed.
1299     */
1300    
1301     EntryComputeGeometry(entryPtr);
1302     entryPtr->flags |= UPDATE_SCROLLBAR;
1303     EventuallyRedraw(entryPtr);
1304     }
1305    
1306     /*
1307     *--------------------------------------------------------------
1308     *
1309     * DisplayEntry --
1310     *
1311     * This procedure redraws the contents of an entry window.
1312     *
1313     * Results:
1314     * None.
1315     *
1316     * Side effects:
1317     * Information appears on the screen.
1318     *
1319     *--------------------------------------------------------------
1320     */
1321    
1322     static void
1323     DisplayEntry(clientData)
1324     ClientData clientData; /* Information about window. */
1325     {
1326     Entry *entryPtr = (Entry *) clientData;
1327     Tk_Window tkwin = entryPtr->tkwin;
1328     int baseY, selStartX, selEndX, cursorX;
1329     int xBound;
1330     Tk_FontMetrics fm;
1331     Pixmap pixmap;
1332     int showSelection;
1333    
1334     entryPtr->flags &= ~REDRAW_PENDING;
1335     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1336     return;
1337     }
1338    
1339     Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1340    
1341     /*
1342     * Update the scrollbar if that's needed.
1343     */
1344    
1345     if (entryPtr->flags & UPDATE_SCROLLBAR) {
1346     entryPtr->flags &= ~UPDATE_SCROLLBAR;
1347     EntryUpdateScrollbar(entryPtr);
1348     }
1349    
1350     /*
1351     * In order to avoid screen flashes, this procedure redraws the
1352     * textual area of the entry into off-screen memory, then copies
1353     * it back on-screen in a single operation. This means there's
1354     * no point in time where the on-screen image has been cleared.
1355     */
1356    
1357     pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
1358     Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1359    
1360     /*
1361     * Compute x-coordinate of the pixel just after last visible
1362     * one, plus vertical position of baseline of text.
1363     */
1364    
1365     xBound = Tk_Width(tkwin) - entryPtr->inset;
1366     baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
1367    
1368     /*
1369     * On Windows and Mac, we need to hide the selection whenever we
1370     * don't have the focus.
1371     */
1372    
1373     #ifdef ALWAYS_SHOW_SELECTION
1374     showSelection = 1;
1375     #else
1376     showSelection = (entryPtr->flags & GOT_FOCUS);
1377     #endif
1378    
1379     /*
1380     * Draw the background in three layers. From bottom to top the
1381     * layers are: normal background, selection background, and
1382     * insertion cursor background.
1383     */
1384    
1385     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1386     0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1387    
1388     if (showSelection
1389     && (entryPtr->selectLast > entryPtr->leftIndex)) {
1390     if (entryPtr->selectFirst <= entryPtr->leftIndex) {
1391     selStartX = entryPtr->leftX;
1392     } else {
1393     Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,
1394     &selStartX, NULL, NULL, NULL);
1395     selStartX += entryPtr->layoutX;
1396     }
1397     if ((selStartX - entryPtr->selBorderWidth) < xBound) {
1398     Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast,
1399     &selEndX, NULL, NULL, NULL);
1400     selEndX += entryPtr->layoutX;
1401     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
1402     selStartX - entryPtr->selBorderWidth,
1403     baseY - fm.ascent - entryPtr->selBorderWidth,
1404     (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
1405     (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,
1406     entryPtr->selBorderWidth, TK_RELIEF_RAISED);
1407     }
1408     }
1409    
1410     /*
1411     * Draw a special background for the insertion cursor, overriding
1412     * even the selection background. As a special hack to keep the
1413     * cursor visible when the insertion cursor color is the same as
1414     * the color for selected text (e.g., on mono displays), write
1415     * background in the cursor area (instead of nothing) when the
1416     * cursor isn't on. Otherwise the selection would hide the cursor.
1417     */
1418    
1419     if ((entryPtr->insertPos >= entryPtr->leftIndex)
1420     && (entryPtr->state == STATE_NORMAL)
1421     && (entryPtr->flags & GOT_FOCUS)) {
1422     Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos, &cursorX, NULL,
1423     NULL, NULL);
1424     cursorX += entryPtr->layoutX;
1425     cursorX -= (entryPtr->insertWidth)/2;
1426     if (cursorX < xBound) {
1427     if (entryPtr->flags & CURSOR_ON) {
1428     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
1429     cursorX, baseY - fm.ascent, entryPtr->insertWidth,
1430     fm.ascent + fm.descent, entryPtr->insertBorderWidth,
1431     TK_RELIEF_RAISED);
1432     } else if (entryPtr->insertBorder == entryPtr->selBorder) {
1433     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1434     cursorX, baseY - fm.ascent, entryPtr->insertWidth,
1435     fm.ascent + fm.descent, 0, TK_RELIEF_FLAT);
1436     }
1437     }
1438     }
1439    
1440     /*
1441     * Draw the text in two pieces: first the unselected portion, then the
1442     * selected portion on top of it.
1443     */
1444    
1445     Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
1446     entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1447     entryPtr->leftIndex, entryPtr->numChars);
1448    
1449     if (showSelection
1450     && (entryPtr->selTextGC != entryPtr->textGC)
1451     && (entryPtr->selectFirst < entryPtr->selectLast)) {
1452     int selFirst;
1453    
1454     if (entryPtr->selectFirst < entryPtr->leftIndex) {
1455     selFirst = entryPtr->leftIndex;
1456     } else {
1457     selFirst = entryPtr->selectFirst;
1458     }
1459     Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
1460     entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1461     selFirst, entryPtr->selectLast);
1462     }
1463    
1464     /*
1465     * Draw the border and focus highlight last, so they will overwrite
1466     * any text that extends past the viewable part of the window.
1467     */
1468    
1469     if (entryPtr->relief != TK_RELIEF_FLAT) {
1470     Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1471     entryPtr->highlightWidth, entryPtr->highlightWidth,
1472     Tk_Width(tkwin) - 2 * entryPtr->highlightWidth,
1473     Tk_Height(tkwin) - 2 * entryPtr->highlightWidth,
1474     entryPtr->borderWidth, entryPtr->relief);
1475     }
1476     if (entryPtr->highlightWidth != 0) {
1477     GC fgGC, bgGC;
1478    
1479     bgGC = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
1480     if (entryPtr->flags & GOT_FOCUS) {
1481     fgGC = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
1482     TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
1483     entryPtr->highlightWidth, pixmap);
1484     } else {
1485     TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
1486     entryPtr->highlightWidth, pixmap);
1487     }
1488     }
1489    
1490     /*
1491     * Everything's been redisplayed; now copy the pixmap onto the screen
1492     * and free up the pixmap.
1493     */
1494    
1495     XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
1496     0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
1497     0, 0);
1498     Tk_FreePixmap(entryPtr->display, pixmap);
1499     entryPtr->flags &= ~BORDER_NEEDED;
1500     }
1501    
1502     /*
1503     *----------------------------------------------------------------------
1504     *
1505     * EntryComputeGeometry --
1506     *
1507     * This procedure is invoked to recompute information about where
1508     * in its window an entry's string will be displayed. It also
1509     * computes the requested size for the window.
1510     *
1511     * Results:
1512     * None.
1513     *
1514     * Side effects:
1515     * The leftX and tabOrigin fields are recomputed for entryPtr,
1516     * and leftIndex may be adjusted. Tk_GeometryRequest is called
1517     * to register the desired dimensions for the window.
1518     *
1519     *----------------------------------------------------------------------
1520     */
1521    
1522     static void
1523     EntryComputeGeometry(entryPtr)
1524     Entry *entryPtr; /* Widget record for entry. */
1525     {
1526     int totalLength, overflow, maxOffScreen, rightX;
1527     int height, width, i;
1528     Tk_FontMetrics fm;
1529     char *p;
1530    
1531     if (entryPtr->displayString != entryPtr->string) {
1532     ckfree(entryPtr->displayString);
1533     entryPtr->displayString = entryPtr->string;
1534     entryPtr->numDisplayBytes = entryPtr->numBytes;
1535     }
1536    
1537     /*
1538     * If we're displaying a special character instead of the value of
1539     * the entry, recompute the displayString.
1540     */
1541    
1542     if (entryPtr->showChar != NULL) {
1543     Tcl_UniChar ch;
1544     char buf[TCL_UTF_MAX];
1545     int size;
1546    
1547     /*
1548     * Normalize the special character so we can safely duplicate it
1549     * in the display string. If we didn't do this, then two malformed
1550     * characters might end up looking like one valid UTF character in
1551     * the resulting string.
1552     */
1553    
1554     Tcl_UtfToUniChar(entryPtr->showChar, &ch);
1555     size = Tcl_UniCharToUtf(ch, buf);
1556    
1557     entryPtr->numDisplayBytes = entryPtr->numChars * size;
1558     entryPtr->displayString =
1559     (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1));
1560    
1561     p = entryPtr->displayString;
1562     for (i = entryPtr->numChars; --i >= 0; ) {
1563     p += Tcl_UniCharToUtf(ch, p);
1564     }
1565     *p = '\0';
1566     }
1567     Tk_FreeTextLayout(entryPtr->textLayout);
1568     entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
1569     entryPtr->displayString, entryPtr->numChars, 0,
1570     entryPtr->justify, TK_IGNORE_NEWLINES, &totalLength, &height);
1571    
1572     entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
1573    
1574     /*
1575     * Recompute where the leftmost character on the display will
1576     * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
1577     * so that we don't let characters hang off the edge of the
1578     * window unless the entire window is full.
1579     */
1580    
1581     overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
1582     if (overflow <= 0) {
1583     entryPtr->leftIndex = 0;
1584     if (entryPtr->justify == TK_JUSTIFY_LEFT) {
1585     entryPtr->leftX = entryPtr->inset;
1586     } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
1587     entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
1588     - totalLength;
1589     } else {
1590     entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
1591     }
1592     entryPtr->layoutX = entryPtr->leftX;
1593     } else {
1594     /*
1595     * The whole string can't fit in the window. Compute the
1596     * maximum number of characters that may be off-screen to
1597     * the left without leaving empty space on the right of the
1598     * window, then don't let leftIndex be any greater than that.
1599     */
1600    
1601     maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);
1602     Tk_CharBbox(entryPtr->textLayout, maxOffScreen,
1603     &rightX, NULL, NULL, NULL);
1604     if (rightX < overflow) {
1605     maxOffScreen++;
1606     }
1607     if (entryPtr->leftIndex > maxOffScreen) {
1608     entryPtr->leftIndex = maxOffScreen;
1609     }
1610     Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex, &rightX,
1611     NULL, NULL, NULL);
1612     entryPtr->leftX = entryPtr->inset;
1613     entryPtr->layoutX = entryPtr->leftX - rightX;
1614     }
1615    
1616     Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1617     height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);
1618     if (entryPtr->prefWidth > 0) {
1619     width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
1620     } else {
1621     if (totalLength == 0) {
1622     width = entryPtr->avgWidth + 2*entryPtr->inset;
1623     } else {
1624     width = totalLength + 2*entryPtr->inset;
1625     }
1626     }
1627     Tk_GeometryRequest(entryPtr->tkwin, width, height);
1628     }
1629    
1630     /*
1631     *----------------------------------------------------------------------
1632     *
1633     * InsertChars --
1634     *
1635     * Add new characters to an entry widget.
1636     *
1637     * Results:
1638     * None.
1639     *
1640     * Side effects:
1641     * New information gets added to entryPtr; it will be redisplayed
1642     * soon, but not necessarily immediately.
1643     *
1644     *----------------------------------------------------------------------
1645     */
1646    
1647     static void
1648     InsertChars(entryPtr, index, value)
1649     Entry *entryPtr; /* Entry that is to get the new elements. */
1650     int index; /* Add the new elements before this
1651     * character index. */
1652     char *value; /* New characters to add (NULL-terminated
1653     * string). */
1654     {
1655     int byteIndex, byteCount, oldChars, charsAdded, newByteCount;
1656     char *new, *string;
1657    
1658     string = entryPtr->string;
1659     byteIndex = Tcl_UtfAtIndex(string, index) - string;
1660     byteCount = strlen(value);
1661     if (byteCount == 0) {
1662     return;
1663     }
1664    
1665     newByteCount = entryPtr->numBytes + byteCount + 1;
1666     new = (char *) ckalloc((unsigned) newByteCount);
1667     memcpy(new, string, (size_t) byteIndex);
1668     strcpy(new + byteIndex, value);
1669     strcpy(new + byteIndex + byteCount, string + byteIndex);
1670    
1671     if ((entryPtr->validate == VALIDATE_KEY ||
1672     entryPtr->validate == VALIDATE_ALL) &&
1673     EntryValidateChange(entryPtr, value, new, index,
1674     VALIDATE_INSERT) != TCL_OK) {
1675     ckfree(new);
1676     return;
1677     }
1678    
1679     ckfree(string);
1680     entryPtr->string = new;
1681    
1682     /*
1683     * The following construction is used because inserting improperly
1684     * formed UTF-8 sequences between other improperly formed UTF-8
1685     * sequences could result in actually forming valid UTF-8 sequences;
1686     * the number of characters added may not be Tcl_NumUtfChars(string, -1),
1687     * because of context. The actual number of characters added is how
1688     * many characters are in the string now minus the number that
1689     * used to be there.
1690     */
1691    
1692     oldChars = entryPtr->numChars;
1693     entryPtr->numChars = Tcl_NumUtfChars(new, -1);
1694     charsAdded = entryPtr->numChars - oldChars;
1695     entryPtr->numBytes += byteCount;
1696    
1697     if (entryPtr->displayString == string) {
1698     entryPtr->displayString = new;
1699     entryPtr->numDisplayBytes = entryPtr->numBytes;
1700     }
1701    
1702     /*
1703     * Inserting characters invalidates all indexes into the string.
1704     * Touch up the indexes so that they still refer to the same
1705     * characters (at new positions). When updating the selection
1706     * end-points, don't include the new text in the selection unless
1707     * it was completely surrounded by the selection.
1708     */
1709    
1710     if (entryPtr->selectFirst >= index) {
1711     entryPtr->selectFirst += charsAdded;
1712     }
1713     if (entryPtr->selectLast > index) {
1714     entryPtr->selectLast += charsAdded;
1715     }
1716     if ((entryPtr->selectAnchor > index)
1717     || (entryPtr->selectFirst >= index)) {
1718     entryPtr->selectAnchor += charsAdded;
1719     }
1720     if (entryPtr->leftIndex > index) {
1721     entryPtr->leftIndex += charsAdded;
1722     }
1723     if (entryPtr->insertPos >= index) {
1724     entryPtr->insertPos += charsAdded;
1725     }
1726     EntryValueChanged(entryPtr);
1727     }
1728    
1729     /*
1730     *----------------------------------------------------------------------
1731     *
1732     * DeleteChars --
1733     *
1734     * Remove one or more characters from an entry widget.
1735     *
1736     * Results:
1737     * None.
1738     *
1739     * Side effects:
1740     * Memory gets freed, the entry gets modified and (eventually)
1741     * redisplayed.
1742     *
1743     *----------------------------------------------------------------------
1744     */
1745    
1746     static void
1747     DeleteChars(entryPtr, index, count)
1748     Entry *entryPtr; /* Entry widget to modify. */
1749     int index; /* Index of first character to delete. */
1750     int count; /* How many characters to delete. */
1751     {
1752     int byteIndex, byteCount, newByteCount;
1753     char *new, *string, *todelete;
1754    
1755     if ((index + count) > entryPtr->numChars) {
1756     count = entryPtr->numChars - index;
1757     }
1758     if (count <= 0) {
1759     return;
1760     }
1761    
1762     string = entryPtr->string;
1763     byteIndex = Tcl_UtfAtIndex(string, index) - string;
1764     byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string + byteIndex);
1765    
1766     newByteCount = entryPtr->numBytes + 1 - byteCount;
1767     new = (char *) ckalloc((unsigned) newByteCount);
1768     memcpy(new, string, (size_t) byteIndex);
1769     strcpy(new + byteIndex, string + byteIndex + byteCount);
1770    
1771     todelete = (char *) ckalloc((unsigned) (byteCount + 1));
1772     memcpy(todelete, string + byteIndex, (size_t) byteCount);
1773     todelete[byteCount] = '\0';
1774    
1775     if ((entryPtr->validate == VALIDATE_KEY ||
1776     entryPtr->validate == VALIDATE_ALL) &&
1777     EntryValidateChange(entryPtr, todelete, new, index,
1778     VALIDATE_DELETE) != TCL_OK) {
1779     ckfree(new);
1780     ckfree(todelete);
1781     return;
1782     }
1783    
1784     ckfree(todelete);
1785     ckfree(entryPtr->string);
1786     entryPtr->string = new;
1787     entryPtr->numChars -= count;
1788     entryPtr->numBytes -= byteCount;
1789    
1790     if (entryPtr->displayString == string) {
1791     entryPtr->displayString = new;
1792     entryPtr->numDisplayBytes = entryPtr->numBytes;
1793     }
1794    
1795     /*
1796     * Deleting characters results in the remaining characters being
1797     * renumbered. Update the various indexes into the string to reflect
1798     * this change.
1799     */
1800    
1801     if (entryPtr->selectFirst >= index) {
1802     if (entryPtr->selectFirst >= (index + count)) {
1803     entryPtr->selectFirst -= count;
1804     } else {
1805     entryPtr->selectFirst = index;
1806     }
1807     }
1808     if (entryPtr->selectLast >= index) {
1809     if (entryPtr->selectLast >= (index + count)) {
1810     entryPtr->selectLast -= count;
1811     } else {
1812     entryPtr->selectLast = index;
1813     }
1814     }
1815     if (entryPtr->selectLast <= entryPtr->selectFirst) {
1816     entryPtr->selectFirst = -1;
1817     entryPtr->selectLast = -1;
1818     }
1819     if (entryPtr->selectAnchor >= index) {
1820     if (entryPtr->selectAnchor >= (index+count)) {
1821     entryPtr->selectAnchor -= count;
1822     } else {
1823     entryPtr->selectAnchor = index;
1824     }
1825     }
1826     if (entryPtr->leftIndex > index) {
1827     if (entryPtr->leftIndex >= (index + count)) {
1828     entryPtr->leftIndex -= count;
1829     } else {
1830     entryPtr->leftIndex = index;
1831     }
1832     }
1833     if (entryPtr->insertPos >= index) {
1834     if (entryPtr->insertPos >= (index + count)) {
1835     entryPtr->insertPos -= count;
1836     } else {
1837     entryPtr->insertPos = index;
1838     }
1839     }
1840     EntryValueChanged(entryPtr);
1841     }
1842    
1843     /*
1844     *----------------------------------------------------------------------
1845     *
1846     * EntryValueChanged --
1847     *
1848     * This procedure is invoked when characters are inserted into
1849     * an entry or deleted from it. It updates the entry's associated
1850     * variable, if there is one, and does other bookkeeping such
1851     * as arranging for redisplay.
1852     *
1853     * Results:
1854     * None.
1855     *
1856     * Side effects:
1857     * None.
1858     *
1859     *----------------------------------------------------------------------
1860     */
1861    
1862     static void
1863     EntryValueChanged(entryPtr)
1864     Entry *entryPtr; /* Entry whose value just changed. */
1865     {
1866     char *newValue;
1867    
1868     if (entryPtr->textVarName == NULL) {
1869     newValue = NULL;
1870     } else {
1871     newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
1872     entryPtr->string, TCL_GLOBAL_ONLY);
1873     }
1874    
1875     if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
1876     /*
1877     * The value of the variable is different than what we asked for.
1878     * This means that a trace on the variable modified it. In this
1879     * case our trace procedure wasn't invoked since the modification
1880     * came while a trace was already active on the variable. So,
1881     * update our value to reflect the variable's latest value.
1882     */
1883    
1884     EntrySetValue(entryPtr, newValue);
1885     } else {
1886     /*
1887     * Arrange for redisplay.
1888     */
1889    
1890     entryPtr->flags |= UPDATE_SCROLLBAR;
1891     EntryComputeGeometry(entryPtr);
1892     EventuallyRedraw(entryPtr);
1893     }
1894     }
1895    
1896     /*
1897     *----------------------------------------------------------------------
1898     *
1899     * EntrySetValue --
1900     *
1901     * Replace the contents of a text entry with a given value. This
1902     * procedure is invoked when updating the entry from the entry's
1903     * associated variable.
1904     *
1905     * Results:
1906     * None.
1907     *
1908     * Side effects:
1909     * The string displayed in the entry will change. The selection,
1910     * insertion point, and view may have to be adjusted to keep them
1911     * within the bounds of the new string. Note: this procedure does
1912     * *not* update the entry's associated variable, since that could
1913     * result in an infinite loop.
1914     *
1915     *----------------------------------------------------------------------
1916     */
1917    
1918     static void
1919     EntrySetValue(entryPtr, value)
1920     Entry *entryPtr; /* Entry whose value is to be changed. */
1921     char *value; /* New text to display in entry. */
1922     {
1923     char *oldSource;
1924     int code, valueLen, malloced = 0;
1925    
1926     if (strcmp(value, entryPtr->string) == 0) {
1927     return;
1928     }
1929     valueLen = strlen(value);
1930    
1931     if (entryPtr->flags & VALIDATE_VAR) {
1932     entryPtr->flags |= VALIDATE_ABORT;
1933     } else {
1934     /*
1935     * If we validate, we create a copy of the value, as it may
1936     * point to volatile memory, like the value of the -textvar
1937     * which may get freed during validation
1938     */
1939     oldSource = (char *) ckalloc((unsigned) (valueLen + 1));
1940     strcpy(oldSource, value);
1941     value = oldSource;
1942     malloced = 1;
1943    
1944     entryPtr->flags |= VALIDATE_VAR;
1945     code = EntryValidateChange(entryPtr, (char *) NULL, value, -1,
1946     VALIDATE_FORCED);
1947     entryPtr->flags &= ~VALIDATE_VAR;
1948     /*
1949     * If VALIDATE_ABORT has been set, then this operation should be
1950     * aborted because the validatecommand did something else instead
1951     */
1952     if (entryPtr->flags & VALIDATE_ABORT) {
1953     entryPtr->flags &= ~VALIDATE_ABORT;
1954     ckfree(value);
1955     return;
1956     }
1957     }
1958    
1959     oldSource = entryPtr->string;
1960     ckfree(entryPtr->string);
1961    
1962     if (malloced) {
1963     entryPtr->string = value;
1964     } else {
1965     entryPtr->string = (char *) ckalloc((unsigned) (valueLen + 1));
1966     strcpy(entryPtr->string, value);
1967     }
1968     entryPtr->numBytes = valueLen;
1969     entryPtr->numChars = Tcl_NumUtfChars(value, valueLen);
1970    
1971     if (entryPtr->displayString == oldSource) {
1972     entryPtr->displayString = entryPtr->string;
1973     entryPtr->numDisplayBytes = entryPtr->numBytes;
1974     }
1975    
1976     if (entryPtr->selectFirst >= 0) {
1977     if (entryPtr->selectFirst >= entryPtr->numChars) {
1978     entryPtr->selectFirst = -1;
1979     entryPtr->selectLast = -1;
1980     } else if (entryPtr->selectLast > entryPtr->numChars) {
1981     entryPtr->selectLast = entryPtr->numChars;
1982     }
1983     }
1984     if (entryPtr->leftIndex >= entryPtr->numChars) {
1985     if (entryPtr->numChars > 0) {
1986     entryPtr->leftIndex = entryPtr->numChars - 1;
1987     } else {
1988     entryPtr->leftIndex = 0;
1989     }
1990     }
1991     if (entryPtr->insertPos > entryPtr->numChars) {
1992     entryPtr->insertPos = entryPtr->numChars;
1993     }
1994    
1995     entryPtr->flags |= UPDATE_SCROLLBAR;
1996     EntryComputeGeometry(entryPtr);
1997     EventuallyRedraw(entryPtr);
1998     }
1999    
2000     /*
2001     *--------------------------------------------------------------
2002     *
2003     * EntryEventProc --
2004     *
2005     * This procedure is invoked by the Tk dispatcher for various
2006     * events on entryes.
2007     *
2008     * Results:
2009     * None.
2010     *
2011     * Side effects:
2012     * When the window gets deleted, internal structures get
2013     * cleaned up. When it gets exposed, it is redisplayed.
2014     *
2015     *--------------------------------------------------------------
2016     */
2017    
2018     static void
2019     EntryEventProc(clientData, eventPtr)
2020     ClientData clientData; /* Information about window. */
2021     XEvent *eventPtr; /* Information about event. */
2022     {
2023     Entry *entryPtr = (Entry *) clientData;
2024     if (eventPtr->type == Expose) {
2025     EventuallyRedraw(entryPtr);
2026     entryPtr->flags |= BORDER_NEEDED;
2027     } else if (eventPtr->type == DestroyNotify) {
2028     DestroyEntry((char *) clientData);
2029     } else if (eventPtr->type == ConfigureNotify) {
2030     Tcl_Preserve((ClientData) entryPtr);
2031     entryPtr->flags |= UPDATE_SCROLLBAR;
2032     EntryComputeGeometry(entryPtr);
2033     EventuallyRedraw(entryPtr);
2034     Tcl_Release((ClientData) entryPtr);
2035     } else if (eventPtr->type == FocusIn) {
2036     if (eventPtr->xfocus.detail != NotifyInferior) {
2037     EntryFocusProc(entryPtr, 1);
2038     }
2039     } else if (eventPtr->type == FocusOut) {
2040     if (eventPtr->xfocus.detail != NotifyInferior) {
2041     EntryFocusProc(entryPtr, 0);
2042     }
2043     }
2044     }
2045    
2046     /*
2047     *----------------------------------------------------------------------
2048     *
2049     * EntryCmdDeletedProc --
2050     *
2051     * This procedure is invoked when a widget command is deleted. If
2052     * the widget isn't already in the process of being destroyed,
2053     * this command destroys it.
2054     *
2055     * Results:
2056     * None.
2057     *
2058     * Side effects:
2059     * The widget is destroyed.
2060     *
2061     *----------------------------------------------------------------------
2062     */
2063    
2064     static void
2065     EntryCmdDeletedProc(clientData)
2066     ClientData clientData; /* Pointer to widget record for widget. */
2067     {
2068     Entry *entryPtr = (Entry *) clientData;
2069    
2070     /*
2071     * This procedure could be invoked either because the window was
2072     * destroyed and the command was then deleted (in which case tkwin
2073     * is NULL) or because the command was deleted, and then this procedure
2074     * destroys the widget.
2075     */
2076    
2077     if (!(entryPtr->flags & ENTRY_DELETED)) {
2078     Tk_DestroyWindow(entryPtr->tkwin);
2079     }
2080     }
2081    
2082     /*
2083     *---------------------------------------------------------------------------
2084     *
2085     * GetEntryIndex --
2086     *
2087     * Parse an index into an entry and return either its value
2088     * or an error.
2089     *
2090     * Results:
2091     * A standard Tcl result. If all went well, then *indexPtr is
2092     * filled in with the character index (into entryPtr) corresponding to
2093     * string. The index value is guaranteed to lie between 0 and
2094     * the number of characters in the string, inclusive. If an
2095     * error occurs then an error message is left in the interp's result.
2096     *
2097     * Side effects:
2098     * None.
2099     *
2100     *---------------------------------------------------------------------------
2101     */
2102    
2103     static int
2104     GetEntryIndex(interp, entryPtr, string, indexPtr)
2105     Tcl_Interp *interp; /* For error messages. */
2106     Entry *entryPtr; /* Entry for which the index is being
2107     * specified. */
2108     char *string; /* Specifies character in entryPtr. */
2109     int *indexPtr; /* Where to store converted character
2110     * index. */
2111     {
2112     size_t length;
2113    
2114     length = strlen(string);
2115    
2116     if (string[0] == 'a') {
2117     if (strncmp(string, "anchor", length) == 0) {
2118     *indexPtr = entryPtr->selectAnchor;
2119     } else {
2120     badIndex:
2121    
2122     /*
2123     * Some of the paths here leave messages in the interp's result,
2124     * so we have to clear it out before storing our own message.
2125     */
2126    
2127     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2128     Tcl_AppendResult(interp, "bad entry index \"", string,
2129     "\"", (char *) NULL);
2130     return TCL_ERROR;
2131     }
2132     } else if (string[0] == 'e') {
2133     if (strncmp(string, "end", length) == 0) {
2134     *indexPtr = entryPtr->numChars;
2135     } else {
2136     goto badIndex;
2137     }
2138     } else if (string[0] == 'i') {
2139     if (strncmp(string, "insert", length) == 0) {
2140     *indexPtr = entryPtr->insertPos;
2141     } else {
2142     goto badIndex;
2143     }
2144     } else if (string[0] == 's') {
2145     if (entryPtr->selectFirst < 0) {
2146     Tcl_SetResult(interp, "selection isn't in entry", TCL_STATIC);
2147     return TCL_ERROR;
2148     }
2149     if (length < 5) {
2150     goto badIndex;
2151     }
2152     if (strncmp(string, "sel.first", length) == 0) {
2153     *indexPtr = entryPtr->selectFirst;
2154     } else if (strncmp(string, "sel.last", length) == 0) {
2155     *indexPtr = entryPtr->selectLast;
2156     } else {
2157     goto badIndex;
2158     }
2159     } else if (string[0] == '@') {
2160     int x, roundUp;
2161    
2162     if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) {
2163     goto badIndex;
2164     }
2165     if (x < entryPtr->inset) {
2166     x = entryPtr->inset;
2167     }
2168     roundUp = 0;
2169     if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
2170     x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
2171     roundUp = 1;
2172     }
2173     *indexPtr = Tk_PointToChar(entryPtr->textLayout,
2174     x - entryPtr->layoutX, 0);
2175    
2176     /*
2177     * Special trick: if the x-position was off-screen to the right,
2178     * round the index up to refer to the character just after the
2179     * last visible one on the screen. This is needed to enable the
2180     * last character to be selected, for example.
2181     */
2182    
2183     if (roundUp && (*indexPtr < entryPtr->numChars)) {
2184     *indexPtr += 1;
2185     }
2186     } else {
2187     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
2188     goto badIndex;
2189     }
2190     if (*indexPtr < 0){
2191     *indexPtr = 0;
2192     } else if (*indexPtr > entryPtr->numChars) {
2193     *indexPtr = entryPtr->numChars;
2194     }
2195     }
2196     return TCL_OK;
2197     }
2198    
2199     /*
2200     *----------------------------------------------------------------------
2201     *
2202     * EntryScanTo --
2203     *
2204     * Given a y-coordinate (presumably of the curent mouse location)
2205     * drag the view in the window to implement the scan operation.
2206     *
2207     * Results:
2208     * None.
2209     *
2210     * Side effects:
2211     * The view in the window may change.
2212     *
2213     *----------------------------------------------------------------------
2214     */
2215    
2216     static void
2217     EntryScanTo(entryPtr, x)
2218     Entry *entryPtr; /* Information about widget. */
2219     int x; /* X-coordinate to use for scan operation. */
2220     {
2221     int newLeftIndex;
2222    
2223     /*
2224     * Compute new leftIndex for entry by amplifying the difference
2225     * between the current position and the place where the scan
2226     * started (the "mark" position). If we run off the left or right
2227     * side of the entry, then reset the mark point so that the current
2228     * position continues to correspond to the edge of the window.
2229     * This means that the picture will start dragging as soon as the
2230     * mouse reverses direction (without this reset, might have to slide
2231     * mouse a long ways back before the picture starts moving again).
2232     */
2233    
2234     newLeftIndex = entryPtr->scanMarkIndex
2235     - (10 * (x - entryPtr->scanMarkX)) / entryPtr->avgWidth;
2236     if (newLeftIndex >= entryPtr->numChars) {
2237     newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars - 1;
2238     entryPtr->scanMarkX = x;
2239     }
2240     if (newLeftIndex < 0) {
2241     newLeftIndex = entryPtr->scanMarkIndex = 0;
2242     entryPtr->scanMarkX = x;
2243     }
2244    
2245     if (newLeftIndex != entryPtr->leftIndex) {
2246     entryPtr->leftIndex = newLeftIndex;
2247     entryPtr->flags |= UPDATE_SCROLLBAR;
2248     EntryComputeGeometry(entryPtr);
2249     if (newLeftIndex != entryPtr->leftIndex) {
2250     entryPtr->scanMarkIndex = entryPtr->leftIndex;
2251     entryPtr->scanMarkX = x;
2252     }
2253     EventuallyRedraw(entryPtr);
2254     }
2255     }
2256    
2257     /*
2258     *----------------------------------------------------------------------
2259     *
2260     * EntrySelectTo --
2261     *
2262     * Modify the selection by moving its un-anchored end. This could
2263     * make the selection either larger or smaller.
2264     *
2265     * Results:
2266     * None.
2267     *
2268     * Side effects:
2269     * The selection changes.
2270     *
2271     *----------------------------------------------------------------------
2272     */
2273    
2274     static void
2275     EntrySelectTo(entryPtr, index)
2276     Entry *entryPtr; /* Information about widget. */
2277     int index; /* Character index of element that is to
2278     * become the "other" end of the selection. */
2279     {
2280     int newFirst, newLast;
2281    
2282     /*
2283     * Grab the selection if we don't own it already.
2284     */
2285    
2286     if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {
2287     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
2288     (ClientData) entryPtr);
2289     entryPtr->flags |= GOT_SELECTION;
2290     }
2291    
2292     /*
2293     * Pick new starting and ending points for the selection.
2294     */
2295    
2296     if (entryPtr->selectAnchor > entryPtr->numChars) {
2297     entryPtr->selectAnchor = entryPtr->numChars;
2298     }
2299     if (entryPtr->selectAnchor <= index) {
2300     newFirst = entryPtr->selectAnchor;
2301     newLast = index;
2302     } else {
2303     newFirst = index;
2304     newLast = entryPtr->selectAnchor;
2305     if (newLast < 0) {
2306     newFirst = newLast = -1;
2307     }
2308     }
2309     if ((entryPtr->selectFirst == newFirst)
2310     && (entryPtr->selectLast == newLast)) {
2311     return;
2312     }
2313     entryPtr->selectFirst = newFirst;
2314     entryPtr->selectLast = newLast;
2315     EventuallyRedraw(entryPtr);
2316     }
2317    
2318     /*
2319     *----------------------------------------------------------------------
2320     *
2321     * EntryFetchSelection --
2322     *
2323     * This procedure is called back by Tk when the selection is
2324     * requested by someone. It returns part or all of the selection
2325     * in a buffer provided by the caller.
2326     *
2327     * Results:
2328     * The return value is the number of non-NULL bytes stored
2329     * at buffer. Buffer is filled (or partially filled) with a
2330     * NULL-terminated string containing part or all of the selection,
2331     * as given by offset and maxBytes.
2332     *
2333     * Side effects:
2334     * None.
2335     *
2336     *----------------------------------------------------------------------
2337     */
2338    
2339     static int
2340     EntryFetchSelection(clientData, offset, buffer, maxBytes)
2341     ClientData clientData; /* Information about entry widget. */
2342     int offset; /* Byte offset within selection of first
2343     * character to be returned. */
2344     char *buffer; /* Location in which to place selection. */
2345     int maxBytes; /* Maximum number of bytes to place at
2346     * buffer, not including terminating NULL
2347     * character. */
2348     {
2349     Entry *entryPtr = (Entry *) clientData;
2350     int byteCount;
2351     char *string, *selStart, *selEnd;
2352    
2353     if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
2354     return -1;
2355     }
2356     string = entryPtr->displayString;
2357     selStart = Tcl_UtfAtIndex(string, entryPtr->selectFirst);
2358     selEnd = Tcl_UtfAtIndex(selStart,
2359     entryPtr->selectLast - entryPtr->selectFirst);
2360     byteCount = selEnd - selStart - offset;
2361     if (byteCount > maxBytes) {
2362     byteCount = maxBytes;
2363     }
2364     if (byteCount <= 0) {
2365     return 0;
2366     }
2367     memcpy(buffer, selStart + offset, (size_t) byteCount);
2368     buffer[byteCount] = '\0';
2369     return byteCount;
2370     }
2371    
2372     /*
2373     *----------------------------------------------------------------------
2374     *
2375     * EntryLostSelection --
2376     *
2377     * This procedure is called back by Tk when the selection is
2378     * grabbed away from an entry widget.
2379     *
2380     * Results:
2381     * None.
2382     *
2383     * Side effects:
2384     * The existing selection is unhighlighted, and the window is
2385     * marked as not containing a selection.
2386     *
2387     *----------------------------------------------------------------------
2388     */
2389    
2390     static void
2391     EntryLostSelection(clientData)
2392     ClientData clientData; /* Information about entry widget. */
2393     {
2394     Entry *entryPtr = (Entry *) clientData;
2395    
2396     entryPtr->flags &= ~GOT_SELECTION;
2397    
2398     /*
2399     * On Windows and Mac systems, we want to remember the selection
2400     * for the next time the focus enters the window. On Unix, we need
2401     * to clear the selection since it is always visible.
2402     */
2403    
2404     #ifdef ALWAYS_SHOW_SELECTION
2405     if ((entryPtr->selectFirst >= 0) && entryPtr->exportSelection) {
2406     entryPtr->selectFirst = -1;
2407     entryPtr->selectLast = -1;
2408     EventuallyRedraw(entryPtr);
2409     }
2410     #endif
2411     }
2412    
2413     /*
2414     *----------------------------------------------------------------------
2415     *
2416     * EventuallyRedraw --
2417     *
2418     * Ensure that an entry is eventually redrawn on the display.
2419     *
2420     * Results:
2421     * None.
2422     *
2423     * Side effects:
2424     * Information gets redisplayed. Right now we don't do selective
2425     * redisplays: the whole window will be redrawn. This doesn't
2426     * seem to hurt performance noticeably, but if it does then this
2427     * could be changed.
2428     *
2429     *----------------------------------------------------------------------
2430     */
2431    
2432     static void
2433     EventuallyRedraw(entryPtr)
2434     Entry *entryPtr; /* Information about widget. */
2435     {
2436     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
2437     return;
2438     }
2439    
2440     /*
2441     * Right now we don't do selective redisplays: the whole window
2442     * will be redrawn. This doesn't seem to hurt performance noticeably,
2443     * but if it does then this could be changed.
2444     */
2445    
2446     if (!(entryPtr->flags & REDRAW_PENDING)) {
2447     entryPtr->flags |= REDRAW_PENDING;
2448     Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
2449     }
2450     }
2451    
2452     /*
2453     *----------------------------------------------------------------------
2454     *
2455     * EntryVisibleRange --
2456     *
2457     * Return information about the range of the entry that is
2458     * currently visible.
2459     *
2460     * Results:
2461     * *firstPtr and *lastPtr are modified to hold fractions between
2462     * 0 and 1 identifying the range of characters visible in the
2463     * entry.
2464     *
2465     * Side effects:
2466     * None.
2467     *
2468     *----------------------------------------------------------------------
2469     */
2470    
2471     static void
2472     EntryVisibleRange(entryPtr, firstPtr, lastPtr)
2473     Entry *entryPtr; /* Information about widget. */
2474     double *firstPtr; /* Return position of first visible
2475     * character in widget. */
2476     double *lastPtr; /* Return position of char just after last
2477     * visible one. */
2478     {
2479     int charsInWindow;
2480    
2481     if (entryPtr->numChars == 0) {
2482     *firstPtr = 0.0;
2483     *lastPtr = 1.0;
2484     } else {
2485     charsInWindow = Tk_PointToChar(entryPtr->textLayout,
2486     Tk_Width(entryPtr->tkwin) - entryPtr->inset
2487     - entryPtr->layoutX - 1, 0);
2488     if (charsInWindow < entryPtr->numChars) {
2489     charsInWindow++;
2490     }
2491     charsInWindow -= entryPtr->leftIndex;
2492     if (charsInWindow == 0) {
2493     charsInWindow = 1;
2494     }
2495    
2496     *firstPtr = (double) entryPtr->leftIndex / entryPtr->numChars;
2497     *lastPtr = (double) (entryPtr->leftIndex + charsInWindow)
2498     / entryPtr->numChars;
2499     }
2500     }
2501    
2502     /*
2503     *----------------------------------------------------------------------
2504     *
2505     * EntryUpdateScrollbar --
2506     *
2507     * This procedure is invoked whenever information has changed in
2508     * an entry in a way that would invalidate a scrollbar display.
2509     * If there is an associated scrollbar, then this procedure updates
2510     * it by invoking a Tcl command.
2511     *
2512     * Results:
2513     * None.
2514     *
2515     * Side effects:
2516     * A Tcl command is invoked, and an additional command may be
2517     * invoked to process errors in the command.
2518     *
2519     *----------------------------------------------------------------------
2520     */
2521    
2522     static void
2523     EntryUpdateScrollbar(entryPtr)
2524     Entry *entryPtr; /* Information about widget. */
2525     {
2526     char args[TCL_DOUBLE_SPACE * 2];
2527     int code;
2528     double first, last;
2529     Tcl_Interp *interp;
2530    
2531     if (entryPtr->scrollCmd == NULL) {
2532     return;
2533     }
2534    
2535     interp = entryPtr->interp;
2536     Tcl_Preserve((ClientData) interp);
2537     EntryVisibleRange(entryPtr, &first, &last);
2538     sprintf(args, " %g %g", first, last);
2539     code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
2540     if (code != TCL_OK) {
2541     Tcl_AddErrorInfo(interp,
2542     "\n (horizontal scrolling command executed by entry)");
2543     Tcl_BackgroundError(interp);
2544     }
2545     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2546     Tcl_Release((ClientData) interp);
2547     }
2548    
2549     /*
2550     *----------------------------------------------------------------------
2551     *
2552     * EntryBlinkProc --
2553     *
2554     * This procedure is called as a timer handler to blink the
2555     * insertion cursor off and on.
2556     *
2557     * Results:
2558     * None.
2559     *
2560     * Side effects:
2561     * The cursor gets turned on or off, redisplay gets invoked,
2562     * and this procedure reschedules itself.
2563     *
2564     *----------------------------------------------------------------------
2565     */
2566    
2567     static void
2568     EntryBlinkProc(clientData)
2569     ClientData clientData; /* Pointer to record describing entry. */
2570     {
2571     Entry *entryPtr = (Entry *) clientData;
2572    
2573     if ((entryPtr->state == STATE_DISABLED) ||
2574     !(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
2575     return;
2576     }
2577     if (entryPtr->flags & CURSOR_ON) {
2578     entryPtr->flags &= ~CURSOR_ON;
2579     entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2580     entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
2581     } else {
2582     entryPtr->flags |= CURSOR_ON;
2583     entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2584     entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
2585     }
2586     EventuallyRedraw(entryPtr);
2587     }
2588    
2589     /*
2590     *----------------------------------------------------------------------
2591     *
2592     * EntryFocusProc --
2593     *
2594     * This procedure is called whenever the entry gets or loses the
2595     * input focus. It's also called whenever the window is reconfigured
2596     * while it has the focus.
2597     *
2598     * Results:
2599     * None.
2600     *
2601     * Side effects:
2602     * The cursor gets turned on or off.
2603     *
2604     *----------------------------------------------------------------------
2605     */
2606    
2607     static void
2608     EntryFocusProc(entryPtr, gotFocus)
2609     Entry *entryPtr; /* Entry that got or lost focus. */
2610     int gotFocus; /* 1 means window is getting focus, 0 means
2611     * it's losing it. */
2612     {
2613     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
2614     if (gotFocus) {
2615     entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
2616     if (entryPtr->insertOffTime != 0) {
2617     entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2618     entryPtr->insertOnTime, EntryBlinkProc,
2619     (ClientData) entryPtr);
2620     }
2621     if (entryPtr->validate == VALIDATE_ALL ||
2622     entryPtr->validate == VALIDATE_FOCUS ||
2623     entryPtr->validate == VALIDATE_FOCUSIN) {
2624     EntryValidateChange(entryPtr, (char *) NULL,
2625     entryPtr->string, -1, VALIDATE_FOCUSIN);
2626     }
2627     } else {
2628     entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
2629     entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
2630     if (entryPtr->validate == VALIDATE_ALL ||
2631     entryPtr->validate == VALIDATE_FOCUS ||
2632     entryPtr->validate == VALIDATE_FOCUSOUT) {
2633     EntryValidateChange(entryPtr, (char *) NULL,
2634     entryPtr->string, -1, VALIDATE_FOCUSOUT);
2635     }
2636     }
2637     EventuallyRedraw(entryPtr);
2638     }
2639    
2640     /*
2641     *--------------------------------------------------------------
2642     *
2643     * EntryTextVarProc --
2644     *
2645     * This procedure is invoked when someone changes the variable
2646     * whose contents are to be displayed in an entry.
2647     *
2648     * Results:
2649     * NULL is always returned.
2650     *
2651     * Side effects:
2652     * The text displayed in the entry will change to match the
2653     * variable.
2654     *
2655     *--------------------------------------------------------------
2656     */
2657    
2658     /* ARGSUSED */
2659     static char *
2660     EntryTextVarProc(clientData, interp, name1, name2, flags)
2661     ClientData clientData; /* Information about button. */
2662     Tcl_Interp *interp; /* Interpreter containing variable. */
2663     char *name1; /* Not used. */
2664     char *name2; /* Not used. */
2665     int flags; /* Information about what happened. */
2666     {
2667     Entry *entryPtr = (Entry *) clientData;
2668     char *value;
2669    
2670     /*
2671     * If the variable is unset, then immediately recreate it unless
2672     * the whole interpreter is going away.
2673     */
2674    
2675     if (flags & TCL_TRACE_UNSETS) {
2676     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2677     Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
2678     TCL_GLOBAL_ONLY);
2679     Tcl_TraceVar(interp, entryPtr->textVarName,
2680     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2681     EntryTextVarProc, clientData);
2682     }
2683     return (char *) NULL;
2684     }
2685    
2686     /*
2687     * Update the entry's text with the value of the variable, unless
2688     * the entry already has that value (this happens when the variable
2689     * changes value because we changed it because someone typed in
2690     * the entry).
2691     */
2692    
2693     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
2694     if (value == NULL) {
2695     value = "";
2696     }
2697     EntrySetValue(entryPtr, value);
2698     return (char *) NULL;
2699     }
2700    
2701     /*
2702     *--------------------------------------------------------------
2703     *
2704     * EntryValidate --
2705     *
2706     * This procedure is invoked when any character is added or
2707     * removed from the entry widget, or a focus has trigerred validation.
2708     *
2709     * Results:
2710     * TCL_OK if the validatecommand passes the new string.
2711     * TCL_BREAK if the vcmd executed OK, but rejects the string.
2712     * TCL_ERROR if an error occurred while executing the vcmd
2713     * or a valid Tcl_Bool is not returned.
2714     *
2715     * Side effects:
2716     * An error condition may arise
2717     *
2718     *--------------------------------------------------------------
2719     */
2720    
2721     static int
2722     EntryValidate(entryPtr, cmd)
2723     register Entry *entryPtr; /* Entry that needs validation. */
2724     register char *cmd; /* Validation command (NULL-terminated
2725     * string). */
2726     {
2727     register Tcl_Interp *interp = entryPtr->interp;
2728     int code, bool;
2729    
2730     code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
2731    
2732     if (code != TCL_OK && code != TCL_RETURN) {
2733     Tcl_AddErrorInfo(interp,
2734     "\n\t(in validation command executed by entry)");
2735     Tcl_BackgroundError(interp);
2736     return TCL_ERROR;
2737     }
2738    
2739     if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),
2740     &bool) != TCL_OK) {
2741     Tcl_AddErrorInfo(interp,
2742     "\nvalid boolean not returned by validation command");
2743     Tcl_BackgroundError(interp);
2744     Tcl_SetResult(interp, NULL, 0);
2745     return TCL_ERROR;
2746     }
2747    
2748     Tcl_SetResult(interp, NULL, 0);
2749     return (bool ? TCL_OK : TCL_BREAK);
2750     }
2751    
2752     /*
2753     *--------------------------------------------------------------
2754     *
2755     * EntryValidateChange --
2756     *
2757     * This procedure is invoked when any character is added or
2758     * removed from the entry widget, or a focus has trigerred validation.
2759     *
2760     * Results:
2761     * TCL_OK if the validatecommand accepts the new string,
2762     * TCL_ERROR if any problems occured with validatecommand.
2763     *
2764     * Side effects:
2765     * The insertion/deletion may be aborted, and the
2766     * validatecommand might turn itself off (if an error
2767     * or loop condition arises).
2768     *
2769     *--------------------------------------------------------------
2770     */
2771    
2772     static int
2773     EntryValidateChange(entryPtr, change, new, index, type)
2774     register Entry *entryPtr; /* Entry that needs validation. */
2775     char *change; /* Characters to be added/deleted
2776     * (NULL-terminated string). */
2777     char *new; /* Potential new value of entry string */
2778     int index; /* index of insert/delete, -1 otherwise */
2779     int type; /* forced, delete, insert,
2780     * focusin or focusout */
2781     {
2782     int code, varValidate = (entryPtr->flags & VALIDATE_VAR);
2783     char *p;
2784     Tcl_DString script;
2785    
2786     if (entryPtr->validateCmd == NULL ||
2787     entryPtr->validate == VALIDATE_NONE) {
2788     return (varValidate ? TCL_ERROR : TCL_OK);
2789     }
2790    
2791     /*
2792     * If we're already validating, then we're hitting a loop condition
2793     * Return and set validate to 0 to disallow further validations
2794     * and prevent current validation from finishing
2795     */
2796     if (entryPtr->flags & VALIDATING) {
2797     entryPtr->validate = VALIDATE_NONE;
2798     return (varValidate ? TCL_ERROR : TCL_OK);
2799     }
2800    
2801     entryPtr->flags |= VALIDATING;
2802    
2803     /*
2804     * Now form command string and run through the -validatecommand
2805     */
2806    
2807     Tcl_DStringInit(&script);
2808     ExpandPercents(entryPtr, entryPtr->validateCmd,
2809     change, new, index, type, &script);
2810     Tcl_DStringAppend(&script, "", 1);
2811    
2812     p = Tcl_DStringValue(&script);
2813     code = EntryValidate(entryPtr, p);
2814     Tcl_DStringFree(&script);
2815    
2816     /*
2817     * If e->validate has become VALIDATE_NONE during the validation, or
2818     * we now have VALIDATE_VAR set (from EntrySetValue) and didn't before,
2819     * it means that a loop condition almost occured. Do not allow
2820     * this validation result to finish.
2821     */
2822     if (entryPtr->validate == VALIDATE_NONE
2823     || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) {
2824     code = TCL_ERROR;
2825     }
2826     /*
2827     * If validate will return ERROR, then disallow further validations
2828     * Otherwise, if it didn't accept the new string (returned TCL_BREAK)
2829     * then eval the invalidCmd (if it's set)
2830     */
2831     if (code == TCL_ERROR) {
2832     entryPtr->validate = VALIDATE_NONE;
2833     } else if (code == TCL_BREAK) {
2834     /*
2835     * If we were doing forced validation (like via a variable
2836     * trace) and the command returned 0, the we turn off validation
2837     * because we assume that textvariables have precedence in
2838     * managing the value. We also don't call the invcmd, as it
2839     * may want to do entry manipulation which the setting of the
2840     * var will later wipe anyway.
2841     */
2842     if (varValidate) {
2843     entryPtr->validate = VALIDATE_NONE;
2844     } else if (entryPtr->invalidCmd != NULL) {
2845     Tcl_DStringInit(&script);
2846     ExpandPercents(entryPtr, entryPtr->invalidCmd,
2847     change, new, index, type, &script);
2848     Tcl_DStringAppend(&script, "", 1);
2849     p = Tcl_DStringValue(&script);
2850     if (Tcl_EvalEx(entryPtr->interp, p, -1,
2851     TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT) != TCL_OK) {
2852     Tcl_AddErrorInfo(entryPtr->interp,
2853     "\n\t(in invalidcommand executed by entry)");
2854     Tcl_BackgroundError(entryPtr->interp);
2855     code = TCL_ERROR;
2856     entryPtr->validate = VALIDATE_NONE;
2857     }
2858     Tcl_DStringFree(&script);
2859     }
2860     }
2861    
2862     entryPtr->flags &= ~VALIDATING;
2863    
2864     return code;
2865     }
2866    
2867     /*
2868     *--------------------------------------------------------------
2869     *
2870     * ExpandPercents --
2871     *
2872     * Given a command and an event, produce a new command
2873     * by replacing % constructs in the original command
2874     * with information from the X event.
2875     *
2876     * Results:
2877     * The new expanded command is appended to the dynamic string
2878     * given by dsPtr.
2879     *
2880     * Side effects:
2881     * None.
2882     *
2883     *--------------------------------------------------------------
2884     */
2885    
2886     static void
2887     ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
2888     register Entry *entryPtr; /* Entry that needs validation. */
2889     register char *before; /* Command containing percent
2890     * expressions to be replaced. */
2891     char *change; /* Characters to added/deleted
2892     * (NULL-terminated string). */
2893     char *new; /* Potential new value of entry string */
2894     int index; /* index of insert/delete */
2895     int type; /* INSERT or DELETE */
2896     Tcl_DString *dsPtr; /* Dynamic string in which to append
2897     * new command. */
2898     {
2899     int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
2900     * list element. */
2901     int number, length;
2902     register char *string;
2903     Tcl_UniChar ch;
2904     char numStorage[2*TCL_INTEGER_SPACE];
2905    
2906     while (1) {
2907     if (*before == '\0') {
2908     break;
2909     }
2910     /*
2911     * Find everything up to the next % character and append it
2912     * to the result string.
2913     */
2914    
2915     string = before;
2916     /* No need to convert '%', as it is in ascii range */
2917     string = Tcl_UtfFindFirst(before, '%');
2918     if (string == (char *) NULL) {
2919     Tcl_DStringAppend(dsPtr, before, -1);
2920     break;
2921     } else if (string != before) {
2922     Tcl_DStringAppend(dsPtr, before, string-before);
2923     before = string;
2924     }
2925    
2926     /*
2927     * There's a percent sequence here. Process it.
2928     */
2929    
2930     before++; /* skip over % */
2931     if (*before != '\0') {
2932     before += Tcl_UtfToUniChar(before, &ch);
2933     } else {
2934     ch = '%';
2935     }
2936     switch (ch) {
2937     case 'd': /* Type of call that caused validation */
2938     switch (type) {
2939     case VALIDATE_INSERT:
2940     number = 1;
2941     break;
2942     case VALIDATE_DELETE:
2943     number = 0;
2944     break;
2945     default:
2946     number = -1;
2947     break;
2948     }
2949     sprintf(numStorage, "%d", number);
2950     string = numStorage;
2951     break;
2952     case 'i': /* index of insert/delete */
2953     sprintf(numStorage, "%d", index);
2954     string = numStorage;
2955     break;
2956     case 'P': /* 'Peeked' new value of the string */
2957     string = new;
2958     break;
2959     case 's': /* Current string value of entry */
2960     string = entryPtr->string;
2961     break;
2962     case 'S': /* string to be inserted/deleted, if any */
2963     string = change;
2964     break;
2965     case 'v': /* type of validation currently set */
2966     string = validateStrings[entryPtr->validate];
2967     break;
2968     case 'V': /* type of validation in effect */
2969     switch (type) {
2970     case VALIDATE_INSERT:
2971     case VALIDATE_DELETE:
2972     string = validateStrings[VALIDATE_KEY];
2973     break;
2974     case VALIDATE_FORCED:
2975     string = "forced";
2976     break;
2977     default:
2978     string = validateStrings[type];
2979     break;
2980     }
2981     break;
2982     case 'W': /* widget name */
2983     string = Tk_PathName(entryPtr->tkwin);
2984     break;
2985     default:
2986     length = Tcl_UniCharToUtf(ch, numStorage);
2987     numStorage[length] = '\0';
2988     string = numStorage;
2989     break;
2990     }
2991    
2992     spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);
2993     length = Tcl_DStringLength(dsPtr);
2994     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2995     spaceNeeded = Tcl_ConvertCountedElement(string, -1,
2996     Tcl_DStringValue(dsPtr) + length,
2997     cvtFlags | TCL_DONT_USE_BRACES);
2998     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2999     }
3000     }
3001    
3002    
3003     /* $History: tkEntry.c $
3004     *
3005     * ***************** Version 1 *****************
3006     * User: Dtashley Date: 1/02/01 Time: 2:39a
3007     * Created in $/IjuScripter, IjuConsole/Source/Tk Base
3008     * Initial check-in.
3009     */
3010    
3011     /* End of TKENTRY.C */

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25