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 (c) 1990-1994 The Regents of the University of California.
9
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
11
* See the file "license.terms" for information on usage and redistribution
12
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14
* RCS: @(#) $Id: tkEntry.c,v 1.2 1998/09/14 18:23:09 stanton Exp $
21
* A data structure of the following type is kept for each entry
22
* widget managed by this file:
26
Tk_Window tkwin; /* Window that embodies the entry. NULL
27
* means that the window has been destroyed
28
* but the data structures haven't yet been
30
Display *display; /* Display containing widget. Used, among
31
* other things, so that resources can be
32
* freed even after tkwin has gone away. */
33
Tcl_Interp *interp; /* Interpreter associated with entry. */
34
Tcl_Command widgetCmd; /* Token for entry's widget command. */
37
* Fields that are set by widget commands other than "configure".
40
char *string; /* Pointer to storage for string;
41
* NULL-terminated; malloc-ed. */
42
int insertPos; /* Index of character before which next
43
* typed character will be inserted. */
46
* Information about what's selected, if any.
49
int selectFirst; /* Index of first selected character (-1 means
50
* nothing selected. */
51
int selectLast; /* Index of last selected character (-1 means
52
* nothing selected. */
53
int selectAnchor; /* Fixed end of selection (i.e. "select to"
54
* operation will use this as one end of the
58
* Information for scanning:
61
int scanMarkX; /* X-position at which scan started (e.g.
62
* button was pressed here). */
63
int scanMarkIndex; /* Index of character that was at left of
64
* window when scan started. */
67
* Configuration settings that are updated by Tk_ConfigureWidget.
70
Tk_3DBorder normalBorder; /* Used for drawing border around whole
71
* window, plus used for background. */
72
int borderWidth; /* Width of 3-D border around window. */
73
Tk_Cursor cursor; /* Current cursor for window, or None. */
74
int exportSelection; /* Non-zero means tie internal entry selection
76
Tk_Font tkfont; /* Information about text font, or NULL. */
77
XColor *fgColorPtr; /* Text color in normal mode. */
78
XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
79
* area when highlight is off. */
80
XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
81
int highlightWidth; /* Width in pixels of highlight to draw
82
* around widget when it has the focus.
83
* <= 0 means don't draw a highlight. */
84
Tk_3DBorder insertBorder; /* Used to draw vertical bar for insertion
86
int insertBorderWidth; /* Width of 3-D border around insert cursor. */
87
int insertOffTime; /* Number of milliseconds cursor should spend
88
* in "off" state for each blink. */
89
int insertOnTime; /* Number of milliseconds cursor should spend
90
* in "on" state for each blink. */
91
int insertWidth; /* Total width of insert cursor. */
92
Tk_Justify justify; /* Justification to use for text within
94
int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
95
Tk_3DBorder selBorder; /* Border and background for selected
97
int selBorderWidth; /* Width of border around selection. */
98
XColor *selFgColorPtr; /* Foreground color for selected text. */
99
char *showChar; /* Value of -show option. If non-NULL, first
100
* character is used for displaying all
101
* characters in entry. Malloc'ed. */
102
Tk_Uid state; /* Normal or disabled. Entry is read-only
104
char *textVarName; /* Name of variable (malloc'ed) or NULL.
105
* If non-NULL, entry's string tracks the
106
* contents of this variable and vice versa. */
107
char *takeFocus; /* Value of -takefocus option; not used in
108
* the C code, but used by keyboard traversal
109
* scripts. Malloc'ed, but may be NULL. */
110
int prefWidth; /* Desired width of window, measured in
111
* average characters. */
112
char *scrollCmd; /* Command prefix for communicating with
113
* scrollbar(s). Malloc'ed. NULL means
114
* no command to issue. */
117
* Fields whose values are derived from the current values of the
118
* configuration settings above.
121
int numChars; /* Number of non-NULL characters in
122
* string (may be 0). */
123
char *displayString; /* If non-NULL, points to string with same
124
* length as string but whose characters
125
* are all equal to showChar. Malloc'ed. */
126
int inset; /* Number of pixels on the left and right
127
* sides that are taken up by XPAD, borderWidth
128
* (if any), and highlightWidth (if any). */
129
Tk_TextLayout textLayout; /* Cached text layout information. */
130
int layoutX, layoutY; /* Origin for layout. */
131
int leftIndex; /* Index of left-most character visible in
133
int leftX; /* X position at which character at leftIndex
134
* is drawn (varies depending on justify). */
135
Tcl_TimerToken insertBlinkHandler;
136
/* Timer handler used to blink cursor on and
138
GC textGC; /* For drawing normal text. */
139
GC selTextGC; /* For drawing selected text. */
140
GC highlightGC; /* For drawing traversal highlight. */
141
int avgWidth; /* Width of average character. */
142
int flags; /* Miscellaneous flags; see below for
147
* Assigned bits of "flags" fields of Entry structures, and what those
150
* REDRAW_PENDING: Non-zero means a DoWhenIdle handler has
151
* already been queued to redisplay the entry.
152
* BORDER_NEEDED: Non-zero means 3-D border must be redrawn
153
* around window during redisplay. Normally
154
* only text portion needs to be redrawn.
155
* CURSOR_ON: Non-zero means insert cursor is displayed at
156
* present. 0 means it isn't displayed.
157
* GOT_FOCUS: Non-zero means this window has the input
159
* UPDATE_SCROLLBAR: Non-zero means scrollbar should be updated
160
* during next redisplay operation.
161
* GOT_SELECTION: Non-zero means we've claimed the selection.
164
#define REDRAW_PENDING 1
165
#define BORDER_NEEDED 2
168
#define UPDATE_SCROLLBAR 0x10
169
#define GOT_SELECTION 0x20
172
* The following macro defines how many extra pixels to leave on each
173
* side of the text in the entry.
180
* Information used for argv parsing.
183
static Tk_ConfigSpec configSpecs[] = {
184
{TK_CONFIG_BORDER, "-background", "background", "Background",
185
DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
186
TK_CONFIG_COLOR_ONLY},
187
{TK_CONFIG_BORDER, "-background", "background", "Background",
188
DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
189
TK_CONFIG_MONO_ONLY},
190
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
191
(char *) NULL, 0, 0},
192
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
193
(char *) NULL, 0, 0},
194
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
195
DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
196
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
197
DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
198
{TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
199
"ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
200
Tk_Offset(Entry, exportSelection), 0},
201
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
202
(char *) NULL, 0, 0},
203
{TK_CONFIG_FONT, "-font", "font", "Font",
204
DEF_ENTRY_FONT, Tk_Offset(Entry, tkfont), 0},
205
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
206
DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
207
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
208
"HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
209
Tk_Offset(Entry, highlightBgColorPtr), 0},
210
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
211
DEF_ENTRY_HIGHLIGHT, Tk_Offset(Entry, highlightColorPtr), 0},
212
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
213
"HighlightThickness",
214
DEF_ENTRY_HIGHLIGHT_WIDTH, Tk_Offset(Entry, highlightWidth), 0},
215
{TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
216
DEF_ENTRY_INSERT_BG, Tk_Offset(Entry, insertBorder), 0},
217
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
218
DEF_ENTRY_INSERT_BD_COLOR, Tk_Offset(Entry, insertBorderWidth),
219
TK_CONFIG_COLOR_ONLY},
220
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
221
DEF_ENTRY_INSERT_BD_MONO, Tk_Offset(Entry, insertBorderWidth),
222
TK_CONFIG_MONO_ONLY},
223
{TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
224
DEF_ENTRY_INSERT_OFF_TIME, Tk_Offset(Entry, insertOffTime), 0},
225
{TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
226
DEF_ENTRY_INSERT_ON_TIME, Tk_Offset(Entry, insertOnTime), 0},
227
{TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
228
DEF_ENTRY_INSERT_WIDTH, Tk_Offset(Entry, insertWidth), 0},
229
{TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
230
DEF_ENTRY_JUSTIFY, Tk_Offset(Entry, justify), 0},
231
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
232
DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
233
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
234
DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
235
TK_CONFIG_COLOR_ONLY},
236
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
237
DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
238
TK_CONFIG_MONO_ONLY},
239
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
240
DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
241
TK_CONFIG_COLOR_ONLY},
242
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
243
DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
244
TK_CONFIG_MONO_ONLY},
245
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
246
DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
247
TK_CONFIG_COLOR_ONLY},
248
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
249
DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
250
TK_CONFIG_MONO_ONLY},
251
{TK_CONFIG_STRING, "-show", "show", "Show",
252
DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
253
{TK_CONFIG_UID, "-state", "state", "State",
254
DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
255
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
256
DEF_ENTRY_TAKE_FOCUS, Tk_Offset(Entry, takeFocus), TK_CONFIG_NULL_OK},
257
{TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
258
DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
260
{TK_CONFIG_INT, "-width", "width", "Width",
261
DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
262
{TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
263
DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd),
265
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
270
* Flags for GetEntryIndex procedure:
274
#define LAST_PLUS_ONE_OK 2
277
* Forward declarations for procedures defined later in this file:
280
static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
281
Entry *entryPtr, int argc, char **argv,
283
static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
285
static void DestroyEntry _ANSI_ARGS_((char *memPtr));
286
static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
287
static void EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
288
static void EntryCmdDeletedProc _ANSI_ARGS_((
289
ClientData clientData));
290
static void EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
291
static void EntryEventProc _ANSI_ARGS_((ClientData clientData,
293
static void EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
295
static int EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
296
int offset, char *buffer, int maxBytes));
297
static void EntryLostSelection _ANSI_ARGS_((
298
ClientData clientData));
299
static void EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
300
static void EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
301
static void EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
303
static void EntrySelectTo _ANSI_ARGS_((
304
Entry *entryPtr, int index));
305
static char * EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
306
Tcl_Interp *interp, char *name1, char *name2,
308
static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
309
static void EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
310
static void EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
311
double *firstPtr, double *lastPtr));
312
static int EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
313
Tcl_Interp *interp, int argc, char **argv));
314
static void EntryWorldChanged _ANSI_ARGS_((
315
ClientData instanceData));
316
static int GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
317
Entry *entryPtr, char *string, int *indexPtr));
318
static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
322
* The structure below defines entry class behavior by means of procedures
323
* that can be invoked from generic window code.
326
static TkClassProcs entryClass = {
327
NULL, /* createProc. */
328
EntryWorldChanged, /* geometryProc. */
329
NULL /* modalProc. */
334
*--------------------------------------------------------------
338
* This procedure is invoked to process the "entry" Tcl
339
* command. See the user documentation for details on what
343
* A standard Tcl result.
346
* See the user documentation.
348
*--------------------------------------------------------------
352
Tk_EntryCmd(clientData, interp, argc, argv)
353
ClientData clientData; /* Main window associated with
355
Tcl_Interp *interp; /* Current interpreter. */
356
int argc; /* Number of arguments. */
357
char **argv; /* Argument strings. */
359
Tk_Window tkwin = (Tk_Window) clientData;
360
register Entry *entryPtr;
364
Tcl_AppendResult(interp, "wrong # args: should be \"",
365
argv[0], " pathName ?options?\"", (char *) NULL);
369
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
375
* Initialize the fields of the structure that won't be initialized
376
* by ConfigureEntry, or that ConfigureEntry requires to be
377
* initialized already (e.g. resource pointers).
380
entryPtr = (Entry *) ckalloc(sizeof(Entry));
381
entryPtr->tkwin = new;
382
entryPtr->display = Tk_Display(new);
383
entryPtr->interp = interp;
384
entryPtr->widgetCmd = Tcl_CreateCommand(interp,
385
Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
386
(ClientData) entryPtr, EntryCmdDeletedProc);
387
entryPtr->string = (char *) ckalloc(1);
388
entryPtr->string[0] = '\0';
389
entryPtr->insertPos = 0;
390
entryPtr->selectFirst = -1;
391
entryPtr->selectLast = -1;
392
entryPtr->selectAnchor = 0;
393
entryPtr->scanMarkX = 0;
394
entryPtr->scanMarkIndex = 0;
396
entryPtr->normalBorder = NULL;
397
entryPtr->borderWidth = 0;
398
entryPtr->cursor = None;
399
entryPtr->exportSelection = 1;
400
entryPtr->tkfont = NULL;
401
entryPtr->fgColorPtr = NULL;
402
entryPtr->highlightBgColorPtr = NULL;
403
entryPtr->highlightColorPtr = NULL;
404
entryPtr->highlightWidth = 0;
405
entryPtr->insertBorder = NULL;
406
entryPtr->insertBorderWidth = 0;
407
entryPtr->insertOffTime = 0;
408
entryPtr->insertOnTime = 0;
409
entryPtr->insertWidth = 0;
410
entryPtr->justify = TK_JUSTIFY_LEFT;
411
entryPtr->relief = TK_RELIEF_FLAT;
412
entryPtr->selBorder = NULL;
413
entryPtr->selBorderWidth = 0;
414
entryPtr->selFgColorPtr = NULL;
415
entryPtr->showChar = NULL;
416
entryPtr->state = tkNormalUid;
417
entryPtr->textVarName = NULL;
418
entryPtr->takeFocus = NULL;
419
entryPtr->prefWidth = 0;
420
entryPtr->scrollCmd = NULL;
422
entryPtr->numChars = 0;
423
entryPtr->displayString = NULL;
424
entryPtr->inset = XPAD;
425
entryPtr->textLayout = NULL;
426
entryPtr->layoutX = 0;
427
entryPtr->layoutY = 0;
428
entryPtr->leftIndex = 0;
430
entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
431
entryPtr->textGC = None;
432
entryPtr->selTextGC = None;
433
entryPtr->highlightGC = None;
434
entryPtr->avgWidth = 1;
437
Tk_SetClass(entryPtr->tkwin, "Entry");
438
TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
439
Tk_CreateEventHandler(entryPtr->tkwin,
440
ExposureMask|StructureNotifyMask|FocusChangeMask,
441
EntryEventProc, (ClientData) entryPtr);
442
Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
443
EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
444
if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
448
interp->result = Tk_PathName(entryPtr->tkwin);
452
Tk_DestroyWindow(entryPtr->tkwin);
457
*--------------------------------------------------------------
461
* This procedure is invoked to process the Tcl command
462
* that corresponds to a widget managed by this module.
463
* See the user documentation for details on what it does.
466
* A standard Tcl result.
469
* See the user documentation.
471
*--------------------------------------------------------------
475
EntryWidgetCmd(clientData, interp, argc, argv)
476
ClientData clientData; /* Information about entry widget. */
477
Tcl_Interp *interp; /* Current interpreter. */
478
int argc; /* Number of arguments. */
479
char **argv; /* Argument strings. */
481
register Entry *entryPtr = (Entry *) clientData;
487
Tcl_AppendResult(interp, "wrong # args: should be \"",
488
argv[0], " option ?arg arg ...?\"", (char *) NULL);
491
Tcl_Preserve((ClientData) entryPtr);
493
length = strlen(argv[1]);
494
if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
496
int x, y, width, height;
499
Tcl_AppendResult(interp, "wrong # args: should be \"",
500
argv[0], " bbox index\"",
504
if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
507
if ((index == entryPtr->numChars) && (index > 0)) {
510
Tk_CharBbox(entryPtr->textLayout, index, &x, &y, &width, &height);
511
sprintf(interp->result, "%d %d %d %d",
512
x + entryPtr->layoutX, y + entryPtr->layoutY, width, height);
513
} else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
516
Tcl_AppendResult(interp, "wrong # args: should be \"",
517
argv[0], " cget option\"",
521
result = Tk_ConfigureValue(interp, entryPtr->tkwin, configSpecs,
522
(char *) entryPtr, argv[2], 0);
523
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
526
result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
527
(char *) entryPtr, (char *) NULL, 0);
528
} else if (argc == 3) {
529
result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
530
(char *) entryPtr, argv[2], 0);
532
result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
533
TK_CONFIG_ARGV_ONLY);
535
} else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
538
if ((argc < 3) || (argc > 4)) {
539
Tcl_AppendResult(interp, "wrong # args: should be \"",
540
argv[0], " delete firstIndex ?lastIndex?\"",
544
if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
550
if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
554
if ((last >= first) && (entryPtr->state == tkNormalUid)) {
555
DeleteChars(entryPtr, first, last-first);
557
} else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
559
Tcl_AppendResult(interp, "wrong # args: should be \"",
560
argv[0], " get\"", (char *) NULL);
563
interp->result = entryPtr->string;
564
} else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
567
Tcl_AppendResult(interp, "wrong # args: should be \"",
568
argv[0], " icursor pos\"",
572
if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
576
EventuallyRedraw(entryPtr);
577
} else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
582
Tcl_AppendResult(interp, "wrong # args: should be \"",
583
argv[0], " index string\"", (char *) NULL);
586
if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
589
sprintf(interp->result, "%d", index);
590
} else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
595
Tcl_AppendResult(interp, "wrong # args: should be \"",
596
argv[0], " insert index text\"",
600
if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
603
if (entryPtr->state == tkNormalUid) {
604
InsertChars(entryPtr, index, argv[3]);
606
} else if ((c == 's') && (length >= 2)
607
&& (strncmp(argv[1], "scan", length) == 0)) {
611
Tcl_AppendResult(interp, "wrong # args: should be \"",
612
argv[0], " scan mark|dragto x\"", (char *) NULL);
615
if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
618
if ((argv[2][0] == 'm')
619
&& (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
620
entryPtr->scanMarkX = x;
621
entryPtr->scanMarkIndex = entryPtr->leftIndex;
622
} else if ((argv[2][0] == 'd')
623
&& (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
624
EntryScanTo(entryPtr, x);
626
Tcl_AppendResult(interp, "bad scan option \"", argv[2],
627
"\": must be mark or dragto", (char *) NULL);
630
} else if ((c == 's') && (length >= 2)
631
&& (strncmp(argv[1], "selection", length) == 0)) {
635
Tcl_AppendResult(interp, "wrong # args: should be \"",
636
argv[0], " select option ?index?\"", (char *) NULL);
639
length = strlen(argv[2]);
641
if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
643
Tcl_AppendResult(interp, "wrong # args: should be \"",
644
argv[0], " selection clear\"", (char *) NULL);
647
if (entryPtr->selectFirst != -1) {
648
entryPtr->selectFirst = entryPtr->selectLast = -1;
649
EventuallyRedraw(entryPtr);
652
} else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
654
Tcl_AppendResult(interp, "wrong # args: should be \"",
655
argv[0], " selection present\"", (char *) NULL);
658
if (entryPtr->selectFirst == -1) {
659
interp->result = "0";
661
interp->result = "1";
666
if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
670
if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
672
Tcl_AppendResult(interp, "wrong # args: should be \"",
673
argv[0], " selection adjust index\"",
677
if (entryPtr->selectFirst >= 0) {
680
half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
681
half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
683
entryPtr->selectAnchor = entryPtr->selectLast;
684
} else if (index > half2) {
685
entryPtr->selectAnchor = entryPtr->selectFirst;
688
* We're at about the halfway point in the selection;
689
* just keep the existing anchor.
693
EntrySelectTo(entryPtr, index);
694
} else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
696
Tcl_AppendResult(interp, "wrong # args: should be \"",
697
argv[0], " selection from index\"",
701
entryPtr->selectAnchor = index;
702
} else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
704
Tcl_AppendResult(interp, "wrong # args: should be \"",
705
argv[0], " selection range start end\"",
709
if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
712
if (index >= index2) {
713
entryPtr->selectFirst = entryPtr->selectLast = -1;
715
entryPtr->selectFirst = index;
716
entryPtr->selectLast = index2;
718
if (!(entryPtr->flags & GOT_SELECTION)
719
&& (entryPtr->exportSelection)) {
720
Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
721
EntryLostSelection, (ClientData) entryPtr);
722
entryPtr->flags |= GOT_SELECTION;
724
EventuallyRedraw(entryPtr);
725
} else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
727
Tcl_AppendResult(interp, "wrong # args: should be \"",
728
argv[0], " selection to index\"",
732
EntrySelectTo(entryPtr, index);
734
Tcl_AppendResult(interp, "bad selection option \"", argv[2],
735
"\": must be adjust, clear, from, present, range, or to",
739
} else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
740
int index, type, count, charsPerPage;
741
double fraction, first, last;
744
EntryVisibleRange(entryPtr, &first, &last);
745
sprintf(interp->result, "%g %g", first, last);
747
} else if (argc == 3) {
748
if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
752
type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
753
index = entryPtr->leftIndex;
755
case TK_SCROLL_ERROR:
757
case TK_SCROLL_MOVETO:
758
index = (int) ((fraction * entryPtr->numChars) + 0.5);
760
case TK_SCROLL_PAGES:
761
charsPerPage = ((Tk_Width(entryPtr->tkwin)
762
- 2*entryPtr->inset) / entryPtr->avgWidth) - 2;
763
if (charsPerPage < 1) {
766
index += charsPerPage*count;
768
case TK_SCROLL_UNITS:
773
if (index >= entryPtr->numChars) {
774
index = entryPtr->numChars-1;
779
entryPtr->leftIndex = index;
780
entryPtr->flags |= UPDATE_SCROLLBAR;
781
EntryComputeGeometry(entryPtr);
782
EventuallyRedraw(entryPtr);
784
Tcl_AppendResult(interp, "bad option \"", argv[1],
785
"\": must be bbox, cget, configure, delete, get, ",
786
"icursor, index, insert, scan, selection, or xview",
791
Tcl_Release((ClientData) entryPtr);
795
Tcl_Release((ClientData) entryPtr);
800
*----------------------------------------------------------------------
804
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
805
* to clean up the internal structure of an entry at a safe time
806
* (when no-one is using it anymore).
812
* Everything associated with the entry is freed up.
814
*----------------------------------------------------------------------
819
char *memPtr; /* Info about entry widget. */
821
register Entry *entryPtr = (Entry *) memPtr;
824
* Free up all the stuff that requires special handling, then
825
* let Tk_FreeOptions handle all the standard option-related
829
ckfree(entryPtr->string);
830
if (entryPtr->textVarName != NULL) {
831
Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
832
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
833
EntryTextVarProc, (ClientData) entryPtr);
835
if (entryPtr->textGC != None) {
836
Tk_FreeGC(entryPtr->display, entryPtr->textGC);
838
if (entryPtr->selTextGC != None) {
839
Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
841
Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
842
if (entryPtr->displayString != NULL) {
843
ckfree(entryPtr->displayString);
845
Tk_FreeTextLayout(entryPtr->textLayout);
846
Tk_FreeOptions(configSpecs, (char *) entryPtr, entryPtr->display, 0);
847
ckfree((char *) entryPtr);
851
*----------------------------------------------------------------------
855
* This procedure is called to process an argv/argc list, plus
856
* the Tk option database, in order to configure (or reconfigure)
860
* The return value is a standard Tcl result. If TCL_ERROR is
861
* returned, then interp->result contains an error message.
864
* Configuration information, such as colors, border width,
865
* etc. get set for entryPtr; old resources get freed,
868
*----------------------------------------------------------------------
872
ConfigureEntry(interp, entryPtr, argc, argv, flags)
873
Tcl_Interp *interp; /* Used for error reporting. */
874
register Entry *entryPtr; /* Information about widget; may or may
875
* not already have values for some fields. */
876
int argc; /* Number of valid entries in argv. */
877
char **argv; /* Arguments. */
878
int flags; /* Flags to pass to Tk_ConfigureWidget. */
883
* Eliminate any existing trace on a variable monitored by the entry.
886
if (entryPtr->textVarName != NULL) {
887
Tcl_UntraceVar(interp, entryPtr->textVarName,
888
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
889
EntryTextVarProc, (ClientData) entryPtr);
892
oldExport = entryPtr->exportSelection;
893
if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
894
argc, argv, (char *) entryPtr, flags) != TCL_OK) {
899
* If the entry is tied to the value of a variable, then set up
900
* a trace on the variable's value, create the variable if it doesn't
901
* exist, and set the entry's value from the variable's value.
904
if (entryPtr->textVarName != NULL) {
907
value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
909
EntryValueChanged(entryPtr);
911
EntrySetValue(entryPtr, value);
913
Tcl_TraceVar(interp, entryPtr->textVarName,
914
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
915
EntryTextVarProc, (ClientData) entryPtr);
919
* A few other options also need special processing, such as parsing
920
* the geometry and setting the background from a 3-D border.
923
if ((entryPtr->state != tkNormalUid)
924
&& (entryPtr->state != tkDisabledUid)) {
925
Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
926
"\": must be normal or disabled", (char *) NULL);
927
entryPtr->state = tkNormalUid;
931
Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
933
if (entryPtr->insertWidth <= 0) {
934
entryPtr->insertWidth = 2;
936
if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
937
entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
941
* Restart the cursor timing sequence in case the on-time or off-time
945
if (entryPtr->flags & GOT_FOCUS) {
946
EntryFocusProc(entryPtr, 1);
950
* Claim the selection if we've suddenly started exporting it.
953
if (entryPtr->exportSelection && (!oldExport)
954
&& (entryPtr->selectFirst != -1)
955
&& !(entryPtr->flags & GOT_SELECTION)) {
956
Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
957
(ClientData) entryPtr);
958
entryPtr->flags |= GOT_SELECTION;
962
* Recompute the window's geometry and arrange for it to be
966
Tk_SetInternalBorder(entryPtr->tkwin,
967
entryPtr->borderWidth + entryPtr->highlightWidth);
968
if (entryPtr->highlightWidth <= 0) {
969
entryPtr->highlightWidth = 0;
971
entryPtr->inset = entryPtr->highlightWidth + entryPtr->borderWidth + XPAD;
973
EntryWorldChanged((ClientData) entryPtr);
978
*---------------------------------------------------------------------------
980
* EntryWorldChanged --
982
* This procedure is called when the world has changed in some
983
* way and the widget needs to recompute all its graphics contexts
984
* and determine its new geometry.
990
* Entry will be relayed out and redisplayed.
992
*---------------------------------------------------------------------------
996
EntryWorldChanged(instanceData)
997
ClientData instanceData; /* Information about widget. */
1004
entryPtr = (Entry *) instanceData;
1006
entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
1007
if (entryPtr->avgWidth == 0) {
1008
entryPtr->avgWidth = 1;
1011
gcValues.foreground = entryPtr->fgColorPtr->pixel;
1012
gcValues.font = Tk_FontId(entryPtr->tkfont);
1013
gcValues.graphics_exposures = False;
1014
mask = GCForeground | GCFont | GCGraphicsExposures;
1015
gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1016
if (entryPtr->textGC != None) {
1017
Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1019
entryPtr->textGC = gc;
1021
gcValues.foreground = entryPtr->selFgColorPtr->pixel;
1022
gcValues.font = Tk_FontId(entryPtr->tkfont);
1023
mask = GCForeground | GCFont;
1024
gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1025
if (entryPtr->selTextGC != None) {
1026
Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1028
entryPtr->selTextGC = gc;
1031
* Recompute the window's geometry and arrange for it to be
1035
EntryComputeGeometry(entryPtr);
1036
entryPtr->flags |= UPDATE_SCROLLBAR;
1037
EventuallyRedraw(entryPtr);
1041
*--------------------------------------------------------------
1045
* This procedure redraws the contents of an entry window.
1051
* Information appears on the screen.
1053
*--------------------------------------------------------------
1057
DisplayEntry(clientData)
1058
ClientData clientData; /* Information about window. */
1060
register Entry *entryPtr = (Entry *) clientData;
1061
register Tk_Window tkwin = entryPtr->tkwin;
1062
int baseY, selStartX, selEndX, cursorX, x, w;
1068
entryPtr->flags &= ~REDRAW_PENDING;
1069
if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1073
Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1076
* Update the scrollbar if that's needed.
1079
if (entryPtr->flags & UPDATE_SCROLLBAR) {
1080
entryPtr->flags &= ~UPDATE_SCROLLBAR;
1081
EntryUpdateScrollbar(entryPtr);
1085
* In order to avoid screen flashes, this procedure redraws the
1086
* textual area of the entry into off-screen memory, then copies
1087
* it back on-screen in a single operation. This means there's
1088
* no point in time where the on-screen image has been cleared.
1091
pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
1092
Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1095
* Compute x-coordinate of the pixel just after last visible
1096
* one, plus vertical position of baseline of text.
1099
xBound = Tk_Width(tkwin) - entryPtr->inset;
1100
baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
1103
* On Windows and Mac, we need to hide the selection whenever we
1104
* don't have the focus.
1107
#ifdef ALWAYS_SHOW_SELECTION
1110
showSelection = (entryPtr->flags & GOT_FOCUS);
1114
* Draw the background in three layers. From bottom to top the
1115
* layers are: normal background, selection background, and
1116
* insertion cursor background.
1119
Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1120
0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1121
if (showSelection && (entryPtr->selectLast > entryPtr->leftIndex)) {
1122
if (entryPtr->selectFirst <= entryPtr->leftIndex) {
1123
selStartX = entryPtr->leftX;
1125
Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,
1126
&x, NULL, NULL, NULL);
1127
selStartX = x + entryPtr->layoutX;
1129
if ((selStartX - entryPtr->selBorderWidth) < xBound) {
1130
Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast - 1,
1131
&x, NULL, &w, NULL);
1132
selEndX = x + w + entryPtr->layoutX;
1133
Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
1134
selStartX - entryPtr->selBorderWidth,
1135
baseY - fm.ascent - entryPtr->selBorderWidth,
1136
(selEndX - selStartX) + 2*entryPtr->selBorderWidth,
1137
(fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,
1138
entryPtr->selBorderWidth, TK_RELIEF_RAISED);
1143
* Draw a special background for the insertion cursor, overriding
1144
* even the selection background. As a special hack to keep the
1145
* cursor visible when the insertion cursor color is the same as
1146
* the color for selected text (e.g., on mono displays), write
1147
* background in the cursor area (instead of nothing) when the
1148
* cursor isn't on. Otherwise the selection would hide the cursor.
1151
if ((entryPtr->insertPos >= entryPtr->leftIndex)
1152
&& (entryPtr->state == tkNormalUid)
1153
&& (entryPtr->flags & GOT_FOCUS)) {
1154
if (entryPtr->insertPos == 0) {
1156
} else if (entryPtr->insertPos >= entryPtr->numChars) {
1157
Tk_CharBbox(entryPtr->textLayout, entryPtr->numChars - 1,
1158
&x, NULL, &w, NULL);
1161
Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos,
1162
&x, NULL, NULL, NULL);
1165
cursorX += entryPtr->layoutX;
1166
cursorX -= (entryPtr->insertWidth)/2;
1167
if (cursorX < xBound) {
1168
if (entryPtr->flags & CURSOR_ON) {
1169
Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
1170
cursorX, baseY - fm.ascent,
1171
entryPtr->insertWidth, fm.ascent + fm.descent,
1172
entryPtr->insertBorderWidth, TK_RELIEF_RAISED);
1173
} else if (entryPtr->insertBorder == entryPtr->selBorder) {
1174
Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1175
cursorX, baseY - fm.ascent,
1176
entryPtr->insertWidth, fm.ascent + fm.descent,
1183
* Draw the text in two pieces: first the unselected portion, then the
1184
* selected portion on top of it.
1187
Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
1188
entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1189
entryPtr->leftIndex, entryPtr->numChars);
1191
if (showSelection && (entryPtr->selTextGC != entryPtr->textGC) &&
1192
(entryPtr->selectFirst < entryPtr->selectLast)) {
1195
if (entryPtr->selectFirst - entryPtr->leftIndex < 0) {
1196
first = entryPtr->leftIndex;
1198
first = entryPtr->selectFirst;
1200
Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
1201
entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1202
first, entryPtr->selectLast);
1206
* Draw the border and focus highlight last, so they will overwrite
1207
* any text that extends past the viewable part of the window.
1210
if (entryPtr->relief != TK_RELIEF_FLAT) {
1211
Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1212
entryPtr->highlightWidth, entryPtr->highlightWidth,
1213
Tk_Width(tkwin) - 2*entryPtr->highlightWidth,
1214
Tk_Height(tkwin) - 2*entryPtr->highlightWidth,
1215
entryPtr->borderWidth, entryPtr->relief);
1217
if (entryPtr->highlightWidth != 0) {
1220
if (entryPtr->flags & GOT_FOCUS) {
1221
gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
1223
gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
1225
Tk_DrawFocusHighlight(tkwin, gc, entryPtr->highlightWidth, pixmap);
1229
* Everything's been redisplayed; now copy the pixmap onto the screen
1230
* and free up the pixmap.
1233
XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
1234
0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
1236
Tk_FreePixmap(entryPtr->display, pixmap);
1237
entryPtr->flags &= ~BORDER_NEEDED;
1241
*----------------------------------------------------------------------
1243
* EntryComputeGeometry --
1245
* This procedure is invoked to recompute information about where
1246
* in its window an entry's string will be displayed. It also
1247
* computes the requested size for the window.
1253
* The leftX and tabOrigin fields are recomputed for entryPtr,
1254
* and leftIndex may be adjusted. Tk_GeometryRequest is called
1255
* to register the desired dimensions for the window.
1257
*----------------------------------------------------------------------
1261
EntryComputeGeometry(entryPtr)
1262
Entry *entryPtr; /* Widget record for entry. */
1264
int totalLength, overflow, maxOffScreen, rightX;
1265
int height, width, i;
1267
char *p, *displayString;
1270
* If we're displaying a special character instead of the value of
1271
* the entry, recompute the displayString.
1274
if (entryPtr->displayString != NULL) {
1275
ckfree(entryPtr->displayString);
1276
entryPtr->displayString = NULL;
1278
if (entryPtr->showChar != NULL) {
1279
entryPtr->displayString = (char *) ckalloc((unsigned)
1280
(entryPtr->numChars + 1));
1281
for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
1283
*p = entryPtr->showChar[0];
1286
displayString = entryPtr->displayString;
1288
displayString = entryPtr->string;
1290
Tk_FreeTextLayout(entryPtr->textLayout);
1291
entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
1292
displayString, entryPtr->numChars, 0, entryPtr->justify,
1293
TK_IGNORE_NEWLINES, &totalLength, &height);
1295
entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
1298
* Recompute where the leftmost character on the display will
1299
* be drawn (entryPtr->leftX) and adjust leftIndex if necessary
1300
* so that we don't let characters hang off the edge of the
1301
* window unless the entire window is full.
1304
overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
1305
if (overflow <= 0) {
1306
entryPtr->leftIndex = 0;
1307
if (entryPtr->justify == TK_JUSTIFY_LEFT) {
1308
entryPtr->leftX = entryPtr->inset;
1309
} else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
1310
entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
1313
entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
1315
entryPtr->layoutX = entryPtr->leftX;
1318
* The whole string can't fit in the window. Compute the
1319
* maximum number of characters that may be off-screen to
1320
* the left without leaving empty space on the right of the
1321
* window, then don't let leftIndex be any greater than that.
1324
maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);
1325
Tk_CharBbox(entryPtr->textLayout, maxOffScreen,
1326
&rightX, NULL, NULL, NULL);
1327
if (rightX < overflow) {
1330
if (entryPtr->leftIndex > maxOffScreen) {
1331
entryPtr->leftIndex = maxOffScreen;
1333
Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex,
1334
&rightX, NULL, NULL, NULL);
1335
entryPtr->leftX = entryPtr->inset;
1336
entryPtr->layoutX = entryPtr->leftX - rightX;
1339
Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1340
height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);
1341
if (entryPtr->prefWidth > 0) {
1342
width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
1344
if (totalLength == 0) {
1345
width = entryPtr->avgWidth + 2*entryPtr->inset;
1347
width = totalLength + 2*entryPtr->inset;
1350
Tk_GeometryRequest(entryPtr->tkwin, width, height);
1354
*----------------------------------------------------------------------
1358
* Add new characters to an entry widget.
1364
* New information gets added to entryPtr; it will be redisplayed
1365
* soon, but not necessarily immediately.
1367
*----------------------------------------------------------------------
1371
InsertChars(entryPtr, index, string)
1372
register Entry *entryPtr; /* Entry that is to get the new
1374
int index; /* Add the new elements before this
1376
char *string; /* New characters to add (NULL-terminated
1382
length = strlen(string);
1386
new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
1387
strncpy(new, entryPtr->string, (size_t) index);
1388
strcpy(new+index, string);
1389
strcpy(new+index+length, entryPtr->string+index);
1390
ckfree(entryPtr->string);
1391
entryPtr->string = new;
1392
entryPtr->numChars += length;
1395
* Inserting characters invalidates all indexes into the string.
1396
* Touch up the indexes so that they still refer to the same
1397
* characters (at new positions). When updating the selection
1398
* end-points, don't include the new text in the selection unless
1399
* it was completely surrounded by the selection.
1402
if (entryPtr->selectFirst >= index) {
1403
entryPtr->selectFirst += length;
1405
if (entryPtr->selectLast > index) {
1406
entryPtr->selectLast += length;
1408
if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
1409
entryPtr->selectAnchor += length;
1411
if (entryPtr->leftIndex > index) {
1412
entryPtr->leftIndex += length;
1414
if (entryPtr->insertPos >= index) {
1415
entryPtr->insertPos += length;
1417
EntryValueChanged(entryPtr);
1421
*----------------------------------------------------------------------
1425
* Remove one or more characters from an entry widget.
1431
* Memory gets freed, the entry gets modified and (eventually)
1434
*----------------------------------------------------------------------
1438
DeleteChars(entryPtr, index, count)
1439
register Entry *entryPtr; /* Entry widget to modify. */
1440
int index; /* Index of first character to delete. */
1441
int count; /* How many characters to delete. */
1445
if ((index + count) > entryPtr->numChars) {
1446
count = entryPtr->numChars - index;
1452
new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
1453
strncpy(new, entryPtr->string, (size_t) index);
1454
strcpy(new+index, entryPtr->string+index+count);
1455
ckfree(entryPtr->string);
1456
entryPtr->string = new;
1457
entryPtr->numChars -= count;
1460
* Deleting characters results in the remaining characters being
1461
* renumbered. Update the various indexes into the string to reflect
1465
if (entryPtr->selectFirst >= index) {
1466
if (entryPtr->selectFirst >= (index+count)) {
1467
entryPtr->selectFirst -= count;
1469
entryPtr->selectFirst = index;
1472
if (entryPtr->selectLast >= index) {
1473
if (entryPtr->selectLast >= (index+count)) {
1474
entryPtr->selectLast -= count;
1476
entryPtr->selectLast = index;
1479
if (entryPtr->selectLast <= entryPtr->selectFirst) {
1480
entryPtr->selectFirst = entryPtr->selectLast = -1;
1482
if (entryPtr->selectAnchor >= index) {
1483
if (entryPtr->selectAnchor >= (index+count)) {
1484
entryPtr->selectAnchor -= count;
1486
entryPtr->selectAnchor = index;
1489
if (entryPtr->leftIndex > index) {
1490
if (entryPtr->leftIndex >= (index+count)) {
1491
entryPtr->leftIndex -= count;
1493
entryPtr->leftIndex = index;
1496
if (entryPtr->insertPos >= index) {
1497
if (entryPtr->insertPos >= (index+count)) {
1498
entryPtr->insertPos -= count;
1500
entryPtr->insertPos = index;
1503
EntryValueChanged(entryPtr);
1507
*----------------------------------------------------------------------
1509
* EntryValueChanged --
1511
* This procedure is invoked when characters are inserted into
1512
* an entry or deleted from it. It updates the entry's associated
1513
* variable, if there is one, and does other bookkeeping such
1514
* as arranging for redisplay.
1522
*----------------------------------------------------------------------
1526
EntryValueChanged(entryPtr)
1527
Entry *entryPtr; /* Entry whose value just changed. */
1531
if (entryPtr->textVarName == NULL) {
1534
newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
1535
entryPtr->string, TCL_GLOBAL_ONLY);
1538
if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
1540
* The value of the variable is different than what we asked for.
1541
* This means that a trace on the variable modified it. In this
1542
* case our trace procedure wasn't invoked since the modification
1543
* came while a trace was already active on the variable. So,
1544
* update our value to reflect the variable's latest value.
1547
EntrySetValue(entryPtr, newValue);
1550
* Arrange for redisplay.
1553
entryPtr->flags |= UPDATE_SCROLLBAR;
1554
EntryComputeGeometry(entryPtr);
1555
EventuallyRedraw(entryPtr);
1560
*----------------------------------------------------------------------
1564
* Replace the contents of a text entry with a given value. This
1565
* procedure is invoked when updating the entry from the entry's
1566
* associated variable.
1572
* The string displayed in the entry will change. The selection,
1573
* insertion point, and view may have to be adjusted to keep them
1574
* within the bounds of the new string. Note: this procedure does
1575
* *not* update the entry's associated variable, since that could
1576
* result in an infinite loop.
1578
*----------------------------------------------------------------------
1582
EntrySetValue(entryPtr, value)
1583
register Entry *entryPtr; /* Entry whose value is to be
1585
char *value; /* New text to display in entry. */
1587
ckfree(entryPtr->string);
1588
entryPtr->numChars = strlen(value);
1589
entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
1590
strcpy(entryPtr->string, value);
1591
if (entryPtr->selectFirst != -1) {
1592
if (entryPtr->selectFirst >= entryPtr->numChars) {
1593
entryPtr->selectFirst = entryPtr->selectLast = -1;
1594
} else if (entryPtr->selectLast > entryPtr->numChars) {
1595
entryPtr->selectLast = entryPtr->numChars;
1598
if (entryPtr->leftIndex >= entryPtr->numChars) {
1599
entryPtr->leftIndex = entryPtr->numChars-1;
1600
if (entryPtr->leftIndex < 0) {
1601
entryPtr->leftIndex = 0;
1604
if (entryPtr->insertPos > entryPtr->numChars) {
1605
entryPtr->insertPos = entryPtr->numChars;
1608
entryPtr->flags |= UPDATE_SCROLLBAR;
1609
EntryComputeGeometry(entryPtr);
1610
EventuallyRedraw(entryPtr);
1614
*--------------------------------------------------------------
1618
* This procedure is invoked by the Tk dispatcher for various
1619
* events on entryes.
1625
* When the window gets deleted, internal structures get
1626
* cleaned up. When it gets exposed, it is redisplayed.
1628
*--------------------------------------------------------------
1632
EntryEventProc(clientData, eventPtr)
1633
ClientData clientData; /* Information about window. */
1634
XEvent *eventPtr; /* Information about event. */
1636
Entry *entryPtr = (Entry *) clientData;
1637
if (eventPtr->type == Expose) {
1638
EventuallyRedraw(entryPtr);
1639
entryPtr->flags |= BORDER_NEEDED;
1640
} else if (eventPtr->type == DestroyNotify) {
1641
if (entryPtr->tkwin != NULL) {
1642
entryPtr->tkwin = NULL;
1643
Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
1645
if (entryPtr->flags & REDRAW_PENDING) {
1646
Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1648
Tcl_EventuallyFree((ClientData) entryPtr, DestroyEntry);
1649
} else if (eventPtr->type == ConfigureNotify) {
1650
Tcl_Preserve((ClientData) entryPtr);
1651
entryPtr->flags |= UPDATE_SCROLLBAR;
1652
EntryComputeGeometry(entryPtr);
1653
EventuallyRedraw(entryPtr);
1654
Tcl_Release((ClientData) entryPtr);
1655
} else if (eventPtr->type == FocusIn) {
1656
if (eventPtr->xfocus.detail != NotifyInferior) {
1657
EntryFocusProc(entryPtr, 1);
1659
} else if (eventPtr->type == FocusOut) {
1660
if (eventPtr->xfocus.detail != NotifyInferior) {
1661
EntryFocusProc(entryPtr, 0);
1667
*----------------------------------------------------------------------
1669
* EntryCmdDeletedProc --
1671
* This procedure is invoked when a widget command is deleted. If
1672
* the widget isn't already in the process of being destroyed,
1673
* this command destroys it.
1679
* The widget is destroyed.
1681
*----------------------------------------------------------------------
1685
EntryCmdDeletedProc(clientData)
1686
ClientData clientData; /* Pointer to widget record for widget. */
1688
Entry *entryPtr = (Entry *) clientData;
1689
Tk_Window tkwin = entryPtr->tkwin;
1692
* This procedure could be invoked either because the window was
1693
* destroyed and the command was then deleted (in which case tkwin
1694
* is NULL) or because the command was deleted, and then this procedure
1695
* destroys the widget.
1698
if (tkwin != NULL) {
1699
entryPtr->tkwin = NULL;
1700
Tk_DestroyWindow(tkwin);
1705
*--------------------------------------------------------------
1709
* Parse an index into an entry and return either its value
1713
* A standard Tcl result. If all went well, then *indexPtr is
1714
* filled in with the index (into entryPtr) corresponding to
1715
* string. The index value is guaranteed to lie between 0 and
1716
* the number of characters in the string, inclusive. If an
1717
* error occurs then an error message is left in interp->result.
1722
*--------------------------------------------------------------
1726
GetEntryIndex(interp, entryPtr, string, indexPtr)
1727
Tcl_Interp *interp; /* For error messages. */
1728
Entry *entryPtr; /* Entry for which the index is being
1730
char *string; /* Specifies character in entryPtr. */
1731
int *indexPtr; /* Where to store converted index. */
1735
length = strlen(string);
1737
if (string[0] == 'a') {
1738
if (strncmp(string, "anchor", length) == 0) {
1739
*indexPtr = entryPtr->selectAnchor;
1744
* Some of the paths here leave messages in interp->result,
1745
* so we have to clear it out before storing our own message.
1748
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1749
Tcl_AppendResult(interp, "bad entry index \"", string,
1750
"\"", (char *) NULL);
1753
} else if (string[0] == 'e') {
1754
if (strncmp(string, "end", length) == 0) {
1755
*indexPtr = entryPtr->numChars;
1759
} else if (string[0] == 'i') {
1760
if (strncmp(string, "insert", length) == 0) {
1761
*indexPtr = entryPtr->insertPos;
1765
} else if (string[0] == 's') {
1766
if (entryPtr->selectFirst == -1) {
1767
interp->result = "selection isn't in entry";
1773
if (strncmp(string, "sel.first", length) == 0) {
1774
*indexPtr = entryPtr->selectFirst;
1775
} else if (strncmp(string, "sel.last", length) == 0) {
1776
*indexPtr = entryPtr->selectLast;
1780
} else if (string[0] == '@') {
1783
if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
1786
if (x < entryPtr->inset) {
1787
x = entryPtr->inset;
1790
if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
1791
x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
1794
*indexPtr = Tk_PointToChar(entryPtr->textLayout,
1795
x - entryPtr->layoutX, 0);
1798
* Special trick: if the x-position was off-screen to the right,
1799
* round the index up to refer to the character just after the
1800
* last visible one on the screen. This is needed to enable the
1801
* last character to be selected, for example.
1804
if (roundUp && (*indexPtr < entryPtr->numChars)) {
1808
if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1813
} else if (*indexPtr > entryPtr->numChars) {
1814
*indexPtr = entryPtr->numChars;
1821
*----------------------------------------------------------------------
1825
* Given a y-coordinate (presumably of the curent mouse location)
1826
* drag the view in the window to implement the scan operation.
1832
* The view in the window may change.
1834
*----------------------------------------------------------------------
1838
EntryScanTo(entryPtr, x)
1839
register Entry *entryPtr; /* Information about widget. */
1840
int x; /* X-coordinate to use for scan
1846
* Compute new leftIndex for entry by amplifying the difference
1847
* between the current position and the place where the scan
1848
* started (the "mark" position). If we run off the left or right
1849
* side of the entry, then reset the mark point so that the current
1850
* position continues to correspond to the edge of the window.
1851
* This means that the picture will start dragging as soon as the
1852
* mouse reverses direction (without this reset, might have to slide
1853
* mouse a long ways back before the picture starts moving again).
1856
newLeftIndex = entryPtr->scanMarkIndex
1857
- (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
1858
if (newLeftIndex >= entryPtr->numChars) {
1859
newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
1860
entryPtr->scanMarkX = x;
1862
if (newLeftIndex < 0) {
1863
newLeftIndex = entryPtr->scanMarkIndex = 0;
1864
entryPtr->scanMarkX = x;
1866
if (newLeftIndex != entryPtr->leftIndex) {
1867
entryPtr->leftIndex = newLeftIndex;
1868
entryPtr->flags |= UPDATE_SCROLLBAR;
1869
EntryComputeGeometry(entryPtr);
1870
EventuallyRedraw(entryPtr);
1875
*----------------------------------------------------------------------
1879
* Modify the selection by moving its un-anchored end. This could
1880
* make the selection either larger or smaller.
1886
* The selection changes.
1888
*----------------------------------------------------------------------
1892
EntrySelectTo(entryPtr, index)
1893
register Entry *entryPtr; /* Information about widget. */
1894
int index; /* Index of element that is to
1895
* become the "other" end of the
1898
int newFirst, newLast;
1901
* Grab the selection if we don't own it already.
1904
if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {
1905
Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
1906
(ClientData) entryPtr);
1907
entryPtr->flags |= GOT_SELECTION;
1911
* Pick new starting and ending points for the selection.
1914
if (entryPtr->selectAnchor > entryPtr->numChars) {
1915
entryPtr->selectAnchor = entryPtr->numChars;
1917
if (entryPtr->selectAnchor <= index) {
1918
newFirst = entryPtr->selectAnchor;
1922
newLast = entryPtr->selectAnchor;
1924
newFirst = newLast = -1;
1927
if ((entryPtr->selectFirst == newFirst)
1928
&& (entryPtr->selectLast == newLast)) {
1931
entryPtr->selectFirst = newFirst;
1932
entryPtr->selectLast = newLast;
1933
EventuallyRedraw(entryPtr);
1937
*----------------------------------------------------------------------
1939
* EntryFetchSelection --
1941
* This procedure is called back by Tk when the selection is
1942
* requested by someone. It returns part or all of the selection
1943
* in a buffer provided by the caller.
1946
* The return value is the number of non-NULL bytes stored
1947
* at buffer. Buffer is filled (or partially filled) with a
1948
* NULL-terminated string containing part or all of the selection,
1949
* as given by offset and maxBytes.
1954
*----------------------------------------------------------------------
1958
EntryFetchSelection(clientData, offset, buffer, maxBytes)
1959
ClientData clientData; /* Information about entry widget. */
1960
int offset; /* Offset within selection of first
1961
* character to be returned. */
1962
char *buffer; /* Location in which to place
1964
int maxBytes; /* Maximum number of bytes to place
1965
* at buffer, not including terminating
1966
* NULL character. */
1968
Entry *entryPtr = (Entry *) clientData;
1970
char *displayString;
1972
if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
1975
count = entryPtr->selectLast - entryPtr->selectFirst - offset;
1976
if (count > maxBytes) {
1982
if (entryPtr->displayString == NULL) {
1983
displayString = entryPtr->string;
1985
displayString = entryPtr->displayString;
1987
strncpy(buffer, displayString + entryPtr->selectFirst + offset,
1989
buffer[count] = '\0';
1994
*----------------------------------------------------------------------
1996
* EntryLostSelection --
1998
* This procedure is called back by Tk when the selection is
1999
* grabbed away from an entry widget.
2005
* The existing selection is unhighlighted, and the window is
2006
* marked as not containing a selection.
2008
*----------------------------------------------------------------------
2012
EntryLostSelection(clientData)
2013
ClientData clientData; /* Information about entry widget. */
2015
Entry *entryPtr = (Entry *) clientData;
2017
entryPtr->flags &= ~GOT_SELECTION;
2020
* On Windows and Mac systems, we want to remember the selection
2021
* for the next time the focus enters the window. On Unix, we need
2022
* to clear the selection since it is always visible.
2025
#ifdef ALWAYS_SHOW_SELECTION
2026
if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
2027
entryPtr->selectFirst = -1;
2028
entryPtr->selectLast = -1;
2029
EventuallyRedraw(entryPtr);
2035
*----------------------------------------------------------------------
2037
* EventuallyRedraw --
2039
* Ensure that an entry is eventually redrawn on the display.
2045
* Information gets redisplayed. Right now we don't do selective
2046
* redisplays: the whole window will be redrawn. This doesn't
2047
* seem to hurt performance noticeably, but if it does then this
2050
*----------------------------------------------------------------------
2054
EventuallyRedraw(entryPtr)
2055
register Entry *entryPtr; /* Information about widget. */
2057
if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
2062
* Right now we don't do selective redisplays: the whole window
2063
* will be redrawn. This doesn't seem to hurt performance noticeably,
2064
* but if it does then this could be changed.
2067
if (!(entryPtr->flags & REDRAW_PENDING)) {
2068
entryPtr->flags |= REDRAW_PENDING;
2069
Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
2074
*----------------------------------------------------------------------
2076
* EntryVisibleRange --
2078
* Return information about the range of the entry that is
2079
* currently visible.
2082
* *firstPtr and *lastPtr are modified to hold fractions between
2083
* 0 and 1 identifying the range of characters visible in the
2089
*----------------------------------------------------------------------
2093
EntryVisibleRange(entryPtr, firstPtr, lastPtr)
2094
Entry *entryPtr; /* Information about widget. */
2095
double *firstPtr; /* Return position of first visible
2096
* character in widget. */
2097
double *lastPtr; /* Return position of char just after
2098
* last visible one. */
2102
if (entryPtr->numChars == 0) {
2106
charsInWindow = Tk_PointToChar(entryPtr->textLayout,
2107
Tk_Width(entryPtr->tkwin) - entryPtr->inset
2108
- entryPtr->layoutX - 1, 0) + 1;
2109
if (charsInWindow > entryPtr->numChars) {
2111
* If all chars were visible, then charsInWindow will be
2112
* the index just after the last char that was visible.
2115
charsInWindow = entryPtr->numChars;
2117
charsInWindow -= entryPtr->leftIndex;
2118
if (charsInWindow == 0) {
2121
*firstPtr = ((double) entryPtr->leftIndex)/entryPtr->numChars;
2122
*lastPtr = ((double) (entryPtr->leftIndex + charsInWindow))
2123
/entryPtr->numChars;
2128
*----------------------------------------------------------------------
2130
* EntryUpdateScrollbar --
2132
* This procedure is invoked whenever information has changed in
2133
* an entry in a way that would invalidate a scrollbar display.
2134
* If there is an associated scrollbar, then this procedure updates
2135
* it by invoking a Tcl command.
2141
* A Tcl command is invoked, and an additional command may be
2142
* invoked to process errors in the command.
2144
*----------------------------------------------------------------------
2148
EntryUpdateScrollbar(entryPtr)
2149
Entry *entryPtr; /* Information about widget. */
2156
if (entryPtr->scrollCmd == NULL) {
2160
interp = entryPtr->interp;
2161
Tcl_Preserve((ClientData) interp);
2162
EntryVisibleRange(entryPtr, &first, &last);
2163
sprintf(args, " %g %g", first, last);
2164
code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
2165
if (code != TCL_OK) {
2166
Tcl_AddErrorInfo(interp,
2167
"\n (horizontal scrolling command executed by entry)");
2168
Tcl_BackgroundError(interp);
2170
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2171
Tcl_Release((ClientData) interp);
2175
*----------------------------------------------------------------------
2179
* This procedure is called as a timer handler to blink the
2180
* insertion cursor off and on.
2186
* The cursor gets turned on or off, redisplay gets invoked,
2187
* and this procedure reschedules itself.
2189
*----------------------------------------------------------------------
2193
EntryBlinkProc(clientData)
2194
ClientData clientData; /* Pointer to record describing entry. */
2196
register Entry *entryPtr = (Entry *) clientData;
2198
if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
2201
if (entryPtr->flags & CURSOR_ON) {
2202
entryPtr->flags &= ~CURSOR_ON;
2203
entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2204
entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
2206
entryPtr->flags |= CURSOR_ON;
2207
entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2208
entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
2210
EventuallyRedraw(entryPtr);
2214
*----------------------------------------------------------------------
2218
* This procedure is called whenever the entry gets or loses the
2219
* input focus. It's also called whenever the window is reconfigured
2220
* while it has the focus.
2226
* The cursor gets turned on or off.
2228
*----------------------------------------------------------------------
2232
EntryFocusProc(entryPtr, gotFocus)
2233
register Entry *entryPtr; /* Entry that got or lost focus. */
2234
int gotFocus; /* 1 means window is getting focus, 0 means
2235
* it's losing it. */
2237
Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
2239
entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
2240
if (entryPtr->insertOffTime != 0) {
2241
entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2242
entryPtr->insertOnTime, EntryBlinkProc,
2243
(ClientData) entryPtr);
2246
entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
2247
entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
2249
EventuallyRedraw(entryPtr);
2253
*--------------------------------------------------------------
2255
* EntryTextVarProc --
2257
* This procedure is invoked when someone changes the variable
2258
* whose contents are to be displayed in an entry.
2261
* NULL is always returned.
2264
* The text displayed in the entry will change to match the
2267
*--------------------------------------------------------------
2272
EntryTextVarProc(clientData, interp, name1, name2, flags)
2273
ClientData clientData; /* Information about button. */
2274
Tcl_Interp *interp; /* Interpreter containing variable. */
2275
char *name1; /* Not used. */
2276
char *name2; /* Not used. */
2277
int flags; /* Information about what happened. */
2279
register Entry *entryPtr = (Entry *) clientData;
2283
* If the variable is unset, then immediately recreate it unless
2284
* the whole interpreter is going away.
2287
if (flags & TCL_TRACE_UNSETS) {
2288
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2289
Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
2291
Tcl_TraceVar(interp, entryPtr->textVarName,
2292
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2293
EntryTextVarProc, clientData);
2295
return (char *) NULL;
2299
* Update the entry's text with the value of the variable, unless
2300
* the entry already has that value (this happens when the variable
2301
* changes value because we changed it because someone typed in
2305
value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
2306
if (value == NULL) {
2309
if (strcmp(value, entryPtr->string) != 0) {
2310
EntrySetValue(entryPtr, value);
2312
return (char *) NULL;