]> cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkentry.c
Fixes for compilation with gcc 15
[micropolis] / src / tk / tkentry.c
1 /*
2 * tkEntry.c --
3 *
4 * This module implements entry widgets for the Tk
5 * toolkit. An entry displays a string and allows
6 * the string to be edited.
7 *
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.
16 */
17
18 #ifndef lint
19 static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkEntry.c,v 1.37 92/08/21 16:09:15 ouster Exp $ SPRITE (Berkeley)";
20 #endif
21
22 #include "default.h"
23 #include "tkconfig.h"
24 #include "tkint.h"
25
26 /*
27 * A data structure of the following type is kept for each entry
28 * widget managed by this file:
29 */
30
31 typedef struct {
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
35 * cleaned up.*/
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
45 * when disabled. */
46
47 /*
48 * Information used when displaying widget:
49 */
50
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
59 * characters. */
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
64 * cursor. */
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
73 * off. */
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
80 * window. */
81 int cursorPos; /* Index of character before which next
82 * typed character will be inserted. */
83
84 /*
85 * Information about what's selected, if any.
86 */
87
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
94 * selection). */
95 int exportSelection; /* Non-zero means tie internal entry selection
96 * to X selection. */
97
98 /*
99 * Information for scanning:
100 */
101
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. */
106
107 /*
108 * Miscellaneous information:
109 */
110
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
116 * definitions. */
117 } Entry;
118
119 /*
120 * Assigned bits of "flags" fields of Entry structures, and what those
121 * bits mean:
122 *
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
131 * focus.
132 */
133
134 #define REDRAW_PENDING 1
135 #define BORDER_NEEDED 2
136 #define CURSOR_ON 4
137 #define GOT_FOCUS 8
138
139 /*
140 * Information used for argv parsing.
141 */
142
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),
207 TK_CONFIG_NULL_OK},
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,
211 (char *) NULL, 0, 0}
212 };
213
214 /*
215 * Flags for GetEntryIndex procedure:
216 */
217
218 #define ZERO_OK 1
219 #define LAST_PLUS_ONE_OK 2
220
221 /*
222 * Forward declarations for procedures defined later in this file:
223 */
224
225 static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
226 Entry *entryPtr, int argc, char **argv,
227 int flags));
228 static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
229 int count));
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,
235 char *string));
236 static void EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
237 static void EntryEventProc _ANSI_ARGS_((ClientData clientData,
238 XEvent *eventPtr));
239 static void EntryFocusProc _ANSI_ARGS_ ((ClientData clientData,
240 int gotFocus));
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,
248 char *value));
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,
253 int flags));
254 static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
255 static int EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
256 Tcl_Interp *interp, int argc, char **argv));
257 \f
258 /*
259 *--------------------------------------------------------------
260 *
261 * Tk_EntryCmd --
262 *
263 * This procedure is invoked to process the "entry" Tcl
264 * command. See the user documentation for details on what
265 * it does.
266 *
267 * Results:
268 * A standard Tcl result.
269 *
270 * Side effects:
271 * See the user documentation.
272 *
273 *--------------------------------------------------------------
274 */
275
276 int
277 Tk_EntryCmd (
278 ClientData clientData, /* Main window associated with
279 * interpreter. */
280 Tcl_Interp *interp, /* Current interpreter. */
281 int argc, /* Number of arguments. */
282 char **argv /* Argument strings. */
283 )
284 {
285 Tk_Window tkwin = (Tk_Window) clientData;
286 register Entry *entryPtr;
287 Tk_Window new;
288
289 if (argc < 2) {
290 Tcl_AppendResult(interp, "wrong # args: should be \"",
291 argv[0], " pathName ?options?\"", (char *) NULL);
292 return TCL_ERROR;
293 }
294
295 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
296 if (new == NULL) {
297 return TCL_ERROR;
298 }
299
300 /*
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).
304 */
305
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;
332 entryPtr->flags = 0;
333
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) {
342 goto error;
343 }
344 Tk_CreateFocusHandler(entryPtr->tkwin, EntryFocusProc,
345 (ClientData) entryPtr);
346
347 interp->result = Tk_PathName(entryPtr->tkwin);
348 return TCL_OK;
349
350 error:
351 Tk_DestroyWindow(entryPtr->tkwin);
352 return TCL_ERROR;
353 }
354 \f
355 /*
356 *--------------------------------------------------------------
357 *
358 * EntryWidgetCmd --
359 *
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.
363 *
364 * Results:
365 * A standard Tcl result.
366 *
367 * Side effects:
368 * See the user documentation.
369 *
370 *--------------------------------------------------------------
371 */
372
373 static int
374 EntryWidgetCmd (
375 ClientData clientData, /* Information about entry widget. */
376 Tcl_Interp *interp, /* Current interpreter. */
377 int argc, /* Number of arguments. */
378 char **argv /* Argument strings. */
379 )
380 {
381 register Entry *entryPtr = (Entry *) clientData;
382 int result = TCL_OK;
383 int length;
384 char c;
385
386 if (argc < 2) {
387 Tcl_AppendResult(interp, "wrong # args: should be \"",
388 argv[0], " option ?arg arg ...?\"", (char *) NULL);
389 return TCL_ERROR;
390 }
391 Tk_Preserve((ClientData) entryPtr);
392 c = argv[1][0];
393 length = strlen(argv[1]);
394 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
395 && (length >= 2)) {
396 if (argc == 2) {
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);
402 } else {
403 result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
404 TK_CONFIG_ARGV_ONLY);
405 }
406 } else if ((c == 'c') && (strncmp(argv[1], "cursor", length) == 0)
407 && (length >= 2)) {
408 if (argc != 3) {
409 Tcl_AppendResult(interp, "wrong # args: should be \"",
410 argv[0], " cursor pos\"",
411 (char *) NULL);
412 goto error;
413 }
414 if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->cursorPos)
415 != TCL_OK) {
416 goto error;
417 }
418 EventuallyRedraw(entryPtr);
419 } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
420 int first, last;
421
422 if ((argc < 3) || (argc > 4)) {
423 Tcl_AppendResult(interp, "wrong # args: should be \"",
424 argv[0], " delete firstIndex ?lastIndex?\"",
425 (char *) NULL);
426 goto error;
427 }
428 if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
429 goto error;
430 }
431 if (argc == 3) {
432 last = first;
433 } else {
434 if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
435 goto error;
436 }
437 }
438 if ((last >= first) && (entryPtr->state == tkNormalUid)) {
439 DeleteChars(entryPtr, first, last+1-first);
440 }
441 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
442 if (argc != 2) {
443 Tcl_AppendResult(interp, "wrong # args: should be \"",
444 argv[0], " get\"", (char *) NULL);
445 goto error;
446 }
447 interp->result = entryPtr->string;
448 } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
449 && (length >= 2)) {
450 int index;
451
452 if (argc != 3) {
453 Tcl_AppendResult(interp, "wrong # args: should be \"",
454 argv[0], " index string\"", (char *) NULL);
455 goto error;
456 }
457 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
458 goto error;
459 }
460 sprintf(interp->result, "%d", index);
461 } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
462 && (length >= 2)) {
463 int index;
464
465 if (argc != 4) {
466 Tcl_AppendResult(interp, "wrong # args: should be \"",
467 argv[0], " insert index text\"",
468 (char *) NULL);
469 goto error;
470 }
471 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
472 goto error;
473 }
474 if (entryPtr->state == tkNormalUid) {
475 InsertChars(entryPtr, index, argv[3]);
476 }
477 } else if ((c == 's') && (length >= 2)
478 && (strncmp(argv[1], "scan", length) == 0)) {
479 int x;
480
481 if (argc != 4) {
482 Tcl_AppendResult(interp, "wrong # args: should be \"",
483 argv[0], " scan mark|dragto x\"", (char *) NULL);
484 goto error;
485 }
486 if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
487 goto error;
488 }
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);
496 } else {
497 Tcl_AppendResult(interp, "bad scan option \"", argv[2],
498 "\": must be mark or dragto", (char *) NULL);
499 goto error;
500 }
501 } else if ((c == 's') && (length >= 2)
502 && (strncmp(argv[1], "select", length) == 0)) {
503 int index;
504
505 if (argc < 3) {
506 Tcl_AppendResult(interp, "too few args: should be \"",
507 argv[0], " select option ?index?\"", (char *) NULL);
508 goto error;
509 }
510 length = strlen(argv[2]);
511 c = argv[2][0];
512 if ((c == 'c') && (argv[2] != NULL)
513 && (strncmp(argv[2], "clear", length) == 0)) {
514 if (argc != 3) {
515 Tcl_AppendResult(interp, "wrong # args: should be \"",
516 argv[0], " select clear\"", (char *) NULL);
517 goto error;
518 }
519 if (entryPtr->selectFirst != -1) {
520 entryPtr->selectFirst = entryPtr->selectLast = -1;
521 EventuallyRedraw(entryPtr);
522 }
523 goto done;
524 }
525 if (argc >= 4) {
526 if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
527 goto error;
528 }
529 }
530 if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
531 if (argc != 4) {
532 Tcl_AppendResult(interp, "wrong # args: should be \"",
533 argv[0], " select adjust index\"",
534 (char *) NULL);
535 goto error;
536 }
537 if (entryPtr->selectFirst >= 0) {
538 if (index < (entryPtr->selectFirst + entryPtr->selectLast)/2) {
539 entryPtr->selectAnchor = entryPtr->selectLast + 1;
540 } else {
541 entryPtr->selectAnchor = entryPtr->selectFirst;
542 }
543 }
544 EntrySelectTo(entryPtr, index);
545 } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
546 if (argc != 4) {
547 Tcl_AppendResult(interp, "wrong # args: should be \"",
548 argv[0], " select from index\"",
549 (char *) NULL);
550 goto error;
551 }
552 entryPtr->selectAnchor = index;
553 } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
554 if (argc != 4) {
555 Tcl_AppendResult(interp, "wrong # args: should be \"",
556 argv[0], " select to index\"",
557 (char *) NULL);
558 goto error;
559 }
560 EntrySelectTo(entryPtr, index);
561 } else {
562 Tcl_AppendResult(interp, "bad select option \"", argv[2],
563 "\": must be adjust, clear, from, or to", (char *) NULL);
564 goto error;
565 }
566 } else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
567 int index;
568
569 if (argc != 3) {
570 Tcl_AppendResult(interp, "wrong # args: should be \"",
571 argv[0], " view index\"", (char *) NULL);
572 goto error;
573 }
574 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
575 goto error;
576 }
577 if ((index >= entryPtr->numChars) && (index > 0)) {
578 index = entryPtr->numChars-1;
579 }
580 entryPtr->leftIndex = index;
581 EventuallyRedraw(entryPtr);
582 EntryUpdateScrollbar(entryPtr);
583 } else {
584 Tcl_AppendResult(interp, "bad option \"", argv[1],
585 "\": must be configure, cursor, delete, get, index, ",
586 "insert, scan, select, or view", (char *) NULL);
587 goto error;
588 }
589 done:
590 Tk_Release((ClientData) entryPtr);
591 return result;
592
593 error:
594 Tk_Release((ClientData) entryPtr);
595 return TCL_ERROR;
596 }
597 \f
598 /*
599 *----------------------------------------------------------------------
600 *
601 * DestroyEntry --
602 *
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).
606 *
607 * Results:
608 * None.
609 *
610 * Side effects:
611 * Everything associated with the entry is freed up.
612 *
613 *----------------------------------------------------------------------
614 */
615
616 static void
617 DestroyEntry (
618 ClientData clientData /* Info about entry widget. */
619 )
620 {
621 register Entry *entryPtr = (Entry *) clientData;
622
623 ckfree(entryPtr->string);
624 if (entryPtr->normalBorder != NULL) {
625 Tk_Free3DBorder(entryPtr->normalBorder);
626 }
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);
632 }
633 if (entryPtr->fontPtr != NULL) {
634 Tk_FreeFontStruct(entryPtr->fontPtr);
635 }
636 if (entryPtr->fgColorPtr != NULL) {
637 Tk_FreeColor(entryPtr->fgColorPtr);
638 }
639 if (entryPtr->textGC != None) {
640 Tk_FreeGC(entryPtr->textGC);
641 }
642 if (entryPtr->selBorder != NULL) {
643 Tk_Free3DBorder(entryPtr->selBorder);
644 }
645 if (entryPtr->selFgColorPtr != NULL) {
646 Tk_FreeColor(entryPtr->selFgColorPtr);
647 }
648 if (entryPtr->selTextGC != None) {
649 Tk_FreeGC(entryPtr->selTextGC);
650 }
651 if (entryPtr->cursorBorder != NULL) {
652 Tk_Free3DBorder(entryPtr->cursorBorder);
653 }
654 if (entryPtr->cursorBlinkHandler != NULL) {
655 Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
656 entryPtr->cursorBlinkHandler = NULL;
657 }
658 if (entryPtr->cursor != None) {
659 Tk_FreeCursor(entryPtr->cursor);
660 }
661 if (entryPtr->scrollCmd != NULL) {
662 ckfree(entryPtr->scrollCmd);
663 }
664 ckfree((char *) entryPtr);
665 }
666 \f
667 /*
668 *----------------------------------------------------------------------
669 *
670 * ConfigureEntry --
671 *
672 * This procedure is called to process an argv/argc list, plus
673 * the Tk option database, in order to configure (or reconfigure)
674 * an entry widget.
675 *
676 * Results:
677 * The return value is a standard Tcl result. If TCL_ERROR is
678 * returned, then interp->result contains an error message.
679 *
680 * Side effects:
681 * Configuration information, such as colors, border width,
682 * etc. get set for entryPtr; old resources get freed,
683 * if there were any.
684 *
685 *----------------------------------------------------------------------
686 */
687
688 static int
689 ConfigureEntry (
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. */
696 )
697 {
698 XGCValues gcValues;
699 GC new;
700 int width, height, fontHeight, oldExport;
701
702 /*
703 * Eliminate any existing trace on a variable monitored by the entry.
704 */
705
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);
710 }
711
712 oldExport = entryPtr->exportSelection;
713 if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
714 argc, argv, (char *) entryPtr, flags) != TCL_OK) {
715 return TCL_ERROR;
716 }
717
718 /*
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.
722 */
723
724 if (entryPtr->textVarName != NULL) {
725 char *value;
726
727 value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
728 if (value == NULL) {
729 Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
730 TCL_GLOBAL_ONLY);
731 } else {
732 EntrySetValue(entryPtr, value);
733 }
734 Tcl_TraceVar(interp, entryPtr->textVarName,
735 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
736 EntryTextVarProc, (ClientData) entryPtr);
737 }
738
739 /*
740 * A few other options also need special processing, such as parsing
741 * the geometry and setting the background from a 3-D border.
742 */
743
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;
749 return TCL_ERROR;
750 }
751
752 Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
753
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,
758 &gcValues);
759 if (entryPtr->textGC != None) {
760 Tk_FreeGC(entryPtr->textGC);
761 }
762 entryPtr->textGC = new;
763
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);
769 }
770 entryPtr->selTextGC = new;
771
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;
776 }
777 }
778 if (entryPtr->cursorBorderWidth > entryPtr->cursorWidth/2) {
779 entryPtr->cursorBorderWidth = entryPtr->cursorWidth/2;
780 }
781
782 /*
783 * Restart the cursor timing sequence in case the on-time or off-time
784 * just changed.
785 */
786
787 if (entryPtr->flags & GOT_FOCUS) {
788 EntryFocusProc((ClientData) entryPtr, 1);
789 }
790
791 /*
792 * Claim the selection if we've suddenly started exporting it.
793 */
794
795 if (entryPtr->exportSelection && (!oldExport)
796 && (entryPtr->selectFirst != -1)) {
797 Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
798 (ClientData) entryPtr);
799 }
800
801 /*
802 * Register the desired geometry for the window, and arrange for
803 * the window to be redisplayed.
804 */
805
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;
814 } else {
815 entryPtr->offset = 0;
816 }
817 EventuallyRedraw(entryPtr);
818 EntryUpdateScrollbar(entryPtr);
819 return TCL_OK;
820 }
821 \f
822 /*
823 *--------------------------------------------------------------
824 *
825 * DisplayEntry --
826 *
827 * This procedure redraws the contents of an entry window.
828 *
829 * Results:
830 * None.
831 *
832 * Side effects:
833 * Information appears on the screen.
834 *
835 *--------------------------------------------------------------
836 */
837
838 static void
839 DisplayEntry (
840 ClientData clientData /* Information about window. */
841 )
842 {
843 register Entry *entryPtr = (Entry *) clientData;
844 register Tk_Window tkwin = entryPtr->tkwin;
845 int startX, baseY, selStartX, selEndX, index, cursorX;
846 int xBound, count;
847 Pixmap pixmap;
848
849 entryPtr->flags &= ~REDRAW_PENDING;
850 if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
851 return;
852 }
853
854 /*
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.
859 */
860
861 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
862 Tk_Width(tkwin), Tk_Height(tkwin),
863 Tk_DefaultDepth(Tk_Screen(tkwin)));
864
865 /*
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.
869 */
870
871 startX = entryPtr->offset;
872 xBound = Tk_Width(tkwin) - entryPtr->offset;
873 baseY = (Tk_Height(tkwin) + entryPtr->fontPtr->ascent
874 - entryPtr->fontPtr->descent)/2;
875
876 /*
877 * Draw the background in three layers. From bottom to top the
878 * layers are: normal background, selection background, and
879 * insertion cursor background.
880 */
881
882 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, entryPtr->normalBorder,
883 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
884
885 if (entryPtr->selectLast >= entryPtr->leftIndex) {
886 if (entryPtr->selectFirst <= entryPtr->leftIndex) {
887 selStartX = startX;
888 index = entryPtr->leftIndex;
889 } else {
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;
895 }
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,
900 &selEndX);
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);
909 } else {
910 selEndX = xBound;
911 }
912 }
913
914 /*
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.
920 */
921
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,
945 0, TK_RELIEF_FLAT);
946 }
947 }
948 }
949
950 /*
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
953 * of the selection.
954 */
955
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);
961 } else {
962 count = entryPtr->selectFirst - entryPtr->leftIndex;
963 if (count > 0) {
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;
968 } else {
969 index = entryPtr->leftIndex;
970 }
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);
976 }
977 count = entryPtr->numChars - entryPtr->selectLast - 1;
978 if ((selEndX < xBound) && (count > 0)) {
979 TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
980 entryPtr->fontPtr,
981 entryPtr->string + entryPtr->selectLast + 1,
982 count, selEndX, baseY, TK_NEWLINES_NOT_SPECIAL);
983 }
984 }
985
986 /*
987 * Draw the border last, so it will overwrite any text that extends
988 * past the viewable part of the window.
989 */
990
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,
995 entryPtr->relief);
996 }
997
998 /*
999 * Everything's been redisplayed; now copy the pixmap onto the screen
1000 * and free up the pixmap.
1001 */
1002
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;
1007 }
1008 \f
1009 /*
1010 *----------------------------------------------------------------------
1011 *
1012 * InsertChars --
1013 *
1014 * Add new characters to an entry widget.
1015 *
1016 * Results:
1017 * None.
1018 *
1019 * Side effects:
1020 * New information gets added to entryPtr; it will be redisplayed
1021 * soon, but not necessarily immediately.
1022 *
1023 *----------------------------------------------------------------------
1024 */
1025
1026 static void
1027 InsertChars (
1028 register Entry *entryPtr, /* Entry that is to get the new
1029 * elements. */
1030 int index, /* Add the new elements before this
1031 * element. */
1032 char *string /* New characters to add (NULL-terminated
1033 * string). */
1034 )
1035 {
1036 int length;
1037 char *new;
1038
1039 length = strlen(string);
1040 if (length == 0) {
1041 return;
1042 }
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;
1050
1051 /*
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).
1055 */
1056
1057 if (entryPtr->selectFirst >= index) {
1058 entryPtr->selectFirst += length;
1059 }
1060 if (entryPtr->selectLast >= index) {
1061 entryPtr->selectLast += length;
1062 }
1063 if (entryPtr->selectAnchor >= index) {
1064 entryPtr->selectAnchor += length;
1065 }
1066 if (entryPtr->leftIndex > index) {
1067 entryPtr->leftIndex += length;
1068 }
1069 if (entryPtr->cursorPos >= index) {
1070 entryPtr->cursorPos += length;
1071 }
1072
1073 if (entryPtr->textVarName != NULL) {
1074 Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1075 TCL_GLOBAL_ONLY);
1076 }
1077 EventuallyRedraw(entryPtr);
1078 EntryUpdateScrollbar(entryPtr);
1079 }
1080 \f
1081 /*
1082 *----------------------------------------------------------------------
1083 *
1084 * DeleteChars --
1085 *
1086 * Remove one or more characters from an entry widget.
1087 *
1088 * Results:
1089 * None.
1090 *
1091 * Side effects:
1092 * Memory gets freed, the entry gets modified and (eventually)
1093 * redisplayed.
1094 *
1095 *----------------------------------------------------------------------
1096 */
1097
1098 static void
1099 DeleteChars (
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. */
1103 )
1104 {
1105 char *new;
1106
1107 if ((index + count) > entryPtr->numChars) {
1108 count = entryPtr->numChars - index;
1109 }
1110 if (count <= 0) {
1111 return;
1112 }
1113
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;
1120
1121 /*
1122 * Deleting characters results in the remaining characters being
1123 * renumbered. Update the various indexes into the string to reflect
1124 * this change.
1125 */
1126 if (entryPtr->selectFirst >= index) {
1127 if (entryPtr->selectFirst >= (index+count)) {
1128 entryPtr->selectFirst -= count;
1129 } else {
1130 entryPtr->selectFirst = index;
1131 }
1132 }
1133 if (entryPtr->selectLast >= index) {
1134 if (entryPtr->selectLast >= (index+count)) {
1135 entryPtr->selectLast -= count;
1136 } else {
1137 entryPtr->selectLast = index-1;
1138 }
1139 }
1140 if (entryPtr->selectLast < entryPtr->selectFirst) {
1141 entryPtr->selectFirst = entryPtr->selectLast = -1;
1142 }
1143 if (entryPtr->selectAnchor >= index) {
1144 if (entryPtr->selectAnchor >= (index+count)) {
1145 entryPtr->selectAnchor -= count;
1146 } else {
1147 entryPtr->selectAnchor = index;
1148 }
1149 }
1150 if (entryPtr->leftIndex > index) {
1151 if (entryPtr->leftIndex >= (index+count)) {
1152 entryPtr->leftIndex -= count;
1153 } else {
1154 entryPtr->leftIndex = index;
1155 }
1156 }
1157 if (entryPtr->cursorPos >= index) {
1158 if (entryPtr->cursorPos >= (index+count)) {
1159 entryPtr->cursorPos -= count;
1160 } else {
1161 entryPtr->cursorPos = index;
1162 }
1163 }
1164
1165 if (entryPtr->textVarName != NULL) {
1166 Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1167 TCL_GLOBAL_ONLY);
1168 }
1169 EventuallyRedraw(entryPtr);
1170 EntryUpdateScrollbar(entryPtr);
1171 }
1172 \f
1173 /*
1174 *----------------------------------------------------------------------
1175 *
1176 * EntrySetValue --
1177 *
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.
1181 *
1182 * Results:
1183 * None.
1184 *
1185 * Side effects:
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
1190 * infinite loop.
1191 *
1192 *----------------------------------------------------------------------
1193 */
1194
1195 static void
1196 EntrySetValue (
1197 register Entry *entryPtr, /* Entry whose value is to be
1198 * changed. */
1199 char *value /* New text to display in entry. */
1200 )
1201 {
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;
1209
1210 EventuallyRedraw(entryPtr);
1211 EntryUpdateScrollbar(entryPtr);
1212 }
1213 \f
1214 /*
1215 *--------------------------------------------------------------
1216 *
1217 * EntryEventProc --
1218 *
1219 * This procedure is invoked by the Tk dispatcher for various
1220 * events on entryes.
1221 *
1222 * Results:
1223 * None.
1224 *
1225 * Side effects:
1226 * When the window gets deleted, internal structures get
1227 * cleaned up. When it gets exposed, it is redisplayed.
1228 *
1229 *--------------------------------------------------------------
1230 */
1231
1232 static void
1233 EntryEventProc (
1234 ClientData clientData, /* Information about window. */
1235 XEvent *eventPtr /* Information about event. */
1236 )
1237 {
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);
1247 }
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);
1254 }
1255 }
1256 \f
1257 /*
1258 *--------------------------------------------------------------
1259 *
1260 * GetEntryIndex --
1261 *
1262 * Parse an index into an entry and return either its value
1263 * or an error.
1264 *
1265 * Results:
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.
1271 *
1272 * Side effects:
1273 * None.
1274 *
1275 *--------------------------------------------------------------
1276 */
1277
1278 static int
1279 GetEntryIndex (
1280 Tcl_Interp *interp, /* For error messages. */
1281 Entry *entryPtr, /* Entry for which the index is being
1282 * specified. */
1283 char *string, /* Specifies character in entryPtr. */
1284 int *indexPtr /* Where to store converted index. */
1285 )
1286 {
1287 int length;
1288
1289 length = strlen(string);
1290
1291 if (string[0] == 'e') {
1292 if (strncmp(string, "end", length) == 0) {
1293 *indexPtr = entryPtr->numChars;
1294 } else {
1295 badIndex:
1296
1297 /*
1298 * Some of the paths here leave messages in interp->result,
1299 * so we have to clear it out before storing our own message.
1300 */
1301
1302 Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1303 Tcl_AppendResult(interp, "bad entry index \"", string,
1304 "\"", (char *) NULL);
1305 return TCL_ERROR;
1306 }
1307 } else if (string[0] == 'c') {
1308 if (strncmp(string, "cursor", length) == 0) {
1309 *indexPtr = entryPtr->cursorPos;
1310 } else {
1311 goto badIndex;
1312 }
1313 } else if (string[0] == 's') {
1314 if (entryPtr->selectFirst == -1) {
1315 interp->result = "selection isn't in entry";
1316 return TCL_ERROR;
1317 }
1318 if (length < 5) {
1319 goto badIndex;
1320 }
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;
1325 } else {
1326 goto badIndex;
1327 }
1328 } else if (string[0] == '@') {
1329 int x, dummy;
1330
1331 if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
1332 goto badIndex;
1333 }
1334 if (entryPtr->numChars == 0) {
1335 *indexPtr = 0;
1336 } else {
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);
1341 }
1342 } else {
1343 if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1344 goto badIndex;
1345 }
1346 if (*indexPtr < 0){
1347 *indexPtr = 0;
1348 } else if (*indexPtr > entryPtr->numChars) {
1349 *indexPtr = entryPtr->numChars;
1350 }
1351 }
1352 return TCL_OK;
1353 }
1354 \f
1355 /*
1356 *----------------------------------------------------------------------
1357 *
1358 * EntryScanTo --
1359 *
1360 * Given a y-coordinate (presumably of the curent mouse location)
1361 * drag the view in the window to implement the scan operation.
1362 *
1363 * Results:
1364 * None.
1365 *
1366 * Side effects:
1367 * The view in the window may change.
1368 *
1369 *----------------------------------------------------------------------
1370 */
1371
1372 static void
1373 EntryScanTo (
1374 register Entry *entryPtr, /* Information about widget. */
1375 int x /* X-coordinate to use for scan
1376 * operation. */
1377 )
1378 {
1379 int newLeftIndex;
1380
1381 /*
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).
1390 */
1391
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;
1397 }
1398 if (newLeftIndex < 0) {
1399 newLeftIndex = entryPtr->scanMarkIndex = 0;
1400 entryPtr->scanMarkX = x;
1401 }
1402 if (newLeftIndex != entryPtr->leftIndex) {
1403 entryPtr->leftIndex = newLeftIndex;
1404 EventuallyRedraw(entryPtr);
1405 EntryUpdateScrollbar(entryPtr);
1406 }
1407 }
1408 \f
1409 /*
1410 *----------------------------------------------------------------------
1411 *
1412 * EntrySelectTo --
1413 *
1414 * Modify the selection by moving its un-anchored end. This could
1415 * make the selection either larger or smaller.
1416 *
1417 * Results:
1418 * None.
1419 *
1420 * Side effects:
1421 * The selection changes.
1422 *
1423 *----------------------------------------------------------------------
1424 */
1425
1426 static void
1427 EntrySelectTo (
1428 register Entry *entryPtr, /* Information about widget. */
1429 int index /* Index of element that is to
1430 * become the "other" end of the
1431 * selection. */
1432 )
1433 {
1434 int newFirst, newLast;
1435
1436 /*
1437 * Grab the selection if we don't own it already.
1438 */
1439
1440 if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
1441 Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
1442 (ClientData) entryPtr);
1443 }
1444
1445 if (index < 0) {
1446 index = 0;
1447 }
1448 if (index >= entryPtr->numChars) {
1449 index = entryPtr->numChars-1;
1450 }
1451 if (entryPtr->selectAnchor > entryPtr->numChars) {
1452 entryPtr->selectAnchor = entryPtr->numChars;
1453 }
1454 if (entryPtr->selectAnchor <= index) {
1455 newFirst = entryPtr->selectAnchor;
1456 newLast = index;
1457 } else {
1458 newFirst = index;
1459 newLast = entryPtr->selectAnchor - 1;
1460 if (newLast < 0) {
1461 newFirst = newLast = -1;
1462 }
1463 }
1464 if ((entryPtr->selectFirst == newFirst)
1465 && (entryPtr->selectLast == newLast)) {
1466 return;
1467 }
1468 entryPtr->selectFirst = newFirst;
1469 entryPtr->selectLast = newLast;
1470 EventuallyRedraw(entryPtr);
1471 }
1472 \f
1473 /*
1474 *----------------------------------------------------------------------
1475 *
1476 * EntryFetchSelection --
1477 *
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.
1481 *
1482 * Results:
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.
1487 *
1488 * Side effects:
1489 * None.
1490 *
1491 *----------------------------------------------------------------------
1492 */
1493
1494 static int
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
1500 * selection. */
1501 int maxBytes /* Maximum number of bytes to place
1502 * at buffer, not including terminating
1503 * NULL character. */
1504 )
1505 {
1506 Entry *entryPtr = (Entry *) clientData;
1507 int count;
1508
1509 if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
1510 return -1;
1511 }
1512 count = entryPtr->selectLast + 1 - entryPtr->selectFirst - offset;
1513 if (count > maxBytes) {
1514 count = maxBytes;
1515 }
1516 if (count <= 0) {
1517 return 0;
1518 }
1519 strncpy(buffer, entryPtr->string + entryPtr->selectFirst + offset, count);
1520 buffer[count] = '\0';
1521 return count;
1522 }
1523 \f
1524 /*
1525 *----------------------------------------------------------------------
1526 *
1527 * EntryLostSelection --
1528 *
1529 * This procedure is called back by Tk when the selection is
1530 * grabbed away from an entry widget.
1531 *
1532 * Results:
1533 * None.
1534 *
1535 * Side effects:
1536 * The existing selection is unhighlighted, and the window is
1537 * marked as not containing a selection.
1538 *
1539 *----------------------------------------------------------------------
1540 */
1541
1542 static void
1543 EntryLostSelection (
1544 ClientData clientData /* Information about entry widget. */
1545 )
1546 {
1547 Entry *entryPtr = (Entry *) clientData;
1548
1549 if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
1550 entryPtr->selectFirst = -1;
1551 entryPtr->selectLast = -1;
1552 EventuallyRedraw(entryPtr);
1553 }
1554 }
1555 \f
1556 /*
1557 *----------------------------------------------------------------------
1558 *
1559 * EventuallyRedraw --
1560 *
1561 * Ensure that an entry is eventually redrawn on the display.
1562 *
1563 * Results:
1564 * None.
1565 *
1566 * Side effects:
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
1570 * could be changed.
1571 *
1572 *----------------------------------------------------------------------
1573 */
1574
1575 static void
1576 EventuallyRedraw (
1577 register Entry *entryPtr /* Information about widget. */
1578 )
1579 {
1580 if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
1581 return;
1582 }
1583
1584 /*
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.
1588 */
1589
1590 if (!(entryPtr->flags & REDRAW_PENDING)) {
1591 entryPtr->flags |= REDRAW_PENDING;
1592 Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
1593 }
1594 }
1595 \f
1596 /*
1597 *----------------------------------------------------------------------
1598 *
1599 * EntryUpdateScrollbar --
1600 *
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.
1605 *
1606 * Results:
1607 * None.
1608 *
1609 * Side effects:
1610 * A Tcl command is invoked, and an additional command may be
1611 * invoked to process errors in the command.
1612 *
1613 *----------------------------------------------------------------------
1614 */
1615
1616 static void
1617 EntryUpdateScrollbar (
1618 register Entry *entryPtr /* Information about widget. */
1619 )
1620 {
1621 char args[100];
1622 int result, last, charsInWindow, endX;
1623
1624 if (entryPtr->scrollCmd == NULL) {
1625 return;
1626 }
1627
1628 /*
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.
1632 */
1633
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;
1641 } else {
1642 last = entryPtr->leftIndex + charsInWindow - 1;
1643 }
1644 if (endX < Tk_Width(entryPtr->tkwin)) {
1645 charsInWindow += (Tk_Width(entryPtr->tkwin) - endX)/entryPtr->avgWidth;
1646 }
1647 sprintf(args, " %d %d %d %d", entryPtr->numChars, charsInWindow,
1648 entryPtr->leftIndex, last);
1649 result = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
1650 (char *) NULL);
1651 if (result != TCL_OK) {
1652 TkBindError(entryPtr->interp);
1653 }
1654 Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
1655 }
1656 \f
1657 /*
1658 *----------------------------------------------------------------------
1659 *
1660 * EntryBlinkProc --
1661 *
1662 * This procedure is called as a timer handler to blink the
1663 * insertion cursor off and on.
1664 *
1665 * Results:
1666 * None.
1667 *
1668 * Side effects:
1669 * The cursor gets turned on or off, redisplay gets invoked,
1670 * and this procedure reschedules itself.
1671 *
1672 *----------------------------------------------------------------------
1673 */
1674
1675 static void
1676 EntryBlinkProc (
1677 ClientData clientData /* Pointer to record describing entry. */
1678 )
1679 {
1680 register Entry *entryPtr = (Entry *) clientData;
1681
1682 if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->cursorOffTime == 0)) {
1683 return;
1684 }
1685 if (entryPtr->flags & CURSOR_ON) {
1686 entryPtr->flags &= ~CURSOR_ON;
1687 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1688 entryPtr->cursorOffTime, EntryBlinkProc, (ClientData) entryPtr);
1689 } else {
1690 entryPtr->flags |= CURSOR_ON;
1691 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1692 entryPtr->cursorOnTime, EntryBlinkProc, (ClientData) entryPtr);
1693 }
1694 EventuallyRedraw(entryPtr);
1695 }
1696 \f
1697 /*
1698 *----------------------------------------------------------------------
1699 *
1700 * EntryFocusProc --
1701 *
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.
1705 *
1706 * Results:
1707 * None.
1708 *
1709 * Side effects:
1710 * The cursor gets turned on or off.
1711 *
1712 *----------------------------------------------------------------------
1713 */
1714
1715 static void
1716 EntryFocusProc (
1717 ClientData clientData, /* Pointer to structure describing entry. */
1718 int gotFocus /* 1 means window is getting focus, 0 means
1719 * it's losing it. */
1720 )
1721 {
1722 register Entry *entryPtr = (Entry *) clientData;
1723
1724 if (entryPtr->cursorBlinkHandler != NULL) {
1725 Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
1726 entryPtr->cursorBlinkHandler = NULL;
1727 }
1728 if (gotFocus) {
1729 entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
1730 if (entryPtr->cursorOffTime != 0) {
1731 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1732 entryPtr->cursorOnTime, EntryBlinkProc,
1733 (ClientData) entryPtr);
1734 }
1735 } else {
1736 entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
1737 entryPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
1738 }
1739 EventuallyRedraw(entryPtr);
1740 }
1741 \f
1742 /*
1743 *--------------------------------------------------------------
1744 *
1745 * EntryTextVarProc --
1746 *
1747 * This procedure is invoked when someone changes the variable
1748 * whose contents are to be displayed in an entry.
1749 *
1750 * Results:
1751 * NULL is always returned.
1752 *
1753 * Side effects:
1754 * The text displayed in the entry will change to match the
1755 * variable.
1756 *
1757 *--------------------------------------------------------------
1758 */
1759
1760 /* ARGSUSED */
1761 static char *
1762 EntryTextVarProc (
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. */
1768 )
1769 {
1770 register Entry *entryPtr = (Entry *) clientData;
1771 char *value;
1772
1773 /*
1774 * If the variable is unset, then immediately recreate it unless
1775 * the whole interpreter is going away.
1776 */
1777
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);
1785 }
1786 return (char *) NULL;
1787 }
1788
1789 /*
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
1793 * the entry).
1794 */
1795
1796 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1797 if (value == NULL) {
1798 value = "";
1799 }
1800 if (strcmp(value, entryPtr->string) != 0) {
1801 EntrySetValue(entryPtr, value);
1802 }
1803 return (char *) NULL;
1804 }
Impressum, Datenschutz