4 * This module implements entry widgets for the Tk
5 * toolkit. An entry displays a string and allows
6 * the string to be edited.
8 * Copyright 1990 Regents of the University of California.
9 * Permission to use, copy, modify, and distribute this
10 * software and its documentation for any purpose and without
11 * fee is hereby granted, provided that the above copyright
12 * notice appear in all copies. The University of California
13 * makes no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without
15 * express or implied warranty.
19 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkEntry.c,v 1.37 92/08/21 16:09:15 ouster Exp $ SPRITE (Berkeley)";
27 * A data structure of the following type is kept for each entry
28 * widget managed by this file:
32 Tk_Window tkwin
; /* Window that embodies the entry. NULL
33 * means that the window has been destroyed
34 * but the data structures haven't yet been
36 Tcl_Interp
*interp
; /* Interpreter associated with entry. */
37 int numChars
; /* Number of non-NULL characters in
38 * string (may be 0). */
39 char *string
; /* Pointer to storage for string;
40 * NULL-terminated; malloc-ed. */
41 char *textVarName
; /* Name of variable (malloc'ed) or NULL.
42 * If non-NULL, entry's string tracks the
43 * contents of this variable and vice versa. */
44 Tk_Uid state
; /* Normal or disabled. Entry is read-only
48 * Information used when displaying widget:
51 Tk_3DBorder normalBorder
; /* Used for drawing border around whole
52 * window, plus used for background. */
53 int borderWidth
; /* Width of 3-D border around window. */
54 int relief
; /* 3-D effect: TK_RELIEF_RAISED, etc. */
55 XFontStruct
*fontPtr
; /* Information about text font, or NULL. */
56 XColor
*fgColorPtr
; /* Text color in normal mode. */
57 GC textGC
; /* For drawing normal text. */
58 Tk_3DBorder selBorder
; /* Border and background for selected
60 int selBorderWidth
; /* Width of border around selection. */
61 XColor
*selFgColorPtr
; /* Foreground color for selected text. */
62 GC selTextGC
; /* For drawing selected text. */
63 Tk_3DBorder cursorBorder
; /* Used to draw vertical bar for insertion
65 int cursorWidth
; /* Total width of insert cursor. */
66 int cursorBorderWidth
; /* Width of 3-D border around insert cursor. */
67 int cursorOnTime
; /* Number of milliseconds cursor should spend
68 * in "on" state for each blink. */
69 int cursorOffTime
; /* Number of milliseconds cursor should spend
70 * in "off" state for each blink. */
71 Tk_TimerToken cursorBlinkHandler
;
72 /* Timer handler used to blink cursor on and
74 int avgWidth
; /* Width of average character. */
75 int prefWidth
; /* Desired width of window, measured in
76 * average characters. */
77 int offset
; /* 0 if window is flat, or borderWidth if
78 * raised or sunken. */
79 int leftIndex
; /* Index of left-most character visible in
81 int cursorPos
; /* Index of character before which next
82 * typed character will be inserted. */
85 * Information about what's selected, if any.
88 int selectFirst
; /* Index of first selected character (-1 means
89 * nothing selected. */
90 int selectLast
; /* Index of last selected character (-1 means
91 * nothing selected. */
92 int selectAnchor
; /* Fixed end of selection (i.e. "select to"
93 * operation will use this as one end of the
95 int exportSelection
; /* Non-zero means tie internal entry selection
99 * Information for scanning:
102 int scanMarkX
; /* X-position at which scan started (e.g.
103 * button was pressed here). */
104 int scanMarkIndex
; /* Index of character that was at left of
105 * window when scan started. */
108 * Miscellaneous information:
111 Cursor cursor
; /* Current cursor for window, or None. */
112 char *scrollCmd
; /* Command prefix for communicating with
113 * scrollbar(s). Malloc'ed. NULL means
114 * no command to issue. */
115 int flags
; /* Miscellaneous flags; see below for
120 * Assigned bits of "flags" fields of Entry structures, and what those
123 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has
124 * already been queued to redisplay the entry.
125 * BORDER_NEEDED: Non-zero means 3-D border must be redrawn
126 * around window during redisplay. Normally
127 * only text portion needs to be redrawn.
128 * CURSOR_ON: Non-zero means cursor is displayed at
129 * present. 0 means it isn't displayed.
130 * GOT_FOCUS: Non-zero means this window has the input
134 #define REDRAW_PENDING 1
135 #define BORDER_NEEDED 2
140 * Information used for argv parsing.
143 static Tk_ConfigSpec configSpecs
[] = {
144 {TK_CONFIG_BORDER
, "-background", "background", "Background",
145 DEF_ENTRY_BG_COLOR
, Tk_Offset(Entry
, normalBorder
),
146 TK_CONFIG_COLOR_ONLY
},
147 {TK_CONFIG_BORDER
, "-background", "background", "Background",
148 DEF_ENTRY_BG_MONO
, Tk_Offset(Entry
, normalBorder
),
149 TK_CONFIG_MONO_ONLY
},
150 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
151 (char *) NULL
, 0, 0},
152 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
153 (char *) NULL
, 0, 0},
154 {TK_CONFIG_PIXELS
, "-borderwidth", "borderWidth", "BorderWidth",
155 DEF_ENTRY_BORDER_WIDTH
, Tk_Offset(Entry
, borderWidth
), 0},
156 {TK_CONFIG_ACTIVE_CURSOR
, "-cursor", "cursor", "Cursor",
157 DEF_ENTRY_CURSOR
, Tk_Offset(Entry
, cursor
), TK_CONFIG_NULL_OK
},
158 {TK_CONFIG_BORDER
, "-cursorbackground", "cursorBackground", "Foreground",
159 DEF_ENTRY_CURSOR_BG
, Tk_Offset(Entry
, cursorBorder
), 0},
160 {TK_CONFIG_PIXELS
, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
161 DEF_ENTRY_CURSOR_BD_COLOR
, Tk_Offset(Entry
, cursorBorderWidth
),
162 TK_CONFIG_COLOR_ONLY
},
163 {TK_CONFIG_PIXELS
, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth",
164 DEF_ENTRY_CURSOR_BD_MONO
, Tk_Offset(Entry
, cursorBorderWidth
),
165 TK_CONFIG_MONO_ONLY
},
166 {TK_CONFIG_INT
, "-cursorofftime", "cursorOffTime", "OffTime",
167 DEF_ENTRY_CURSOR_OFF_TIME
, Tk_Offset(Entry
, cursorOffTime
), 0},
168 {TK_CONFIG_INT
, "-cursorontime", "cursorOnTime", "OnTime",
169 DEF_ENTRY_CURSOR_ON_TIME
, Tk_Offset(Entry
, cursorOnTime
), 0},
170 {TK_CONFIG_PIXELS
, "-cursorwidth", "cursorWidth", "CursorWidth",
171 DEF_ENTRY_CURSOR_WIDTH
, Tk_Offset(Entry
, cursorWidth
), 0},
172 {TK_CONFIG_BOOLEAN
, "-exportselection", "exportSelection",
173 "ExportSelection", DEF_ENTRY_EXPORT_SELECTION
,
174 Tk_Offset(Entry
, exportSelection
), 0},
175 {TK_CONFIG_SYNONYM
, "-fg", "foreground", (char *) NULL
,
176 (char *) NULL
, 0, 0},
177 {TK_CONFIG_FONT
, "-font", "font", "Font",
178 DEF_ENTRY_FONT
, Tk_Offset(Entry
, fontPtr
), 0},
179 {TK_CONFIG_COLOR
, "-foreground", "foreground", "Foreground",
180 DEF_ENTRY_FG
, Tk_Offset(Entry
, fgColorPtr
), 0},
181 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
182 DEF_ENTRY_RELIEF
, Tk_Offset(Entry
, relief
), 0},
183 {TK_CONFIG_STRING
, "-scrollcommand", "scrollCommand", "ScrollCommand",
184 DEF_ENTRY_SCROLL_COMMAND
, Tk_Offset(Entry
, scrollCmd
), 0},
185 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
186 DEF_ENTRY_SELECT_COLOR
, Tk_Offset(Entry
, selBorder
),
187 TK_CONFIG_COLOR_ONLY
},
188 {TK_CONFIG_BORDER
, "-selectbackground", "selectBackground", "Foreground",
189 DEF_ENTRY_SELECT_MONO
, Tk_Offset(Entry
, selBorder
),
190 TK_CONFIG_MONO_ONLY
},
191 {TK_CONFIG_PIXELS
, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
192 DEF_ENTRY_SELECT_BD_COLOR
, Tk_Offset(Entry
, selBorderWidth
),
193 TK_CONFIG_COLOR_ONLY
},
194 {TK_CONFIG_PIXELS
, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
195 DEF_ENTRY_SELECT_BD_MONO
, Tk_Offset(Entry
, selBorderWidth
),
196 TK_CONFIG_MONO_ONLY
},
197 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
198 DEF_ENTRY_SELECT_FG_COLOR
, Tk_Offset(Entry
, selFgColorPtr
),
199 TK_CONFIG_COLOR_ONLY
},
200 {TK_CONFIG_COLOR
, "-selectforeground", "selectForeground", "Background",
201 DEF_ENTRY_SELECT_FG_MONO
, Tk_Offset(Entry
, selFgColorPtr
),
202 TK_CONFIG_MONO_ONLY
},
203 {TK_CONFIG_UID
, "-state", "state", "State",
204 DEF_ENTRY_STATE
, Tk_Offset(Entry
, state
), 0},
205 {TK_CONFIG_STRING
, "-textvariable", "textVariable", "Variable",
206 DEF_ENTRY_TEXT_VARIABLE
, Tk_Offset(Entry
, textVarName
),
208 {TK_CONFIG_INT
, "-width", "width", "Width",
209 DEF_ENTRY_WIDTH
, Tk_Offset(Entry
, prefWidth
), 0},
210 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
215 * Flags for GetEntryIndex procedure:
219 #define LAST_PLUS_ONE_OK 2
222 * Forward declarations for procedures defined later in this file:
225 static int ConfigureEntry
_ANSI_ARGS_((Tcl_Interp
*interp
,
226 Entry
*entryPtr
, int argc
, char **argv
,
228 static void DeleteChars
_ANSI_ARGS_((Entry
*entryPtr
, int index
,
230 static void DestroyEntry
_ANSI_ARGS_((ClientData clientData
));
231 static void DisplayEntry
_ANSI_ARGS_((ClientData clientData
));
232 static int GetEntryIndex
_ANSI_ARGS_((Tcl_Interp
*interp
,
233 Entry
*entryPtr
, char *string
, int *indexPtr
));
234 static void InsertChars
_ANSI_ARGS_((Entry
*entryPtr
, int index
,
236 static void EntryBlinkProc
_ANSI_ARGS_((ClientData clientData
));
237 static void EntryEventProc
_ANSI_ARGS_((ClientData clientData
,
239 static void EntryFocusProc
_ANSI_ARGS_ ((ClientData clientData
,
241 static int EntryFetchSelection
_ANSI_ARGS_((ClientData clientData
,
242 int offset
, char *buffer
, int maxBytes
));
243 static void EntryLostSelection
_ANSI_ARGS_((
244 ClientData clientData
));
245 static void EventuallyRedraw
_ANSI_ARGS_((Entry
*entryPtr
));
246 static void EntryScanTo
_ANSI_ARGS_((Entry
*entryPtr
, int y
));
247 static void EntrySetValue
_ANSI_ARGS_((Entry
*entryPtr
,
249 static void EntrySelectTo
_ANSI_ARGS_((
250 Entry
*entryPtr
, int index
));
251 static char * EntryTextVarProc
_ANSI_ARGS_((ClientData clientData
,
252 Tcl_Interp
*interp
, char *name1
, char *name2
,
254 static void EntryUpdateScrollbar
_ANSI_ARGS_((Entry
*entryPtr
));
255 static int EntryWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
256 Tcl_Interp
*interp
, int argc
, char **argv
));
259 *--------------------------------------------------------------
263 * This procedure is invoked to process the "entry" Tcl
264 * command. See the user documentation for details on what
268 * A standard Tcl result.
271 * See the user documentation.
273 *--------------------------------------------------------------
278 ClientData clientData
, /* Main window associated with
280 Tcl_Interp
*interp
, /* Current interpreter. */
281 int argc
, /* Number of arguments. */
282 char **argv
/* Argument strings. */
285 Tk_Window tkwin
= (Tk_Window
) clientData
;
286 register Entry
*entryPtr
;
290 Tcl_AppendResult(interp
, "wrong # args: should be \"",
291 argv
[0], " pathName ?options?\"", (char *) NULL
);
295 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
301 * Initialize the fields of the structure that won't be initialized
302 * by ConfigureEntry, or that ConfigureEntry requires to be
303 * initialized already (e.g. resource pointers).
306 entryPtr
= (Entry
*) ckalloc(sizeof(Entry
));
307 entryPtr
->tkwin
= new;
308 entryPtr
->interp
= interp
;
309 entryPtr
->numChars
= 0;
310 entryPtr
->string
= (char *) ckalloc(1);
311 entryPtr
->string
[0] = '\0';
312 entryPtr
->textVarName
= NULL
;
313 entryPtr
->state
= tkNormalUid
;
314 entryPtr
->normalBorder
= NULL
;
315 entryPtr
->fontPtr
= NULL
;
316 entryPtr
->fgColorPtr
= NULL
;
317 entryPtr
->textGC
= None
;
318 entryPtr
->selBorder
= NULL
;
319 entryPtr
->selFgColorPtr
= NULL
;
320 entryPtr
->selTextGC
= NULL
;
321 entryPtr
->cursorBorder
= NULL
;
322 entryPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
323 entryPtr
->leftIndex
= 0;
324 entryPtr
->cursorPos
= 0;
325 entryPtr
->selectFirst
= -1;
326 entryPtr
->selectLast
= -1;
327 entryPtr
->selectAnchor
= 0;
328 entryPtr
->exportSelection
= 1;
329 entryPtr
->scanMarkX
= 0;
330 entryPtr
->cursor
= None
;
331 entryPtr
->scrollCmd
= NULL
;
334 Tk_SetClass(entryPtr
->tkwin
, "Entry");
335 Tk_CreateEventHandler(entryPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
336 EntryEventProc
, (ClientData
) entryPtr
);
337 Tk_CreateSelHandler(entryPtr
->tkwin
, XA_STRING
, EntryFetchSelection
,
338 (ClientData
) entryPtr
, XA_STRING
);
339 Tcl_CreateCommand(interp
, Tk_PathName(entryPtr
->tkwin
), EntryWidgetCmd
,
340 (ClientData
) entryPtr
, (void (*)(int *)) NULL
);
341 if (ConfigureEntry(interp
, entryPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
344 Tk_CreateFocusHandler(entryPtr
->tkwin
, EntryFocusProc
,
345 (ClientData
) entryPtr
);
347 interp
->result
= Tk_PathName(entryPtr
->tkwin
);
351 Tk_DestroyWindow(entryPtr
->tkwin
);
356 *--------------------------------------------------------------
360 * This procedure is invoked to process the Tcl command
361 * that corresponds to a widget managed by this module.
362 * See the user documentation for details on what it does.
365 * A standard Tcl result.
368 * See the user documentation.
370 *--------------------------------------------------------------
375 ClientData clientData
, /* Information about entry widget. */
376 Tcl_Interp
*interp
, /* Current interpreter. */
377 int argc
, /* Number of arguments. */
378 char **argv
/* Argument strings. */
381 register Entry
*entryPtr
= (Entry
*) clientData
;
387 Tcl_AppendResult(interp
, "wrong # args: should be \"",
388 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
391 Tk_Preserve((ClientData
) entryPtr
);
393 length
= strlen(argv
[1]);
394 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)
397 result
= Tk_ConfigureInfo(interp
, entryPtr
->tkwin
, configSpecs
,
398 (char *) entryPtr
, (char *) NULL
, 0);
399 } else if (argc
== 3) {
400 result
= Tk_ConfigureInfo(interp
, entryPtr
->tkwin
, configSpecs
,
401 (char *) entryPtr
, argv
[2], 0);
403 result
= ConfigureEntry(interp
, entryPtr
, argc
-2, argv
+2,
404 TK_CONFIG_ARGV_ONLY
);
406 } else if ((c
== 'c') && (strncmp(argv
[1], "cursor", length
) == 0)
409 Tcl_AppendResult(interp
, "wrong # args: should be \"",
410 argv
[0], " cursor pos\"",
414 if (GetEntryIndex(interp
, entryPtr
, argv
[2], &entryPtr
->cursorPos
)
418 EventuallyRedraw(entryPtr
);
419 } else if ((c
== 'd') && (strncmp(argv
[1], "delete", length
) == 0)) {
422 if ((argc
< 3) || (argc
> 4)) {
423 Tcl_AppendResult(interp
, "wrong # args: should be \"",
424 argv
[0], " delete firstIndex ?lastIndex?\"",
428 if (GetEntryIndex(interp
, entryPtr
, argv
[2], &first
) != TCL_OK
) {
434 if (GetEntryIndex(interp
, entryPtr
, argv
[3], &last
) != TCL_OK
) {
438 if ((last
>= first
) && (entryPtr
->state
== tkNormalUid
)) {
439 DeleteChars(entryPtr
, first
, last
+1-first
);
441 } else if ((c
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) {
443 Tcl_AppendResult(interp
, "wrong # args: should be \"",
444 argv
[0], " get\"", (char *) NULL
);
447 interp
->result
= entryPtr
->string
;
448 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)
453 Tcl_AppendResult(interp
, "wrong # args: should be \"",
454 argv
[0], " index string\"", (char *) NULL
);
457 if (GetEntryIndex(interp
, entryPtr
, argv
[2], &index
) != TCL_OK
) {
460 sprintf(interp
->result
, "%d", index
);
461 } else if ((c
== 'i') && (strncmp(argv
[1], "insert", length
) == 0)
466 Tcl_AppendResult(interp
, "wrong # args: should be \"",
467 argv
[0], " insert index text\"",
471 if (GetEntryIndex(interp
, entryPtr
, argv
[2], &index
) != TCL_OK
) {
474 if (entryPtr
->state
== tkNormalUid
) {
475 InsertChars(entryPtr
, index
, argv
[3]);
477 } else if ((c
== 's') && (length
>= 2)
478 && (strncmp(argv
[1], "scan", length
) == 0)) {
482 Tcl_AppendResult(interp
, "wrong # args: should be \"",
483 argv
[0], " scan mark|dragto x\"", (char *) NULL
);
486 if (Tcl_GetInt(interp
, argv
[3], &x
) != TCL_OK
) {
489 if ((argv
[2][0] == 'm')
490 && (strncmp(argv
[2], "mark", strlen(argv
[2])) == 0)) {
491 entryPtr
->scanMarkX
= x
;
492 entryPtr
->scanMarkIndex
= entryPtr
->leftIndex
;
493 } else if ((argv
[2][0] == 'd')
494 && (strncmp(argv
[2], "dragto", strlen(argv
[2])) == 0)) {
495 EntryScanTo(entryPtr
, x
);
497 Tcl_AppendResult(interp
, "bad scan option \"", argv
[2],
498 "\": must be mark or dragto", (char *) NULL
);
501 } else if ((c
== 's') && (length
>= 2)
502 && (strncmp(argv
[1], "select", length
) == 0)) {
506 Tcl_AppendResult(interp
, "too few args: should be \"",
507 argv
[0], " select option ?index?\"", (char *) NULL
);
510 length
= strlen(argv
[2]);
512 if ((c
== 'c') && (argv
[2] != NULL
)
513 && (strncmp(argv
[2], "clear", length
) == 0)) {
515 Tcl_AppendResult(interp
, "wrong # args: should be \"",
516 argv
[0], " select clear\"", (char *) NULL
);
519 if (entryPtr
->selectFirst
!= -1) {
520 entryPtr
->selectFirst
= entryPtr
->selectLast
= -1;
521 EventuallyRedraw(entryPtr
);
526 if (GetEntryIndex(interp
, entryPtr
, argv
[3], &index
) != TCL_OK
) {
530 if ((c
== 'a') && (strncmp(argv
[2], "adjust", length
) == 0)) {
532 Tcl_AppendResult(interp
, "wrong # args: should be \"",
533 argv
[0], " select adjust index\"",
537 if (entryPtr
->selectFirst
>= 0) {
538 if (index
< (entryPtr
->selectFirst
+ entryPtr
->selectLast
)/2) {
539 entryPtr
->selectAnchor
= entryPtr
->selectLast
+ 1;
541 entryPtr
->selectAnchor
= entryPtr
->selectFirst
;
544 EntrySelectTo(entryPtr
, index
);
545 } else if ((c
== 'f') && (strncmp(argv
[2], "from", length
) == 0)) {
547 Tcl_AppendResult(interp
, "wrong # args: should be \"",
548 argv
[0], " select from index\"",
552 entryPtr
->selectAnchor
= index
;
553 } else if ((c
== 't') && (strncmp(argv
[2], "to", length
) == 0)) {
555 Tcl_AppendResult(interp
, "wrong # args: should be \"",
556 argv
[0], " select to index\"",
560 EntrySelectTo(entryPtr
, index
);
562 Tcl_AppendResult(interp
, "bad select option \"", argv
[2],
563 "\": must be adjust, clear, from, or to", (char *) NULL
);
566 } else if ((c
== 'v') && (strncmp(argv
[1], "view", length
) == 0)) {
570 Tcl_AppendResult(interp
, "wrong # args: should be \"",
571 argv
[0], " view index\"", (char *) NULL
);
574 if (GetEntryIndex(interp
, entryPtr
, argv
[2], &index
) != TCL_OK
) {
577 if ((index
>= entryPtr
->numChars
) && (index
> 0)) {
578 index
= entryPtr
->numChars
-1;
580 entryPtr
->leftIndex
= index
;
581 EventuallyRedraw(entryPtr
);
582 EntryUpdateScrollbar(entryPtr
);
584 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
585 "\": must be configure, cursor, delete, get, index, ",
586 "insert, scan, select, or view", (char *) NULL
);
590 Tk_Release((ClientData
) entryPtr
);
594 Tk_Release((ClientData
) entryPtr
);
599 *----------------------------------------------------------------------
603 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
604 * to clean up the internal structure of an entry at a safe time
605 * (when no-one is using it anymore).
611 * Everything associated with the entry is freed up.
613 *----------------------------------------------------------------------
618 ClientData clientData
/* Info about entry widget. */
621 register Entry
*entryPtr
= (Entry
*) clientData
;
623 ckfree(entryPtr
->string
);
624 if (entryPtr
->normalBorder
!= NULL
) {
625 Tk_Free3DBorder(entryPtr
->normalBorder
);
627 if (entryPtr
->textVarName
!= NULL
) {
628 Tcl_UntraceVar(entryPtr
->interp
, entryPtr
->textVarName
,
629 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
630 EntryTextVarProc
, (ClientData
) entryPtr
);
631 ckfree(entryPtr
->textVarName
);
633 if (entryPtr
->fontPtr
!= NULL
) {
634 Tk_FreeFontStruct(entryPtr
->fontPtr
);
636 if (entryPtr
->fgColorPtr
!= NULL
) {
637 Tk_FreeColor(entryPtr
->fgColorPtr
);
639 if (entryPtr
->textGC
!= None
) {
640 Tk_FreeGC(entryPtr
->textGC
);
642 if (entryPtr
->selBorder
!= NULL
) {
643 Tk_Free3DBorder(entryPtr
->selBorder
);
645 if (entryPtr
->selFgColorPtr
!= NULL
) {
646 Tk_FreeColor(entryPtr
->selFgColorPtr
);
648 if (entryPtr
->selTextGC
!= None
) {
649 Tk_FreeGC(entryPtr
->selTextGC
);
651 if (entryPtr
->cursorBorder
!= NULL
) {
652 Tk_Free3DBorder(entryPtr
->cursorBorder
);
654 if (entryPtr
->cursorBlinkHandler
!= NULL
) {
655 Tk_DeleteTimerHandler(entryPtr
->cursorBlinkHandler
);
656 entryPtr
->cursorBlinkHandler
= NULL
;
658 if (entryPtr
->cursor
!= None
) {
659 Tk_FreeCursor(entryPtr
->cursor
);
661 if (entryPtr
->scrollCmd
!= NULL
) {
662 ckfree(entryPtr
->scrollCmd
);
664 ckfree((char *) entryPtr
);
668 *----------------------------------------------------------------------
672 * This procedure is called to process an argv/argc list, plus
673 * the Tk option database, in order to configure (or reconfigure)
677 * The return value is a standard Tcl result. If TCL_ERROR is
678 * returned, then interp->result contains an error message.
681 * Configuration information, such as colors, border width,
682 * etc. get set for entryPtr; old resources get freed,
685 *----------------------------------------------------------------------
690 Tcl_Interp
*interp
, /* Used for error reporting. */
691 register Entry
*entryPtr
, /* Information about widget; may or may
692 * not already have values for some fields. */
693 int argc
, /* Number of valid entries in argv. */
694 char **argv
, /* Arguments. */
695 int flags
/* Flags to pass to Tk_ConfigureWidget. */
700 int width
, height
, fontHeight
, oldExport
;
703 * Eliminate any existing trace on a variable monitored by the entry.
706 if (entryPtr
->textVarName
!= NULL
) {
707 Tcl_UntraceVar(interp
, entryPtr
->textVarName
,
708 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
709 EntryTextVarProc
, (ClientData
) entryPtr
);
712 oldExport
= entryPtr
->exportSelection
;
713 if (Tk_ConfigureWidget(interp
, entryPtr
->tkwin
, configSpecs
,
714 argc
, argv
, (char *) entryPtr
, flags
) != TCL_OK
) {
719 * If the entry is tied to the value of a variable, then set up
720 * a trace on the variable's value, create the variable if it doesn't
721 * exist, and set the entry's value from the variable's value.
724 if (entryPtr
->textVarName
!= NULL
) {
727 value
= Tcl_GetVar(interp
, entryPtr
->textVarName
, TCL_GLOBAL_ONLY
);
729 Tcl_SetVar(interp
, entryPtr
->textVarName
, entryPtr
->string
,
732 EntrySetValue(entryPtr
, value
);
734 Tcl_TraceVar(interp
, entryPtr
->textVarName
,
735 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
736 EntryTextVarProc
, (ClientData
) entryPtr
);
740 * A few other options also need special processing, such as parsing
741 * the geometry and setting the background from a 3-D border.
744 if ((entryPtr
->state
!= tkNormalUid
)
745 && (entryPtr
->state
!= tkDisabledUid
)) {
746 Tcl_AppendResult(interp
, "bad state value \"", entryPtr
->state
,
747 "\": must be normal or disabled", (char *) NULL
);
748 entryPtr
->state
= tkNormalUid
;
752 Tk_SetBackgroundFromBorder(entryPtr
->tkwin
, entryPtr
->normalBorder
);
754 gcValues
.foreground
= entryPtr
->fgColorPtr
->pixel
;
755 gcValues
.font
= entryPtr
->fontPtr
->fid
;
756 gcValues
.graphics_exposures
= False
;
757 new = Tk_GetGC(entryPtr
->tkwin
, GCForeground
|GCFont
|GCGraphicsExposures
,
759 if (entryPtr
->textGC
!= None
) {
760 Tk_FreeGC(entryPtr
->textGC
);
762 entryPtr
->textGC
= new;
764 gcValues
.foreground
= entryPtr
->selFgColorPtr
->pixel
;
765 gcValues
.font
= entryPtr
->fontPtr
->fid
;
766 new = Tk_GetGC(entryPtr
->tkwin
, GCForeground
|GCFont
, &gcValues
);
767 if (entryPtr
->selTextGC
!= None
) {
768 Tk_FreeGC(entryPtr
->selTextGC
);
770 entryPtr
->selTextGC
= new;
772 if (entryPtr
->cursorWidth
> 2*entryPtr
->fontPtr
->min_bounds
.width
) {
773 entryPtr
->cursorWidth
= 2*entryPtr
->fontPtr
->min_bounds
.width
;
774 if (entryPtr
->cursorWidth
== 0) {
775 entryPtr
->cursorWidth
= 2;
778 if (entryPtr
->cursorBorderWidth
> entryPtr
->cursorWidth
/2) {
779 entryPtr
->cursorBorderWidth
= entryPtr
->cursorWidth
/2;
783 * Restart the cursor timing sequence in case the on-time or off-time
787 if (entryPtr
->flags
& GOT_FOCUS
) {
788 EntryFocusProc((ClientData
) entryPtr
, 1);
792 * Claim the selection if we've suddenly started exporting it.
795 if (entryPtr
->exportSelection
&& (!oldExport
)
796 && (entryPtr
->selectFirst
!= -1)) {
797 Tk_OwnSelection(entryPtr
->tkwin
, EntryLostSelection
,
798 (ClientData
) entryPtr
);
802 * Register the desired geometry for the window, and arrange for
803 * the window to be redisplayed.
806 fontHeight
= entryPtr
->fontPtr
->ascent
+ entryPtr
->fontPtr
->descent
;
807 entryPtr
->avgWidth
= XTextWidth(entryPtr
->fontPtr
, "0", 1);
808 width
= entryPtr
->prefWidth
*entryPtr
->avgWidth
+ (15*fontHeight
)/10;
809 height
= fontHeight
+ 2*entryPtr
->borderWidth
+ 2;
810 Tk_GeometryRequest(entryPtr
->tkwin
, width
, height
);
811 Tk_SetInternalBorder(entryPtr
->tkwin
, entryPtr
->borderWidth
);
812 if (entryPtr
->relief
!= TK_RELIEF_FLAT
) {
813 entryPtr
->offset
= entryPtr
->borderWidth
;
815 entryPtr
->offset
= 0;
817 EventuallyRedraw(entryPtr
);
818 EntryUpdateScrollbar(entryPtr
);
823 *--------------------------------------------------------------
827 * This procedure redraws the contents of an entry window.
833 * Information appears on the screen.
835 *--------------------------------------------------------------
840 ClientData clientData
/* Information about window. */
843 register Entry
*entryPtr
= (Entry
*) clientData
;
844 register Tk_Window tkwin
= entryPtr
->tkwin
;
845 int startX
, baseY
, selStartX
, selEndX
, index
, cursorX
;
849 entryPtr
->flags
&= ~REDRAW_PENDING
;
850 if ((entryPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
855 * In order to avoid screen flashes, this procedure redraws the
856 * textual area of the entry into off-screen memory, then copies
857 * it back on-screen in a single operation. This means there's
858 * no point in time where the on-screen image has been cleared.
861 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
862 Tk_Width(tkwin
), Tk_Height(tkwin
),
863 Tk_DefaultDepth(Tk_Screen(tkwin
)));
866 * Compute x-coordinate of the "leftIndex" character, plus limit
867 * of visible x-coordinates (actually, pixel just after last visible
868 * one), plus vertical position of baseline of text.
871 startX
= entryPtr
->offset
;
872 xBound
= Tk_Width(tkwin
) - entryPtr
->offset
;
873 baseY
= (Tk_Height(tkwin
) + entryPtr
->fontPtr
->ascent
874 - entryPtr
->fontPtr
->descent
)/2;
877 * Draw the background in three layers. From bottom to top the
878 * layers are: normal background, selection background, and
879 * insertion cursor background.
882 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, entryPtr
->normalBorder
,
883 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 0, TK_RELIEF_FLAT
);
885 if (entryPtr
->selectLast
>= entryPtr
->leftIndex
) {
886 if (entryPtr
->selectFirst
<= entryPtr
->leftIndex
) {
888 index
= entryPtr
->leftIndex
;
890 (void) TkMeasureChars(entryPtr
->fontPtr
,
891 entryPtr
->string
+entryPtr
->leftIndex
,
892 entryPtr
->selectFirst
- entryPtr
->leftIndex
, startX
,
893 xBound
, TK_PARTIAL_OK
|TK_NEWLINES_NOT_SPECIAL
, &selStartX
);
894 index
= entryPtr
->selectFirst
;
896 if (selStartX
< xBound
) {
897 (void) TkMeasureChars(entryPtr
->fontPtr
,
898 entryPtr
->string
+ index
, entryPtr
->selectLast
+1 - index
,
899 selStartX
, xBound
, TK_PARTIAL_OK
|TK_NEWLINES_NOT_SPECIAL
,
901 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, entryPtr
->selBorder
,
902 selStartX
- entryPtr
->selBorderWidth
,
903 baseY
- entryPtr
->fontPtr
->ascent
904 - entryPtr
->selBorderWidth
,
905 (selEndX
- selStartX
) + 2*entryPtr
->selBorderWidth
,
906 entryPtr
->fontPtr
->ascent
+ entryPtr
->fontPtr
->descent
907 + 2*entryPtr
->selBorderWidth
,
908 entryPtr
->selBorderWidth
, TK_RELIEF_RAISED
);
915 * Draw a special background for the insertion cursor, overriding
916 * even the selection background. As a special workaround to keep the
917 * cursor visible on mono displays, write background in the cursor
918 * area (instead of nothing) when the cursor isn't on. Otherwise
919 * the selection would hide the cursor.
922 if ((entryPtr
->cursorPos
>= entryPtr
->leftIndex
)
923 && (entryPtr
->state
== tkNormalUid
)
924 && (entryPtr
->flags
& GOT_FOCUS
)) {
925 (void) TkMeasureChars(entryPtr
->fontPtr
,
926 entryPtr
->string
+ entryPtr
->leftIndex
,
927 entryPtr
->cursorPos
- entryPtr
->leftIndex
, startX
,
928 xBound
, TK_PARTIAL_OK
|TK_NEWLINES_NOT_SPECIAL
, &cursorX
);
929 if (cursorX
< xBound
) {
930 if (entryPtr
->flags
& CURSOR_ON
) {
931 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
932 entryPtr
->cursorBorder
,
933 cursorX
- (entryPtr
->cursorWidth
)/2,
934 baseY
- entryPtr
->fontPtr
->ascent
,
935 entryPtr
->cursorWidth
,
936 entryPtr
->fontPtr
->ascent
+ entryPtr
->fontPtr
->descent
,
937 entryPtr
->cursorBorderWidth
, TK_RELIEF_RAISED
);
938 } else if (Tk_DefaultDepth(Tk_Screen(tkwin
)) == 1) {
939 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
,
940 entryPtr
->normalBorder
,
941 cursorX
- (entryPtr
->cursorWidth
)/2,
942 baseY
- entryPtr
->fontPtr
->ascent
,
943 entryPtr
->cursorWidth
,
944 entryPtr
->fontPtr
->ascent
+ entryPtr
->fontPtr
->descent
,
951 * Draw the text in three pieces: first the piece to the left of
952 * the selection, then the selection, then the piece to the right
956 if (entryPtr
->selectLast
< entryPtr
->leftIndex
) {
957 TkDisplayChars(Tk_Display(tkwin
), pixmap
, entryPtr
->textGC
,
958 entryPtr
->fontPtr
, entryPtr
->string
+ entryPtr
->leftIndex
,
959 entryPtr
->numChars
- entryPtr
->leftIndex
, startX
, baseY
,
960 TK_NEWLINES_NOT_SPECIAL
);
962 count
= entryPtr
->selectFirst
- entryPtr
->leftIndex
;
964 TkDisplayChars(Tk_Display(tkwin
), pixmap
, entryPtr
->textGC
,
965 entryPtr
->fontPtr
, entryPtr
->string
+ entryPtr
->leftIndex
,
966 count
, startX
, baseY
, TK_NEWLINES_NOT_SPECIAL
);
967 index
= entryPtr
->selectFirst
;
969 index
= entryPtr
->leftIndex
;
971 count
= entryPtr
->selectLast
+ 1 - index
;
972 if ((selStartX
< xBound
) && (count
> 0)) {
973 TkDisplayChars(Tk_Display(tkwin
), pixmap
, entryPtr
->selTextGC
,
974 entryPtr
->fontPtr
, entryPtr
->string
+ index
, count
,
975 selStartX
, baseY
, TK_NEWLINES_NOT_SPECIAL
);
977 count
= entryPtr
->numChars
- entryPtr
->selectLast
- 1;
978 if ((selEndX
< xBound
) && (count
> 0)) {
979 TkDisplayChars(Tk_Display(tkwin
), pixmap
, entryPtr
->textGC
,
981 entryPtr
->string
+ entryPtr
->selectLast
+ 1,
982 count
, selEndX
, baseY
, TK_NEWLINES_NOT_SPECIAL
);
987 * Draw the border last, so it will overwrite any text that extends
988 * past the viewable part of the window.
991 if (entryPtr
->relief
!= TK_RELIEF_FLAT
) {
992 Tk_Draw3DRectangle(Tk_Display(tkwin
), pixmap
,
993 entryPtr
->normalBorder
, 0, 0, Tk_Width(tkwin
),
994 Tk_Height(tkwin
), entryPtr
->borderWidth
,
999 * Everything's been redisplayed; now copy the pixmap onto the screen
1000 * and free up the pixmap.
1003 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
), entryPtr
->textGC
,
1004 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 0, 0);
1005 XFreePixmap(Tk_Display(tkwin
), pixmap
);
1006 entryPtr
->flags
&= ~BORDER_NEEDED
;
1010 *----------------------------------------------------------------------
1014 * Add new characters to an entry widget.
1020 * New information gets added to entryPtr; it will be redisplayed
1021 * soon, but not necessarily immediately.
1023 *----------------------------------------------------------------------
1028 register Entry
*entryPtr
, /* Entry that is to get the new
1030 int index
, /* Add the new elements before this
1032 char *string
/* New characters to add (NULL-terminated
1039 length
= strlen(string
);
1043 new = (char *) ckalloc((unsigned) (entryPtr
->numChars
+ length
+ 1));
1044 strncpy(new, entryPtr
->string
, index
);
1045 strcpy(new+index
, string
);
1046 strcpy(new+index
+length
, entryPtr
->string
+index
);
1047 ckfree(entryPtr
->string
);
1048 entryPtr
->string
= new;
1049 entryPtr
->numChars
+= length
;
1052 * Inserting characters invalidates all indexes into the string.
1053 * Touch up the indexes so that they still refer to the same
1054 * characters (at new positions).
1057 if (entryPtr
->selectFirst
>= index
) {
1058 entryPtr
->selectFirst
+= length
;
1060 if (entryPtr
->selectLast
>= index
) {
1061 entryPtr
->selectLast
+= length
;
1063 if (entryPtr
->selectAnchor
>= index
) {
1064 entryPtr
->selectAnchor
+= length
;
1066 if (entryPtr
->leftIndex
> index
) {
1067 entryPtr
->leftIndex
+= length
;
1069 if (entryPtr
->cursorPos
>= index
) {
1070 entryPtr
->cursorPos
+= length
;
1073 if (entryPtr
->textVarName
!= NULL
) {
1074 Tcl_SetVar(entryPtr
->interp
, entryPtr
->textVarName
, entryPtr
->string
,
1077 EventuallyRedraw(entryPtr
);
1078 EntryUpdateScrollbar(entryPtr
);
1082 *----------------------------------------------------------------------
1086 * Remove one or more characters from an entry widget.
1092 * Memory gets freed, the entry gets modified and (eventually)
1095 *----------------------------------------------------------------------
1100 register Entry
*entryPtr
, /* Entry widget to modify. */
1101 int index
, /* Index of first character to delete. */
1102 int count
/* How many characters to delete. */
1107 if ((index
+ count
) > entryPtr
->numChars
) {
1108 count
= entryPtr
->numChars
- index
;
1114 new = (char *) ckalloc((unsigned) (entryPtr
->numChars
+ 1 - count
));
1115 strncpy(new, entryPtr
->string
, index
);
1116 strcpy(new+index
, entryPtr
->string
+index
+count
);
1117 ckfree(entryPtr
->string
);
1118 entryPtr
->string
= new;
1119 entryPtr
->numChars
-= count
;
1122 * Deleting characters results in the remaining characters being
1123 * renumbered. Update the various indexes into the string to reflect
1126 if (entryPtr
->selectFirst
>= index
) {
1127 if (entryPtr
->selectFirst
>= (index
+count
)) {
1128 entryPtr
->selectFirst
-= count
;
1130 entryPtr
->selectFirst
= index
;
1133 if (entryPtr
->selectLast
>= index
) {
1134 if (entryPtr
->selectLast
>= (index
+count
)) {
1135 entryPtr
->selectLast
-= count
;
1137 entryPtr
->selectLast
= index
-1;
1140 if (entryPtr
->selectLast
< entryPtr
->selectFirst
) {
1141 entryPtr
->selectFirst
= entryPtr
->selectLast
= -1;
1143 if (entryPtr
->selectAnchor
>= index
) {
1144 if (entryPtr
->selectAnchor
>= (index
+count
)) {
1145 entryPtr
->selectAnchor
-= count
;
1147 entryPtr
->selectAnchor
= index
;
1150 if (entryPtr
->leftIndex
> index
) {
1151 if (entryPtr
->leftIndex
>= (index
+count
)) {
1152 entryPtr
->leftIndex
-= count
;
1154 entryPtr
->leftIndex
= index
;
1157 if (entryPtr
->cursorPos
>= index
) {
1158 if (entryPtr
->cursorPos
>= (index
+count
)) {
1159 entryPtr
->cursorPos
-= count
;
1161 entryPtr
->cursorPos
= index
;
1165 if (entryPtr
->textVarName
!= NULL
) {
1166 Tcl_SetVar(entryPtr
->interp
, entryPtr
->textVarName
, entryPtr
->string
,
1169 EventuallyRedraw(entryPtr
);
1170 EntryUpdateScrollbar(entryPtr
);
1174 *----------------------------------------------------------------------
1178 * Replace the contents of a text entry with a given value. This
1179 * procedure is invoked when updating the entry from the entry's
1180 * associated variable.
1186 * The string displayed in the entry will change. Any selection
1187 * in the entry is lost and the insertion point gets set to the
1188 * end of the entry. Note: this procedure does *not* update the
1189 * entry's associated variable, since that could result in an
1192 *----------------------------------------------------------------------
1197 register Entry
*entryPtr
, /* Entry whose value is to be
1199 char *value
/* New text to display in entry. */
1202 ckfree(entryPtr
->string
);
1203 entryPtr
->numChars
= strlen(value
);
1204 entryPtr
->string
= (char *) ckalloc((unsigned) (entryPtr
->numChars
+ 1));
1205 strcpy(entryPtr
->string
, value
);
1206 entryPtr
->selectFirst
= entryPtr
->selectLast
= -1;
1207 entryPtr
->leftIndex
= 0;
1208 entryPtr
->cursorPos
= entryPtr
->numChars
;
1210 EventuallyRedraw(entryPtr
);
1211 EntryUpdateScrollbar(entryPtr
);
1215 *--------------------------------------------------------------
1219 * This procedure is invoked by the Tk dispatcher for various
1220 * events on entryes.
1226 * When the window gets deleted, internal structures get
1227 * cleaned up. When it gets exposed, it is redisplayed.
1229 *--------------------------------------------------------------
1234 ClientData clientData
, /* Information about window. */
1235 XEvent
*eventPtr
/* Information about event. */
1238 Entry
*entryPtr
= (Entry
*) clientData
;
1239 if (eventPtr
->type
== Expose
) {
1240 EventuallyRedraw(entryPtr
);
1241 entryPtr
->flags
|= BORDER_NEEDED
;
1242 } else if (eventPtr
->type
== DestroyNotify
) {
1243 Tcl_DeleteCommand(entryPtr
->interp
, Tk_PathName(entryPtr
->tkwin
));
1244 entryPtr
->tkwin
= NULL
;
1245 if (entryPtr
->flags
& REDRAW_PENDING
) {
1246 Tk_CancelIdleCall(DisplayEntry
, (ClientData
) entryPtr
);
1248 Tk_EventuallyFree((ClientData
) entryPtr
, DestroyEntry
);
1249 } else if (eventPtr
->type
== ConfigureNotify
) {
1250 Tk_Preserve((ClientData
) entryPtr
);
1251 EventuallyRedraw(entryPtr
);
1252 EntryUpdateScrollbar(entryPtr
);
1253 Tk_Release((ClientData
) entryPtr
);
1258 *--------------------------------------------------------------
1262 * Parse an index into an entry and return either its value
1266 * A standard Tcl result. If all went well, then *indexPtr is
1267 * filled in with the index (into entryPtr) corresponding to
1268 * string. The index value is guaranteed to lie between 0 and
1269 * the number of characters in the string, inclusive. If an
1270 * error occurs then an error message is left in interp->result.
1275 *--------------------------------------------------------------
1280 Tcl_Interp
*interp
, /* For error messages. */
1281 Entry
*entryPtr
, /* Entry for which the index is being
1283 char *string
, /* Specifies character in entryPtr. */
1284 int *indexPtr
/* Where to store converted index. */
1289 length
= strlen(string
);
1291 if (string
[0] == 'e') {
1292 if (strncmp(string
, "end", length
) == 0) {
1293 *indexPtr
= entryPtr
->numChars
;
1298 * Some of the paths here leave messages in interp->result,
1299 * so we have to clear it out before storing our own message.
1302 Tcl_SetResult(interp
, (char *) NULL
, TCL_STATIC
);
1303 Tcl_AppendResult(interp
, "bad entry index \"", string
,
1304 "\"", (char *) NULL
);
1307 } else if (string
[0] == 'c') {
1308 if (strncmp(string
, "cursor", length
) == 0) {
1309 *indexPtr
= entryPtr
->cursorPos
;
1313 } else if (string
[0] == 's') {
1314 if (entryPtr
->selectFirst
== -1) {
1315 interp
->result
= "selection isn't in entry";
1321 if (strncmp(string
, "sel.first", length
) == 0) {
1322 *indexPtr
= entryPtr
->selectFirst
;
1323 } else if (strncmp(string
, "sel.last", length
) == 0) {
1324 *indexPtr
= entryPtr
->selectLast
;
1328 } else if (string
[0] == '@') {
1331 if (Tcl_GetInt(interp
, string
+1, &x
) != TCL_OK
) {
1334 if (entryPtr
->numChars
== 0) {
1337 *indexPtr
= entryPtr
->leftIndex
+ TkMeasureChars(entryPtr
->fontPtr
,
1338 entryPtr
->string
+ entryPtr
->leftIndex
,
1339 entryPtr
->numChars
- entryPtr
->leftIndex
,
1340 entryPtr
->offset
, x
, TK_NEWLINES_NOT_SPECIAL
, &dummy
);
1343 if (Tcl_GetInt(interp
, string
, indexPtr
) != TCL_OK
) {
1348 } else if (*indexPtr
> entryPtr
->numChars
) {
1349 *indexPtr
= entryPtr
->numChars
;
1356 *----------------------------------------------------------------------
1360 * Given a y-coordinate (presumably of the curent mouse location)
1361 * drag the view in the window to implement the scan operation.
1367 * The view in the window may change.
1369 *----------------------------------------------------------------------
1374 register Entry
*entryPtr
, /* Information about widget. */
1375 int x
/* X-coordinate to use for scan
1382 * Compute new leftIndex for entry by amplifying the difference
1383 * between the current position and the place where the scan
1384 * started (the "mark" position). If we run off the left or right
1385 * side of the entry, then reset the mark point so that the current
1386 * position continues to correspond to the edge of the window.
1387 * This means that the picture will start dragging as soon as the
1388 * mouse reverses direction (without this reset, might have to slide
1389 * mouse a long ways back before the picture starts moving again).
1392 newLeftIndex
= entryPtr
->scanMarkIndex
1393 - (10*(x
- entryPtr
->scanMarkX
))/entryPtr
->avgWidth
;
1394 if (newLeftIndex
>= entryPtr
->numChars
) {
1395 newLeftIndex
= entryPtr
->scanMarkIndex
= entryPtr
->numChars
-1;
1396 entryPtr
->scanMarkX
= x
;
1398 if (newLeftIndex
< 0) {
1399 newLeftIndex
= entryPtr
->scanMarkIndex
= 0;
1400 entryPtr
->scanMarkX
= x
;
1402 if (newLeftIndex
!= entryPtr
->leftIndex
) {
1403 entryPtr
->leftIndex
= newLeftIndex
;
1404 EventuallyRedraw(entryPtr
);
1405 EntryUpdateScrollbar(entryPtr
);
1410 *----------------------------------------------------------------------
1414 * Modify the selection by moving its un-anchored end. This could
1415 * make the selection either larger or smaller.
1421 * The selection changes.
1423 *----------------------------------------------------------------------
1428 register Entry
*entryPtr
, /* Information about widget. */
1429 int index
/* Index of element that is to
1430 * become the "other" end of the
1434 int newFirst
, newLast
;
1437 * Grab the selection if we don't own it already.
1440 if ((entryPtr
->selectFirst
== -1) && (entryPtr
->exportSelection
)) {
1441 Tk_OwnSelection(entryPtr
->tkwin
, EntryLostSelection
,
1442 (ClientData
) entryPtr
);
1448 if (index
>= entryPtr
->numChars
) {
1449 index
= entryPtr
->numChars
-1;
1451 if (entryPtr
->selectAnchor
> entryPtr
->numChars
) {
1452 entryPtr
->selectAnchor
= entryPtr
->numChars
;
1454 if (entryPtr
->selectAnchor
<= index
) {
1455 newFirst
= entryPtr
->selectAnchor
;
1459 newLast
= entryPtr
->selectAnchor
- 1;
1461 newFirst
= newLast
= -1;
1464 if ((entryPtr
->selectFirst
== newFirst
)
1465 && (entryPtr
->selectLast
== newLast
)) {
1468 entryPtr
->selectFirst
= newFirst
;
1469 entryPtr
->selectLast
= newLast
;
1470 EventuallyRedraw(entryPtr
);
1474 *----------------------------------------------------------------------
1476 * EntryFetchSelection --
1478 * This procedure is called back by Tk when the selection is
1479 * requested by someone. It returns part or all of the selection
1480 * in a buffer provided by the caller.
1483 * The return value is the number of non-NULL bytes stored
1484 * at buffer. Buffer is filled (or partially filled) with a
1485 * NULL-terminated string containing part or all of the selection,
1486 * as given by offset and maxBytes.
1491 *----------------------------------------------------------------------
1495 EntryFetchSelection (
1496 ClientData clientData
, /* Information about entry widget. */
1497 int offset
, /* Offset within selection of first
1498 * character to be returned. */
1499 char *buffer
, /* Location in which to place
1501 int maxBytes
/* Maximum number of bytes to place
1502 * at buffer, not including terminating
1503 * NULL character. */
1506 Entry
*entryPtr
= (Entry
*) clientData
;
1509 if ((entryPtr
->selectFirst
< 0) || !(entryPtr
->exportSelection
)) {
1512 count
= entryPtr
->selectLast
+ 1 - entryPtr
->selectFirst
- offset
;
1513 if (count
> maxBytes
) {
1519 strncpy(buffer
, entryPtr
->string
+ entryPtr
->selectFirst
+ offset
, count
);
1520 buffer
[count
] = '\0';
1525 *----------------------------------------------------------------------
1527 * EntryLostSelection --
1529 * This procedure is called back by Tk when the selection is
1530 * grabbed away from an entry widget.
1536 * The existing selection is unhighlighted, and the window is
1537 * marked as not containing a selection.
1539 *----------------------------------------------------------------------
1543 EntryLostSelection (
1544 ClientData clientData
/* Information about entry widget. */
1547 Entry
*entryPtr
= (Entry
*) clientData
;
1549 if ((entryPtr
->selectFirst
!= -1) && entryPtr
->exportSelection
) {
1550 entryPtr
->selectFirst
= -1;
1551 entryPtr
->selectLast
= -1;
1552 EventuallyRedraw(entryPtr
);
1557 *----------------------------------------------------------------------
1559 * EventuallyRedraw --
1561 * Ensure that an entry is eventually redrawn on the display.
1567 * Information gets redisplayed. Right now we don't do selective
1568 * redisplays: the whole window will be redrawn. This doesn't
1569 * seem to hurt performance noticeably, but if it does then this
1572 *----------------------------------------------------------------------
1577 register Entry
*entryPtr
/* Information about widget. */
1580 if ((entryPtr
->tkwin
== NULL
) || !Tk_IsMapped(entryPtr
->tkwin
)) {
1585 * Right now we don't do selective redisplays: the whole window
1586 * will be redrawn. This doesn't seem to hurt performance noticeably,
1587 * but if it does then this could be changed.
1590 if (!(entryPtr
->flags
& REDRAW_PENDING
)) {
1591 entryPtr
->flags
|= REDRAW_PENDING
;
1592 Tk_DoWhenIdle(DisplayEntry
, (ClientData
) entryPtr
);
1597 *----------------------------------------------------------------------
1599 * EntryUpdateScrollbar --
1601 * This procedure is invoked whenever information has changed in
1602 * an entry in a way that would invalidate a scrollbar display.
1603 * If there is an associated scrollbar, then this command updates
1604 * it by invoking a Tcl command.
1610 * A Tcl command is invoked, and an additional command may be
1611 * invoked to process errors in the command.
1613 *----------------------------------------------------------------------
1617 EntryUpdateScrollbar (
1618 register Entry
*entryPtr
/* Information about widget. */
1622 int result
, last
, charsInWindow
, endX
;
1624 if (entryPtr
->scrollCmd
== NULL
) {
1629 * The most painful part here is guessing how many characters
1630 * actually fit in the window. This is only an estimate in the
1631 * case where the window isn't completely filled with characters.
1634 charsInWindow
= TkMeasureChars(entryPtr
->fontPtr
,
1635 entryPtr
->string
+ entryPtr
->leftIndex
,
1636 entryPtr
->numChars
- entryPtr
->leftIndex
, entryPtr
->offset
,
1637 Tk_Width(entryPtr
->tkwin
),
1638 TK_AT_LEAST_ONE
|TK_NEWLINES_NOT_SPECIAL
, &endX
);
1639 if (charsInWindow
== 0) {
1640 last
= entryPtr
->leftIndex
;
1642 last
= entryPtr
->leftIndex
+ charsInWindow
- 1;
1644 if (endX
< Tk_Width(entryPtr
->tkwin
)) {
1645 charsInWindow
+= (Tk_Width(entryPtr
->tkwin
) - endX
)/entryPtr
->avgWidth
;
1647 sprintf(args
, " %d %d %d %d", entryPtr
->numChars
, charsInWindow
,
1648 entryPtr
->leftIndex
, last
);
1649 result
= Tcl_VarEval(entryPtr
->interp
, entryPtr
->scrollCmd
, args
,
1651 if (result
!= TCL_OK
) {
1652 TkBindError(entryPtr
->interp
);
1654 Tcl_SetResult(entryPtr
->interp
, (char *) NULL
, TCL_STATIC
);
1658 *----------------------------------------------------------------------
1662 * This procedure is called as a timer handler to blink the
1663 * insertion cursor off and on.
1669 * The cursor gets turned on or off, redisplay gets invoked,
1670 * and this procedure reschedules itself.
1672 *----------------------------------------------------------------------
1677 ClientData clientData
/* Pointer to record describing entry. */
1680 register Entry
*entryPtr
= (Entry
*) clientData
;
1682 if (!(entryPtr
->flags
& GOT_FOCUS
) || (entryPtr
->cursorOffTime
== 0)) {
1685 if (entryPtr
->flags
& CURSOR_ON
) {
1686 entryPtr
->flags
&= ~CURSOR_ON
;
1687 entryPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
1688 entryPtr
->cursorOffTime
, EntryBlinkProc
, (ClientData
) entryPtr
);
1690 entryPtr
->flags
|= CURSOR_ON
;
1691 entryPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
1692 entryPtr
->cursorOnTime
, EntryBlinkProc
, (ClientData
) entryPtr
);
1694 EventuallyRedraw(entryPtr
);
1698 *----------------------------------------------------------------------
1702 * This procedure is called whenever the entry gets or loses the
1703 * input focus. It's also called whenever the window is reconfigured
1704 * while it has the focus.
1710 * The cursor gets turned on or off.
1712 *----------------------------------------------------------------------
1717 ClientData clientData
, /* Pointer to structure describing entry. */
1718 int gotFocus
/* 1 means window is getting focus, 0 means
1719 * it's losing it. */
1722 register Entry
*entryPtr
= (Entry
*) clientData
;
1724 if (entryPtr
->cursorBlinkHandler
!= NULL
) {
1725 Tk_DeleteTimerHandler(entryPtr
->cursorBlinkHandler
);
1726 entryPtr
->cursorBlinkHandler
= NULL
;
1729 entryPtr
->flags
|= GOT_FOCUS
| CURSOR_ON
;
1730 if (entryPtr
->cursorOffTime
!= 0) {
1731 entryPtr
->cursorBlinkHandler
= Tk_CreateTimerHandler(
1732 entryPtr
->cursorOnTime
, EntryBlinkProc
,
1733 (ClientData
) entryPtr
);
1736 entryPtr
->flags
&= ~(GOT_FOCUS
| CURSOR_ON
);
1737 entryPtr
->cursorBlinkHandler
= (Tk_TimerToken
) NULL
;
1739 EventuallyRedraw(entryPtr
);
1743 *--------------------------------------------------------------
1745 * EntryTextVarProc --
1747 * This procedure is invoked when someone changes the variable
1748 * whose contents are to be displayed in an entry.
1751 * NULL is always returned.
1754 * The text displayed in the entry will change to match the
1757 *--------------------------------------------------------------
1763 ClientData clientData
, /* Information about button. */
1764 Tcl_Interp
*interp
, /* Interpreter containing variable. */
1765 char *name1
, /* Name of variable. */
1766 char *name2
, /* Second part of variable name. */
1767 int flags
/* Information about what happened. */
1770 register Entry
*entryPtr
= (Entry
*) clientData
;
1774 * If the variable is unset, then immediately recreate it unless
1775 * the whole interpreter is going away.
1778 if (flags
& TCL_TRACE_UNSETS
) {
1779 if ((flags
& TCL_TRACE_DESTROYED
) && !(flags
& TCL_INTERP_DESTROYED
)) {
1780 Tcl_SetVar2(interp
, name1
, name2
, entryPtr
->string
,
1781 flags
& TCL_GLOBAL_ONLY
);
1782 Tcl_TraceVar2(interp
, name1
, name2
,
1783 TCL_GLOBAL_ONLY
|TCL_TRACE_WRITES
|TCL_TRACE_UNSETS
,
1784 EntryTextVarProc
, clientData
);
1786 return (char *) NULL
;
1790 * Update the entry's text with the value of the variable, unless
1791 * the entry already has that value (this happens when the variable
1792 * changes value because we changed it because someone typed in
1796 value
= Tcl_GetVar2(interp
, name1
, name2
, flags
& TCL_GLOBAL_ONLY
);
1797 if (value
== NULL
) {
1800 if (strcmp(value
, entryPtr
->string
) != 0) {
1801 EntrySetValue(entryPtr
, value
);
1803 return (char *) NULL
;