|
|
1.1 root 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:
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(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:
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:
372: static int
373: EntryWidgetCmd(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:
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:
614: static void
615: DestroyEntry(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:
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:
685: static int
686: ConfigureEntry(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:
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:
834: static void
835: DisplayEntry(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:
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:
1021: static void
1022: InsertChars(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:
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:
1092: static void
1093: DeleteChars(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:
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:
1188: static void
1189: EntrySetValue(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:
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:
1224: static void
1225: EntryEventProc(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:
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:
1269: static int
1270: GetEntryIndex(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:
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:
1362: static void
1363: EntryScanTo(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:
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:
1415: static void
1416: EntrySelectTo(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:
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:
1482: static int
1483: EntryFetchSelection(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:
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:
1529: static void
1530: EntryLostSelection(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:
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:
1561: static void
1562: EventuallyRedraw(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:
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:
1601: static void
1602: EntryUpdateScrollbar(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:
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:
1659: static void
1660: EntryBlinkProc(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:
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:
1698: static void
1699: EntryFocusProc(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:
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 */
1743: static char *
1744: EntryTextVarProc(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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.