]> git.zerfleddert.de Git - micropolis/blame - src/tk/tkentry.c
handle spaces in filenames
[micropolis] / src / tk / tkentry.c
CommitLineData
6a5fa4e0
MG
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
19static 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
31typedef 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
143static 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
225static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
226 Entry *entryPtr, int argc, char **argv,
227 int flags));
228static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
229 int count));
230static void DestroyEntry _ANSI_ARGS_((ClientData clientData));
231static void DisplayEntry _ANSI_ARGS_((ClientData clientData));
232static int GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
233 Entry *entryPtr, char *string, int *indexPtr));
234static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
235 char *string));
236static void EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
237static void EntryEventProc _ANSI_ARGS_((ClientData clientData,
238 XEvent *eventPtr));
239static void EntryFocusProc _ANSI_ARGS_ ((ClientData clientData,
240 int gotFocus));
241static int EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
242 int offset, char *buffer, int maxBytes));
243static void EntryLostSelection _ANSI_ARGS_((
244 ClientData clientData));
245static void EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
246static void EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
247static void EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
248 char *value));
249static void EntrySelectTo _ANSI_ARGS_((
250 Entry *entryPtr, int index));
251static char * EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
252 Tcl_Interp *interp, char *name1, char *name2,
253 int flags));
254static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
255static 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
276int
277Tk_EntryCmd(clientData, interp, argc, argv)
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 Tk_Window tkwin = (Tk_Window) clientData;
285 register Entry *entryPtr;
286 Tk_Window new;
287
288 if (argc < 2) {
289 Tcl_AppendResult(interp, "wrong # args: should be \"",
290 argv[0], " pathName ?options?\"", (char *) NULL);
291 return TCL_ERROR;
292 }
293
294 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
295 if (new == NULL) {
296 return TCL_ERROR;
297 }
298
299 /*
300 * Initialize the fields of the structure that won't be initialized
301 * by ConfigureEntry, or that ConfigureEntry requires to be
302 * initialized already (e.g. resource pointers).
303 */
304
305 entryPtr = (Entry *) ckalloc(sizeof(Entry));
306 entryPtr->tkwin = new;
307 entryPtr->interp = interp;
308 entryPtr->numChars = 0;
309 entryPtr->string = (char *) ckalloc(1);
310 entryPtr->string[0] = '\0';
311 entryPtr->textVarName = NULL;
312 entryPtr->state = tkNormalUid;
313 entryPtr->normalBorder = NULL;
314 entryPtr->fontPtr = NULL;
315 entryPtr->fgColorPtr = NULL;
316 entryPtr->textGC = None;
317 entryPtr->selBorder = NULL;
318 entryPtr->selFgColorPtr = NULL;
319 entryPtr->selTextGC = NULL;
320 entryPtr->cursorBorder = NULL;
321 entryPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
322 entryPtr->leftIndex = 0;
323 entryPtr->cursorPos = 0;
324 entryPtr->selectFirst = -1;
325 entryPtr->selectLast = -1;
326 entryPtr->selectAnchor = 0;
327 entryPtr->exportSelection = 1;
328 entryPtr->scanMarkX = 0;
329 entryPtr->cursor = None;
330 entryPtr->scrollCmd = NULL;
331 entryPtr->flags = 0;
332
333 Tk_SetClass(entryPtr->tkwin, "Entry");
334 Tk_CreateEventHandler(entryPtr->tkwin, ExposureMask|StructureNotifyMask,
335 EntryEventProc, (ClientData) entryPtr);
336 Tk_CreateSelHandler(entryPtr->tkwin, XA_STRING, EntryFetchSelection,
337 (ClientData) entryPtr, XA_STRING);
338 Tcl_CreateCommand(interp, Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
339 (ClientData) entryPtr, (void (*)()) NULL);
340 if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
341 goto error;
342 }
343 Tk_CreateFocusHandler(entryPtr->tkwin, EntryFocusProc,
344 (ClientData) entryPtr);
345
346 interp->result = Tk_PathName(entryPtr->tkwin);
347 return TCL_OK;
348
349 error:
350 Tk_DestroyWindow(entryPtr->tkwin);
351 return TCL_ERROR;
352}
353\f
354/*
355 *--------------------------------------------------------------
356 *
357 * EntryWidgetCmd --
358 *
359 * This procedure is invoked to process the Tcl command
360 * that corresponds to a widget managed by this module.
361 * See the user documentation for details on what it does.
362 *
363 * Results:
364 * A standard Tcl result.
365 *
366 * Side effects:
367 * See the user documentation.
368 *
369 *--------------------------------------------------------------
370 */
371
372static int
373EntryWidgetCmd(clientData, interp, argc, argv)
374 ClientData clientData; /* Information about entry widget. */
375 Tcl_Interp *interp; /* Current interpreter. */
376 int argc; /* Number of arguments. */
377 char **argv; /* Argument strings. */
378{
379 register Entry *entryPtr = (Entry *) clientData;
380 int result = TCL_OK;
381 int length;
382 char c;
383
384 if (argc < 2) {
385 Tcl_AppendResult(interp, "wrong # args: should be \"",
386 argv[0], " option ?arg arg ...?\"", (char *) NULL);
387 return TCL_ERROR;
388 }
389 Tk_Preserve((ClientData) entryPtr);
390 c = argv[1][0];
391 length = strlen(argv[1]);
392 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
393 && (length >= 2)) {
394 if (argc == 2) {
395 result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
396 (char *) entryPtr, (char *) NULL, 0);
397 } else if (argc == 3) {
398 result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
399 (char *) entryPtr, argv[2], 0);
400 } else {
401 result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
402 TK_CONFIG_ARGV_ONLY);
403 }
404 } else if ((c == 'c') && (strncmp(argv[1], "cursor", length) == 0)
405 && (length >= 2)) {
406 if (argc != 3) {
407 Tcl_AppendResult(interp, "wrong # args: should be \"",
408 argv[0], " cursor pos\"",
409 (char *) NULL);
410 goto error;
411 }
412 if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->cursorPos)
413 != TCL_OK) {
414 goto error;
415 }
416 EventuallyRedraw(entryPtr);
417 } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
418 int first, last;
419
420 if ((argc < 3) || (argc > 4)) {
421 Tcl_AppendResult(interp, "wrong # args: should be \"",
422 argv[0], " delete firstIndex ?lastIndex?\"",
423 (char *) NULL);
424 goto error;
425 }
426 if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
427 goto error;
428 }
429 if (argc == 3) {
430 last = first;
431 } else {
432 if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
433 goto error;
434 }
435 }
436 if ((last >= first) && (entryPtr->state == tkNormalUid)) {
437 DeleteChars(entryPtr, first, last+1-first);
438 }
439 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
440 if (argc != 2) {
441 Tcl_AppendResult(interp, "wrong # args: should be \"",
442 argv[0], " get\"", (char *) NULL);
443 goto error;
444 }
445 interp->result = entryPtr->string;
446 } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
447 && (length >= 2)) {
448 int index;
449
450 if (argc != 3) {
451 Tcl_AppendResult(interp, "wrong # args: should be \"",
452 argv[0], " index string\"", (char *) NULL);
453 goto error;
454 }
455 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
456 goto error;
457 }
458 sprintf(interp->result, "%d", index);
459 } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
460 && (length >= 2)) {
461 int index;
462
463 if (argc != 4) {
464 Tcl_AppendResult(interp, "wrong # args: should be \"",
465 argv[0], " insert index text\"",
466 (char *) NULL);
467 goto error;
468 }
469 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
470 goto error;
471 }
472 if (entryPtr->state == tkNormalUid) {
473 InsertChars(entryPtr, index, argv[3]);
474 }
475 } else if ((c == 's') && (length >= 2)
476 && (strncmp(argv[1], "scan", length) == 0)) {
477 int x;
478
479 if (argc != 4) {
480 Tcl_AppendResult(interp, "wrong # args: should be \"",
481 argv[0], " scan mark|dragto x\"", (char *) NULL);
482 goto error;
483 }
484 if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
485 goto error;
486 }
487 if ((argv[2][0] == 'm')
488 && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
489 entryPtr->scanMarkX = x;
490 entryPtr->scanMarkIndex = entryPtr->leftIndex;
491 } else if ((argv[2][0] == 'd')
492 && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
493 EntryScanTo(entryPtr, x);
494 } else {
495 Tcl_AppendResult(interp, "bad scan option \"", argv[2],
496 "\": must be mark or dragto", (char *) NULL);
497 goto error;
498 }
499 } else if ((c == 's') && (length >= 2)
500 && (strncmp(argv[1], "select", length) == 0)) {
501 int index;
502
503 if (argc < 3) {
504 Tcl_AppendResult(interp, "too few args: should be \"",
505 argv[0], " select option ?index?\"", (char *) NULL);
506 goto error;
507 }
508 length = strlen(argv[2]);
509 c = argv[2][0];
510 if ((c == 'c') && (argv[2] != NULL)
511 && (strncmp(argv[2], "clear", length) == 0)) {
512 if (argc != 3) {
513 Tcl_AppendResult(interp, "wrong # args: should be \"",
514 argv[0], " select clear\"", (char *) NULL);
515 goto error;
516 }
517 if (entryPtr->selectFirst != -1) {
518 entryPtr->selectFirst = entryPtr->selectLast = -1;
519 EventuallyRedraw(entryPtr);
520 }
521 goto done;
522 }
523 if (argc >= 4) {
524 if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
525 goto error;
526 }
527 }
528 if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
529 if (argc != 4) {
530 Tcl_AppendResult(interp, "wrong # args: should be \"",
531 argv[0], " select adjust index\"",
532 (char *) NULL);
533 goto error;
534 }
535 if (entryPtr->selectFirst >= 0) {
536 if (index < (entryPtr->selectFirst + entryPtr->selectLast)/2) {
537 entryPtr->selectAnchor = entryPtr->selectLast + 1;
538 } else {
539 entryPtr->selectAnchor = entryPtr->selectFirst;
540 }
541 }
542 EntrySelectTo(entryPtr, index);
543 } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
544 if (argc != 4) {
545 Tcl_AppendResult(interp, "wrong # args: should be \"",
546 argv[0], " select from index\"",
547 (char *) NULL);
548 goto error;
549 }
550 entryPtr->selectAnchor = index;
551 } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
552 if (argc != 4) {
553 Tcl_AppendResult(interp, "wrong # args: should be \"",
554 argv[0], " select to index\"",
555 (char *) NULL);
556 goto error;
557 }
558 EntrySelectTo(entryPtr, index);
559 } else {
560 Tcl_AppendResult(interp, "bad select option \"", argv[2],
561 "\": must be adjust, clear, from, or to", (char *) NULL);
562 goto error;
563 }
564 } else if ((c == 'v') && (strncmp(argv[1], "view", length) == 0)) {
565 int index;
566
567 if (argc != 3) {
568 Tcl_AppendResult(interp, "wrong # args: should be \"",
569 argv[0], " view index\"", (char *) NULL);
570 goto error;
571 }
572 if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
573 goto error;
574 }
575 if ((index >= entryPtr->numChars) && (index > 0)) {
576 index = entryPtr->numChars-1;
577 }
578 entryPtr->leftIndex = index;
579 EventuallyRedraw(entryPtr);
580 EntryUpdateScrollbar(entryPtr);
581 } else {
582 Tcl_AppendResult(interp, "bad option \"", argv[1],
583 "\": must be configure, cursor, delete, get, index, ",
584 "insert, scan, select, or view", (char *) NULL);
585 goto error;
586 }
587 done:
588 Tk_Release((ClientData) entryPtr);
589 return result;
590
591 error:
592 Tk_Release((ClientData) entryPtr);
593 return TCL_ERROR;
594}
595\f
596/*
597 *----------------------------------------------------------------------
598 *
599 * DestroyEntry --
600 *
601 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
602 * to clean up the internal structure of an entry at a safe time
603 * (when no-one is using it anymore).
604 *
605 * Results:
606 * None.
607 *
608 * Side effects:
609 * Everything associated with the entry is freed up.
610 *
611 *----------------------------------------------------------------------
612 */
613
614static void
615DestroyEntry(clientData)
616 ClientData clientData; /* Info about entry widget. */
617{
618 register Entry *entryPtr = (Entry *) clientData;
619
620 ckfree(entryPtr->string);
621 if (entryPtr->normalBorder != NULL) {
622 Tk_Free3DBorder(entryPtr->normalBorder);
623 }
624 if (entryPtr->textVarName != NULL) {
625 Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
626 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
627 EntryTextVarProc, (ClientData) entryPtr);
628 ckfree(entryPtr->textVarName);
629 }
630 if (entryPtr->fontPtr != NULL) {
631 Tk_FreeFontStruct(entryPtr->fontPtr);
632 }
633 if (entryPtr->fgColorPtr != NULL) {
634 Tk_FreeColor(entryPtr->fgColorPtr);
635 }
636 if (entryPtr->textGC != None) {
637 Tk_FreeGC(entryPtr->textGC);
638 }
639 if (entryPtr->selBorder != NULL) {
640 Tk_Free3DBorder(entryPtr->selBorder);
641 }
642 if (entryPtr->selFgColorPtr != NULL) {
643 Tk_FreeColor(entryPtr->selFgColorPtr);
644 }
645 if (entryPtr->selTextGC != None) {
646 Tk_FreeGC(entryPtr->selTextGC);
647 }
648 if (entryPtr->cursorBorder != NULL) {
649 Tk_Free3DBorder(entryPtr->cursorBorder);
650 }
651 if (entryPtr->cursorBlinkHandler != NULL) {
652 Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
653 entryPtr->cursorBlinkHandler = NULL;
654 }
655 if (entryPtr->cursor != None) {
656 Tk_FreeCursor(entryPtr->cursor);
657 }
658 if (entryPtr->scrollCmd != NULL) {
659 ckfree(entryPtr->scrollCmd);
660 }
661 ckfree((char *) entryPtr);
662}
663\f
664/*
665 *----------------------------------------------------------------------
666 *
667 * ConfigureEntry --
668 *
669 * This procedure is called to process an argv/argc list, plus
670 * the Tk option database, in order to configure (or reconfigure)
671 * an entry widget.
672 *
673 * Results:
674 * The return value is a standard Tcl result. If TCL_ERROR is
675 * returned, then interp->result contains an error message.
676 *
677 * Side effects:
678 * Configuration information, such as colors, border width,
679 * etc. get set for entryPtr; old resources get freed,
680 * if there were any.
681 *
682 *----------------------------------------------------------------------
683 */
684
685static int
686ConfigureEntry(interp, entryPtr, argc, argv, flags)
687 Tcl_Interp *interp; /* Used for error reporting. */
688 register Entry *entryPtr; /* Information about widget; may or may
689 * not already have values for some fields. */
690 int argc; /* Number of valid entries in argv. */
691 char **argv; /* Arguments. */
692 int flags; /* Flags to pass to Tk_ConfigureWidget. */
693{
694 XGCValues gcValues;
695 GC new;
696 int width, height, fontHeight, oldExport;
697
698 /*
699 * Eliminate any existing trace on a variable monitored by the entry.
700 */
701
702 if (entryPtr->textVarName != NULL) {
703 Tcl_UntraceVar(interp, entryPtr->textVarName,
704 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
705 EntryTextVarProc, (ClientData) entryPtr);
706 }
707
708 oldExport = entryPtr->exportSelection;
709 if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
710 argc, argv, (char *) entryPtr, flags) != TCL_OK) {
711 return TCL_ERROR;
712 }
713
714 /*
715 * If the entry is tied to the value of a variable, then set up
716 * a trace on the variable's value, create the variable if it doesn't
717 * exist, and set the entry's value from the variable's value.
718 */
719
720 if (entryPtr->textVarName != NULL) {
721 char *value;
722
723 value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
724 if (value == NULL) {
725 Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
726 TCL_GLOBAL_ONLY);
727 } else {
728 EntrySetValue(entryPtr, value);
729 }
730 Tcl_TraceVar(interp, entryPtr->textVarName,
731 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
732 EntryTextVarProc, (ClientData) entryPtr);
733 }
734
735 /*
736 * A few other options also need special processing, such as parsing
737 * the geometry and setting the background from a 3-D border.
738 */
739
740 if ((entryPtr->state != tkNormalUid)
741 && (entryPtr->state != tkDisabledUid)) {
742 Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
743 "\": must be normal or disabled", (char *) NULL);
744 entryPtr->state = tkNormalUid;
745 return TCL_ERROR;
746 }
747
748 Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
749
750 gcValues.foreground = entryPtr->fgColorPtr->pixel;
751 gcValues.font = entryPtr->fontPtr->fid;
752 gcValues.graphics_exposures = False;
753 new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
754 &gcValues);
755 if (entryPtr->textGC != None) {
756 Tk_FreeGC(entryPtr->textGC);
757 }
758 entryPtr->textGC = new;
759
760 gcValues.foreground = entryPtr->selFgColorPtr->pixel;
761 gcValues.font = entryPtr->fontPtr->fid;
762 new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont, &gcValues);
763 if (entryPtr->selTextGC != None) {
764 Tk_FreeGC(entryPtr->selTextGC);
765 }
766 entryPtr->selTextGC = new;
767
768 if (entryPtr->cursorWidth > 2*entryPtr->fontPtr->min_bounds.width) {
769 entryPtr->cursorWidth = 2*entryPtr->fontPtr->min_bounds.width;
770 if (entryPtr->cursorWidth == 0) {
771 entryPtr->cursorWidth = 2;
772 }
773 }
774 if (entryPtr->cursorBorderWidth > entryPtr->cursorWidth/2) {
775 entryPtr->cursorBorderWidth = entryPtr->cursorWidth/2;
776 }
777
778 /*
779 * Restart the cursor timing sequence in case the on-time or off-time
780 * just changed.
781 */
782
783 if (entryPtr->flags & GOT_FOCUS) {
784 EntryFocusProc((ClientData) entryPtr, 1);
785 }
786
787 /*
788 * Claim the selection if we've suddenly started exporting it.
789 */
790
791 if (entryPtr->exportSelection && (!oldExport)
792 && (entryPtr->selectFirst != -1)) {
793 Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
794 (ClientData) entryPtr);
795 }
796
797 /*
798 * Register the desired geometry for the window, and arrange for
799 * the window to be redisplayed.
800 */
801
802 fontHeight = entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent;
803 entryPtr->avgWidth = XTextWidth(entryPtr->fontPtr, "0", 1);
804 width = entryPtr->prefWidth*entryPtr->avgWidth + (15*fontHeight)/10;
805 height = fontHeight + 2*entryPtr->borderWidth + 2;
806 Tk_GeometryRequest(entryPtr->tkwin, width, height);
807 Tk_SetInternalBorder(entryPtr->tkwin, entryPtr->borderWidth);
808 if (entryPtr->relief != TK_RELIEF_FLAT) {
809 entryPtr->offset = entryPtr->borderWidth;
810 } else {
811 entryPtr->offset = 0;
812 }
813 EventuallyRedraw(entryPtr);
814 EntryUpdateScrollbar(entryPtr);
815 return TCL_OK;
816}
817\f
818/*
819 *--------------------------------------------------------------
820 *
821 * DisplayEntry --
822 *
823 * This procedure redraws the contents of an entry window.
824 *
825 * Results:
826 * None.
827 *
828 * Side effects:
829 * Information appears on the screen.
830 *
831 *--------------------------------------------------------------
832 */
833
834static void
835DisplayEntry(clientData)
836 ClientData clientData; /* Information about window. */
837{
838 register Entry *entryPtr = (Entry *) clientData;
839 register Tk_Window tkwin = entryPtr->tkwin;
840 int startX, baseY, selStartX, selEndX, index, cursorX;
841 int xBound, count;
842 Pixmap pixmap;
843
844 entryPtr->flags &= ~REDRAW_PENDING;
845 if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
846 return;
847 }
848
849 /*
850 * In order to avoid screen flashes, this procedure redraws the
851 * textual area of the entry into off-screen memory, then copies
852 * it back on-screen in a single operation. This means there's
853 * no point in time where the on-screen image has been cleared.
854 */
855
856 pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
857 Tk_Width(tkwin), Tk_Height(tkwin),
858 Tk_DefaultDepth(Tk_Screen(tkwin)));
859
860 /*
861 * Compute x-coordinate of the "leftIndex" character, plus limit
862 * of visible x-coordinates (actually, pixel just after last visible
863 * one), plus vertical position of baseline of text.
864 */
865
866 startX = entryPtr->offset;
867 xBound = Tk_Width(tkwin) - entryPtr->offset;
868 baseY = (Tk_Height(tkwin) + entryPtr->fontPtr->ascent
869 - entryPtr->fontPtr->descent)/2;
870
871 /*
872 * Draw the background in three layers. From bottom to top the
873 * layers are: normal background, selection background, and
874 * insertion cursor background.
875 */
876
877 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, entryPtr->normalBorder,
878 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
879
880 if (entryPtr->selectLast >= entryPtr->leftIndex) {
881 if (entryPtr->selectFirst <= entryPtr->leftIndex) {
882 selStartX = startX;
883 index = entryPtr->leftIndex;
884 } else {
885 (void) TkMeasureChars(entryPtr->fontPtr,
886 entryPtr->string+entryPtr->leftIndex,
887 entryPtr->selectFirst - entryPtr->leftIndex, startX,
888 xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selStartX);
889 index = entryPtr->selectFirst;
890 }
891 if (selStartX < xBound) {
892 (void) TkMeasureChars(entryPtr->fontPtr,
893 entryPtr->string + index, entryPtr->selectLast +1 - index,
894 selStartX, xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL,
895 &selEndX);
896 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, entryPtr->selBorder,
897 selStartX - entryPtr->selBorderWidth,
898 baseY - entryPtr->fontPtr->ascent
899 - entryPtr->selBorderWidth,
900 (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
901 entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent
902 + 2*entryPtr->selBorderWidth,
903 entryPtr->selBorderWidth, TK_RELIEF_RAISED);
904 } else {
905 selEndX = xBound;
906 }
907 }
908
909 /*
910 * Draw a special background for the insertion cursor, overriding
911 * even the selection background. As a special workaround to keep the
912 * cursor visible on mono displays, write background in the cursor
913 * area (instead of nothing) when the cursor isn't on. Otherwise
914 * the selection would hide the cursor.
915 */
916
917 if ((entryPtr->cursorPos >= entryPtr->leftIndex)
918 && (entryPtr->state == tkNormalUid)
919 && (entryPtr->flags & GOT_FOCUS)) {
920 (void) TkMeasureChars(entryPtr->fontPtr,
921 entryPtr->string + entryPtr->leftIndex,
922 entryPtr->cursorPos - entryPtr->leftIndex, startX,
923 xBound, TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &cursorX);
924 if (cursorX < xBound) {
925 if (entryPtr->flags & CURSOR_ON) {
926 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
927 entryPtr->cursorBorder,
928 cursorX - (entryPtr->cursorWidth)/2,
929 baseY - entryPtr->fontPtr->ascent,
930 entryPtr->cursorWidth,
931 entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
932 entryPtr->cursorBorderWidth, TK_RELIEF_RAISED);
933 } else if (Tk_DefaultDepth(Tk_Screen(tkwin)) == 1) {
934 Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
935 entryPtr->normalBorder,
936 cursorX - (entryPtr->cursorWidth)/2,
937 baseY - entryPtr->fontPtr->ascent,
938 entryPtr->cursorWidth,
939 entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
940 0, TK_RELIEF_FLAT);
941 }
942 }
943 }
944
945 /*
946 * Draw the text in three pieces: first the piece to the left of
947 * the selection, then the selection, then the piece to the right
948 * of the selection.
949 */
950
951 if (entryPtr->selectLast < entryPtr->leftIndex) {
952 TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
953 entryPtr->fontPtr, entryPtr->string + entryPtr->leftIndex,
954 entryPtr->numChars - entryPtr->leftIndex, startX, baseY,
955 TK_NEWLINES_NOT_SPECIAL);
956 } else {
957 count = entryPtr->selectFirst - entryPtr->leftIndex;
958 if (count > 0) {
959 TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
960 entryPtr->fontPtr, entryPtr->string + entryPtr->leftIndex,
961 count, startX, baseY, TK_NEWLINES_NOT_SPECIAL);
962 index = entryPtr->selectFirst;
963 } else {
964 index = entryPtr->leftIndex;
965 }
966 count = entryPtr->selectLast + 1 - index;
967 if ((selStartX < xBound) && (count > 0)) {
968 TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->selTextGC,
969 entryPtr->fontPtr, entryPtr->string + index, count,
970 selStartX, baseY, TK_NEWLINES_NOT_SPECIAL);
971 }
972 count = entryPtr->numChars - entryPtr->selectLast - 1;
973 if ((selEndX < xBound) && (count > 0)) {
974 TkDisplayChars(Tk_Display(tkwin), pixmap, entryPtr->textGC,
975 entryPtr->fontPtr,
976 entryPtr->string + entryPtr->selectLast + 1,
977 count, selEndX, baseY, TK_NEWLINES_NOT_SPECIAL);
978 }
979 }
980
981 /*
982 * Draw the border last, so it will overwrite any text that extends
983 * past the viewable part of the window.
984 */
985
986 if (entryPtr->relief != TK_RELIEF_FLAT) {
987 Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap,
988 entryPtr->normalBorder, 0, 0, Tk_Width(tkwin),
989 Tk_Height(tkwin), entryPtr->borderWidth,
990 entryPtr->relief);
991 }
992
993 /*
994 * Everything's been redisplayed; now copy the pixmap onto the screen
995 * and free up the pixmap.
996 */
997
998 XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
999 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
1000 XFreePixmap(Tk_Display(tkwin), pixmap);
1001 entryPtr->flags &= ~BORDER_NEEDED;
1002}
1003\f
1004/*
1005 *----------------------------------------------------------------------
1006 *
1007 * InsertChars --
1008 *
1009 * Add new characters to an entry widget.
1010 *
1011 * Results:
1012 * None.
1013 *
1014 * Side effects:
1015 * New information gets added to entryPtr; it will be redisplayed
1016 * soon, but not necessarily immediately.
1017 *
1018 *----------------------------------------------------------------------
1019 */
1020
1021static void
1022InsertChars(entryPtr, index, string)
1023 register Entry *entryPtr; /* Entry that is to get the new
1024 * elements. */
1025 int index; /* Add the new elements before this
1026 * element. */
1027 char *string; /* New characters to add (NULL-terminated
1028 * string). */
1029{
1030 int length;
1031 char *new;
1032
1033 length = strlen(string);
1034 if (length == 0) {
1035 return;
1036 }
1037 new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
1038 strncpy(new, entryPtr->string, index);
1039 strcpy(new+index, string);
1040 strcpy(new+index+length, entryPtr->string+index);
1041 ckfree(entryPtr->string);
1042 entryPtr->string = new;
1043 entryPtr->numChars += length;
1044
1045 /*
1046 * Inserting characters invalidates all indexes into the string.
1047 * Touch up the indexes so that they still refer to the same
1048 * characters (at new positions).
1049 */
1050
1051 if (entryPtr->selectFirst >= index) {
1052 entryPtr->selectFirst += length;
1053 }
1054 if (entryPtr->selectLast >= index) {
1055 entryPtr->selectLast += length;
1056 }
1057 if (entryPtr->selectAnchor >= index) {
1058 entryPtr->selectAnchor += length;
1059 }
1060 if (entryPtr->leftIndex > index) {
1061 entryPtr->leftIndex += length;
1062 }
1063 if (entryPtr->cursorPos >= index) {
1064 entryPtr->cursorPos += length;
1065 }
1066
1067 if (entryPtr->textVarName != NULL) {
1068 Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1069 TCL_GLOBAL_ONLY);
1070 }
1071 EventuallyRedraw(entryPtr);
1072 EntryUpdateScrollbar(entryPtr);
1073}
1074\f
1075/*
1076 *----------------------------------------------------------------------
1077 *
1078 * DeleteChars --
1079 *
1080 * Remove one or more characters from an entry widget.
1081 *
1082 * Results:
1083 * None.
1084 *
1085 * Side effects:
1086 * Memory gets freed, the entry gets modified and (eventually)
1087 * redisplayed.
1088 *
1089 *----------------------------------------------------------------------
1090 */
1091
1092static void
1093DeleteChars(entryPtr, index, count)
1094 register Entry *entryPtr; /* Entry widget to modify. */
1095 int index; /* Index of first character to delete. */
1096 int count; /* How many characters to delete. */
1097{
1098 char *new;
1099
1100 if ((index + count) > entryPtr->numChars) {
1101 count = entryPtr->numChars - index;
1102 }
1103 if (count <= 0) {
1104 return;
1105 }
1106
1107 new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
1108 strncpy(new, entryPtr->string, index);
1109 strcpy(new+index, entryPtr->string+index+count);
1110 ckfree(entryPtr->string);
1111 entryPtr->string = new;
1112 entryPtr->numChars -= count;
1113
1114 /*
1115 * Deleting characters results in the remaining characters being
1116 * renumbered. Update the various indexes into the string to reflect
1117 * this change.
1118 */
1119 if (entryPtr->selectFirst >= index) {
1120 if (entryPtr->selectFirst >= (index+count)) {
1121 entryPtr->selectFirst -= count;
1122 } else {
1123 entryPtr->selectFirst = index;
1124 }
1125 }
1126 if (entryPtr->selectLast >= index) {
1127 if (entryPtr->selectLast >= (index+count)) {
1128 entryPtr->selectLast -= count;
1129 } else {
1130 entryPtr->selectLast = index-1;
1131 }
1132 }
1133 if (entryPtr->selectLast < entryPtr->selectFirst) {
1134 entryPtr->selectFirst = entryPtr->selectLast = -1;
1135 }
1136 if (entryPtr->selectAnchor >= index) {
1137 if (entryPtr->selectAnchor >= (index+count)) {
1138 entryPtr->selectAnchor -= count;
1139 } else {
1140 entryPtr->selectAnchor = index;
1141 }
1142 }
1143 if (entryPtr->leftIndex > index) {
1144 if (entryPtr->leftIndex >= (index+count)) {
1145 entryPtr->leftIndex -= count;
1146 } else {
1147 entryPtr->leftIndex = index;
1148 }
1149 }
1150 if (entryPtr->cursorPos >= index) {
1151 if (entryPtr->cursorPos >= (index+count)) {
1152 entryPtr->cursorPos -= count;
1153 } else {
1154 entryPtr->cursorPos = index;
1155 }
1156 }
1157
1158 if (entryPtr->textVarName != NULL) {
1159 Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1160 TCL_GLOBAL_ONLY);
1161 }
1162 EventuallyRedraw(entryPtr);
1163 EntryUpdateScrollbar(entryPtr);
1164}
1165\f
1166/*
1167 *----------------------------------------------------------------------
1168 *
1169 * EntrySetValue --
1170 *
1171 * Replace the contents of a text entry with a given value. This
1172 * procedure is invoked when updating the entry from the entry's
1173 * associated variable.
1174 *
1175 * Results:
1176 * None.
1177 *
1178 * Side effects:
1179 * The string displayed in the entry will change. Any selection
1180 * in the entry is lost and the insertion point gets set to the
1181 * end of the entry. Note: this procedure does *not* update the
1182 * entry's associated variable, since that could result in an
1183 * infinite loop.
1184 *
1185 *----------------------------------------------------------------------
1186 */
1187
1188static void
1189EntrySetValue(entryPtr, value)
1190 register Entry *entryPtr; /* Entry whose value is to be
1191 * changed. */
1192 char *value; /* New text to display in entry. */
1193{
1194 ckfree(entryPtr->string);
1195 entryPtr->numChars = strlen(value);
1196 entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
1197 strcpy(entryPtr->string, value);
1198 entryPtr->selectFirst = entryPtr->selectLast = -1;
1199 entryPtr->leftIndex = 0;
1200 entryPtr->cursorPos = entryPtr->numChars;
1201
1202 EventuallyRedraw(entryPtr);
1203 EntryUpdateScrollbar(entryPtr);
1204}
1205\f
1206/*
1207 *--------------------------------------------------------------
1208 *
1209 * EntryEventProc --
1210 *
1211 * This procedure is invoked by the Tk dispatcher for various
1212 * events on entryes.
1213 *
1214 * Results:
1215 * None.
1216 *
1217 * Side effects:
1218 * When the window gets deleted, internal structures get
1219 * cleaned up. When it gets exposed, it is redisplayed.
1220 *
1221 *--------------------------------------------------------------
1222 */
1223
1224static void
1225EntryEventProc(clientData, eventPtr)
1226 ClientData clientData; /* Information about window. */
1227 XEvent *eventPtr; /* Information about event. */
1228{
1229 Entry *entryPtr = (Entry *) clientData;
1230 if (eventPtr->type == Expose) {
1231 EventuallyRedraw(entryPtr);
1232 entryPtr->flags |= BORDER_NEEDED;
1233 } else if (eventPtr->type == DestroyNotify) {
1234 Tcl_DeleteCommand(entryPtr->interp, Tk_PathName(entryPtr->tkwin));
1235 entryPtr->tkwin = NULL;
1236 if (entryPtr->flags & REDRAW_PENDING) {
1237 Tk_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1238 }
1239 Tk_EventuallyFree((ClientData) entryPtr, DestroyEntry);
1240 } else if (eventPtr->type == ConfigureNotify) {
1241 Tk_Preserve((ClientData) entryPtr);
1242 EventuallyRedraw(entryPtr);
1243 EntryUpdateScrollbar(entryPtr);
1244 Tk_Release((ClientData) entryPtr);
1245 }
1246}
1247\f
1248/*
1249 *--------------------------------------------------------------
1250 *
1251 * GetEntryIndex --
1252 *
1253 * Parse an index into an entry and return either its value
1254 * or an error.
1255 *
1256 * Results:
1257 * A standard Tcl result. If all went well, then *indexPtr is
1258 * filled in with the index (into entryPtr) corresponding to
1259 * string. The index value is guaranteed to lie between 0 and
1260 * the number of characters in the string, inclusive. If an
1261 * error occurs then an error message is left in interp->result.
1262 *
1263 * Side effects:
1264 * None.
1265 *
1266 *--------------------------------------------------------------
1267 */
1268
1269static int
1270GetEntryIndex(interp, entryPtr, string, indexPtr)
1271 Tcl_Interp *interp; /* For error messages. */
1272 Entry *entryPtr; /* Entry for which the index is being
1273 * specified. */
1274 char *string; /* Specifies character in entryPtr. */
1275 int *indexPtr; /* Where to store converted index. */
1276{
1277 int length;
1278
1279 length = strlen(string);
1280
1281 if (string[0] == 'e') {
1282 if (strncmp(string, "end", length) == 0) {
1283 *indexPtr = entryPtr->numChars;
1284 } else {
1285 badIndex:
1286
1287 /*
1288 * Some of the paths here leave messages in interp->result,
1289 * so we have to clear it out before storing our own message.
1290 */
1291
1292 Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1293 Tcl_AppendResult(interp, "bad entry index \"", string,
1294 "\"", (char *) NULL);
1295 return TCL_ERROR;
1296 }
1297 } else if (string[0] == 'c') {
1298 if (strncmp(string, "cursor", length) == 0) {
1299 *indexPtr = entryPtr->cursorPos;
1300 } else {
1301 goto badIndex;
1302 }
1303 } else if (string[0] == 's') {
1304 if (entryPtr->selectFirst == -1) {
1305 interp->result = "selection isn't in entry";
1306 return TCL_ERROR;
1307 }
1308 if (length < 5) {
1309 goto badIndex;
1310 }
1311 if (strncmp(string, "sel.first", length) == 0) {
1312 *indexPtr = entryPtr->selectFirst;
1313 } else if (strncmp(string, "sel.last", length) == 0) {
1314 *indexPtr = entryPtr->selectLast;
1315 } else {
1316 goto badIndex;
1317 }
1318 } else if (string[0] == '@') {
1319 int x, dummy;
1320
1321 if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
1322 goto badIndex;
1323 }
1324 if (entryPtr->numChars == 0) {
1325 *indexPtr = 0;
1326 } else {
1327 *indexPtr = entryPtr->leftIndex + TkMeasureChars(entryPtr->fontPtr,
1328 entryPtr->string + entryPtr->leftIndex,
1329 entryPtr->numChars - entryPtr->leftIndex,
1330 entryPtr->offset, x, TK_NEWLINES_NOT_SPECIAL, &dummy);
1331 }
1332 } else {
1333 if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1334 goto badIndex;
1335 }
1336 if (*indexPtr < 0){
1337 *indexPtr = 0;
1338 } else if (*indexPtr > entryPtr->numChars) {
1339 *indexPtr = entryPtr->numChars;
1340 }
1341 }
1342 return TCL_OK;
1343}
1344\f
1345/*
1346 *----------------------------------------------------------------------
1347 *
1348 * EntryScanTo --
1349 *
1350 * Given a y-coordinate (presumably of the curent mouse location)
1351 * drag the view in the window to implement the scan operation.
1352 *
1353 * Results:
1354 * None.
1355 *
1356 * Side effects:
1357 * The view in the window may change.
1358 *
1359 *----------------------------------------------------------------------
1360 */
1361
1362static void
1363EntryScanTo(entryPtr, x)
1364 register Entry *entryPtr; /* Information about widget. */
1365 int x; /* X-coordinate to use for scan
1366 * operation. */
1367{
1368 int newLeftIndex;
1369
1370 /*
1371 * Compute new leftIndex for entry by amplifying the difference
1372 * between the current position and the place where the scan
1373 * started (the "mark" position). If we run off the left or right
1374 * side of the entry, then reset the mark point so that the current
1375 * position continues to correspond to the edge of the window.
1376 * This means that the picture will start dragging as soon as the
1377 * mouse reverses direction (without this reset, might have to slide
1378 * mouse a long ways back before the picture starts moving again).
1379 */
1380
1381 newLeftIndex = entryPtr->scanMarkIndex
1382 - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
1383 if (newLeftIndex >= entryPtr->numChars) {
1384 newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
1385 entryPtr->scanMarkX = x;
1386 }
1387 if (newLeftIndex < 0) {
1388 newLeftIndex = entryPtr->scanMarkIndex = 0;
1389 entryPtr->scanMarkX = x;
1390 }
1391 if (newLeftIndex != entryPtr->leftIndex) {
1392 entryPtr->leftIndex = newLeftIndex;
1393 EventuallyRedraw(entryPtr);
1394 EntryUpdateScrollbar(entryPtr);
1395 }
1396}
1397\f
1398/*
1399 *----------------------------------------------------------------------
1400 *
1401 * EntrySelectTo --
1402 *
1403 * Modify the selection by moving its un-anchored end. This could
1404 * make the selection either larger or smaller.
1405 *
1406 * Results:
1407 * None.
1408 *
1409 * Side effects:
1410 * The selection changes.
1411 *
1412 *----------------------------------------------------------------------
1413 */
1414
1415static void
1416EntrySelectTo(entryPtr, index)
1417 register Entry *entryPtr; /* Information about widget. */
1418 int index; /* Index of element that is to
1419 * become the "other" end of the
1420 * selection. */
1421{
1422 int newFirst, newLast;
1423
1424 /*
1425 * Grab the selection if we don't own it already.
1426 */
1427
1428 if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
1429 Tk_OwnSelection(entryPtr->tkwin, EntryLostSelection,
1430 (ClientData) entryPtr);
1431 }
1432
1433 if (index < 0) {
1434 index = 0;
1435 }
1436 if (index >= entryPtr->numChars) {
1437 index = entryPtr->numChars-1;
1438 }
1439 if (entryPtr->selectAnchor > entryPtr->numChars) {
1440 entryPtr->selectAnchor = entryPtr->numChars;
1441 }
1442 if (entryPtr->selectAnchor <= index) {
1443 newFirst = entryPtr->selectAnchor;
1444 newLast = index;
1445 } else {
1446 newFirst = index;
1447 newLast = entryPtr->selectAnchor - 1;
1448 if (newLast < 0) {
1449 newFirst = newLast = -1;
1450 }
1451 }
1452 if ((entryPtr->selectFirst == newFirst)
1453 && (entryPtr->selectLast == newLast)) {
1454 return;
1455 }
1456 entryPtr->selectFirst = newFirst;
1457 entryPtr->selectLast = newLast;
1458 EventuallyRedraw(entryPtr);
1459}
1460\f
1461/*
1462 *----------------------------------------------------------------------
1463 *
1464 * EntryFetchSelection --
1465 *
1466 * This procedure is called back by Tk when the selection is
1467 * requested by someone. It returns part or all of the selection
1468 * in a buffer provided by the caller.
1469 *
1470 * Results:
1471 * The return value is the number of non-NULL bytes stored
1472 * at buffer. Buffer is filled (or partially filled) with a
1473 * NULL-terminated string containing part or all of the selection,
1474 * as given by offset and maxBytes.
1475 *
1476 * Side effects:
1477 * None.
1478 *
1479 *----------------------------------------------------------------------
1480 */
1481
1482static int
1483EntryFetchSelection(clientData, offset, buffer, maxBytes)
1484 ClientData clientData; /* Information about entry widget. */
1485 int offset; /* Offset within selection of first
1486 * character to be returned. */
1487 char *buffer; /* Location in which to place
1488 * selection. */
1489 int maxBytes; /* Maximum number of bytes to place
1490 * at buffer, not including terminating
1491 * NULL character. */
1492{
1493 Entry *entryPtr = (Entry *) clientData;
1494 int count;
1495
1496 if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
1497 return -1;
1498 }
1499 count = entryPtr->selectLast + 1 - entryPtr->selectFirst - offset;
1500 if (count > maxBytes) {
1501 count = maxBytes;
1502 }
1503 if (count <= 0) {
1504 return 0;
1505 }
1506 strncpy(buffer, entryPtr->string + entryPtr->selectFirst + offset, count);
1507 buffer[count] = '\0';
1508 return count;
1509}
1510\f
1511/*
1512 *----------------------------------------------------------------------
1513 *
1514 * EntryLostSelection --
1515 *
1516 * This procedure is called back by Tk when the selection is
1517 * grabbed away from an entry widget.
1518 *
1519 * Results:
1520 * None.
1521 *
1522 * Side effects:
1523 * The existing selection is unhighlighted, and the window is
1524 * marked as not containing a selection.
1525 *
1526 *----------------------------------------------------------------------
1527 */
1528
1529static void
1530EntryLostSelection(clientData)
1531 ClientData clientData; /* Information about entry widget. */
1532{
1533 Entry *entryPtr = (Entry *) clientData;
1534
1535 if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
1536 entryPtr->selectFirst = -1;
1537 entryPtr->selectLast = -1;
1538 EventuallyRedraw(entryPtr);
1539 }
1540}
1541\f
1542/*
1543 *----------------------------------------------------------------------
1544 *
1545 * EventuallyRedraw --
1546 *
1547 * Ensure that an entry is eventually redrawn on the display.
1548 *
1549 * Results:
1550 * None.
1551 *
1552 * Side effects:
1553 * Information gets redisplayed. Right now we don't do selective
1554 * redisplays: the whole window will be redrawn. This doesn't
1555 * seem to hurt performance noticeably, but if it does then this
1556 * could be changed.
1557 *
1558 *----------------------------------------------------------------------
1559 */
1560
1561static void
1562EventuallyRedraw(entryPtr)
1563 register Entry *entryPtr; /* Information about widget. */
1564{
1565 if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
1566 return;
1567 }
1568
1569 /*
1570 * Right now we don't do selective redisplays: the whole window
1571 * will be redrawn. This doesn't seem to hurt performance noticeably,
1572 * but if it does then this could be changed.
1573 */
1574
1575 if (!(entryPtr->flags & REDRAW_PENDING)) {
1576 entryPtr->flags |= REDRAW_PENDING;
1577 Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
1578 }
1579}
1580\f
1581/*
1582 *----------------------------------------------------------------------
1583 *
1584 * EntryUpdateScrollbar --
1585 *
1586 * This procedure is invoked whenever information has changed in
1587 * an entry in a way that would invalidate a scrollbar display.
1588 * If there is an associated scrollbar, then this command updates
1589 * it by invoking a Tcl command.
1590 *
1591 * Results:
1592 * None.
1593 *
1594 * Side effects:
1595 * A Tcl command is invoked, and an additional command may be
1596 * invoked to process errors in the command.
1597 *
1598 *----------------------------------------------------------------------
1599 */
1600
1601static void
1602EntryUpdateScrollbar(entryPtr)
1603 register Entry *entryPtr; /* Information about widget. */
1604{
1605 char args[100];
1606 int result, last, charsInWindow, endX;
1607
1608 if (entryPtr->scrollCmd == NULL) {
1609 return;
1610 }
1611
1612 /*
1613 * The most painful part here is guessing how many characters
1614 * actually fit in the window. This is only an estimate in the
1615 * case where the window isn't completely filled with characters.
1616 */
1617
1618 charsInWindow = TkMeasureChars(entryPtr->fontPtr,
1619 entryPtr->string + entryPtr->leftIndex,
1620 entryPtr->numChars - entryPtr->leftIndex, entryPtr->offset,
1621 Tk_Width(entryPtr->tkwin),
1622 TK_AT_LEAST_ONE|TK_NEWLINES_NOT_SPECIAL, &endX);
1623 if (charsInWindow == 0) {
1624 last = entryPtr->leftIndex;
1625 } else {
1626 last = entryPtr->leftIndex + charsInWindow - 1;
1627 }
1628 if (endX < Tk_Width(entryPtr->tkwin)) {
1629 charsInWindow += (Tk_Width(entryPtr->tkwin) - endX)/entryPtr->avgWidth;
1630 }
1631 sprintf(args, " %d %d %d %d", entryPtr->numChars, charsInWindow,
1632 entryPtr->leftIndex, last);
1633 result = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
1634 (char *) NULL);
1635 if (result != TCL_OK) {
1636 TkBindError(entryPtr->interp);
1637 }
1638 Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
1639}
1640\f
1641/*
1642 *----------------------------------------------------------------------
1643 *
1644 * EntryBlinkProc --
1645 *
1646 * This procedure is called as a timer handler to blink the
1647 * insertion cursor off and on.
1648 *
1649 * Results:
1650 * None.
1651 *
1652 * Side effects:
1653 * The cursor gets turned on or off, redisplay gets invoked,
1654 * and this procedure reschedules itself.
1655 *
1656 *----------------------------------------------------------------------
1657 */
1658
1659static void
1660EntryBlinkProc(clientData)
1661 ClientData clientData; /* Pointer to record describing entry. */
1662{
1663 register Entry *entryPtr = (Entry *) clientData;
1664
1665 if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->cursorOffTime == 0)) {
1666 return;
1667 }
1668 if (entryPtr->flags & CURSOR_ON) {
1669 entryPtr->flags &= ~CURSOR_ON;
1670 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1671 entryPtr->cursorOffTime, EntryBlinkProc, (ClientData) entryPtr);
1672 } else {
1673 entryPtr->flags |= CURSOR_ON;
1674 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1675 entryPtr->cursorOnTime, EntryBlinkProc, (ClientData) entryPtr);
1676 }
1677 EventuallyRedraw(entryPtr);
1678}
1679\f
1680/*
1681 *----------------------------------------------------------------------
1682 *
1683 * EntryFocusProc --
1684 *
1685 * This procedure is called whenever the entry gets or loses the
1686 * input focus. It's also called whenever the window is reconfigured
1687 * while it has the focus.
1688 *
1689 * Results:
1690 * None.
1691 *
1692 * Side effects:
1693 * The cursor gets turned on or off.
1694 *
1695 *----------------------------------------------------------------------
1696 */
1697
1698static void
1699EntryFocusProc(clientData, gotFocus)
1700 ClientData clientData; /* Pointer to structure describing entry. */
1701 int gotFocus; /* 1 means window is getting focus, 0 means
1702 * it's losing it. */
1703{
1704 register Entry *entryPtr = (Entry *) clientData;
1705
1706 if (entryPtr->cursorBlinkHandler != NULL) {
1707 Tk_DeleteTimerHandler(entryPtr->cursorBlinkHandler);
1708 entryPtr->cursorBlinkHandler = NULL;
1709 }
1710 if (gotFocus) {
1711 entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
1712 if (entryPtr->cursorOffTime != 0) {
1713 entryPtr->cursorBlinkHandler = Tk_CreateTimerHandler(
1714 entryPtr->cursorOnTime, EntryBlinkProc,
1715 (ClientData) entryPtr);
1716 }
1717 } else {
1718 entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
1719 entryPtr->cursorBlinkHandler = (Tk_TimerToken) NULL;
1720 }
1721 EventuallyRedraw(entryPtr);
1722}
1723\f
1724/*
1725 *--------------------------------------------------------------
1726 *
1727 * EntryTextVarProc --
1728 *
1729 * This procedure is invoked when someone changes the variable
1730 * whose contents are to be displayed in an entry.
1731 *
1732 * Results:
1733 * NULL is always returned.
1734 *
1735 * Side effects:
1736 * The text displayed in the entry will change to match the
1737 * variable.
1738 *
1739 *--------------------------------------------------------------
1740 */
1741
1742 /* ARGSUSED */
1743static char *
1744EntryTextVarProc(clientData, interp, name1, name2, flags)
1745 ClientData clientData; /* Information about button. */
1746 Tcl_Interp *interp; /* Interpreter containing variable. */
1747 char *name1; /* Name of variable. */
1748 char *name2; /* Second part of variable name. */
1749 int flags; /* Information about what happened. */
1750{
1751 register Entry *entryPtr = (Entry *) clientData;
1752 char *value;
1753
1754 /*
1755 * If the variable is unset, then immediately recreate it unless
1756 * the whole interpreter is going away.
1757 */
1758
1759 if (flags & TCL_TRACE_UNSETS) {
1760 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1761 Tcl_SetVar2(interp, name1, name2, entryPtr->string,
1762 flags & TCL_GLOBAL_ONLY);
1763 Tcl_TraceVar2(interp, name1, name2,
1764 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1765 EntryTextVarProc, clientData);
1766 }
1767 return (char *) NULL;
1768 }
1769
1770 /*
1771 * Update the entry's text with the value of the variable, unless
1772 * the entry already has that value (this happens when the variable
1773 * changes value because we changed it because someone typed in
1774 * the entry).
1775 */
1776
1777 value = Tcl_GetVar2(interp, name1, name2, flags & TCL_GLOBAL_ONLY);
1778 if (value == NULL) {
1779 value = "";
1780 }
1781 if (strcmp(value, entryPtr->string) != 0) {
1782 EntrySetValue(entryPtr, value);
1783 }
1784 return (char *) NULL;
1785}
Impressum, Datenschutz