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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 44 - (show annotations) (download)
Fri Oct 14 02:09:58 2016 UTC (7 years, 4 months ago) by dashley
File MIME type: text/plain
File size: 94108 byte(s)
Rename for reorganization.
1 /* $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