]>
Commit | Line | Data |
---|---|---|
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 | |
19 | static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkEntry.c,v 1.37 92/08/21 16:09:15 ouster Exp $ SPRITE (Berkeley)"; | |
20 | #endif | |
21 | ||
22 | #include "default.h" | |
23 | #include "tkconfig.h" | |
24 | #include "tkint.h" | |
25 | ||
26 | /* | |
27 | * A data structure of the following type is kept for each entry | |
28 | * widget managed by this file: | |
29 | */ | |
30 | ||
31 | typedef struct { | |
32 | Tk_Window tkwin; /* Window that embodies the entry. NULL | |
33 | * means that the window has been destroyed | |
34 | * but the data structures haven't yet been | |
35 | * cleaned up.*/ | |
36 | Tcl_Interp *interp; /* Interpreter associated with entry. */ | |
37 | int numChars; /* Number of non-NULL characters in | |
38 | * string (may be 0). */ | |
39 | char *string; /* Pointer to storage for string; | |
40 | * NULL-terminated; malloc-ed. */ | |
41 | char *textVarName; /* Name of variable (malloc'ed) or NULL. | |
42 | * If non-NULL, entry's string tracks the | |
43 | * contents of this variable and vice versa. */ | |
44 | Tk_Uid state; /* Normal or disabled. Entry is read-only | |
45 | * when disabled. */ | |
46 | ||
47 | /* | |
48 | * Information used when displaying widget: | |
49 | */ | |
50 | ||
51 | Tk_3DBorder normalBorder; /* Used for drawing border around whole | |
52 | * window, plus used for background. */ | |
53 | int borderWidth; /* Width of 3-D border around window. */ | |
54 | int relief; /* 3-D effect: TK_RELIEF_RAISED, etc. */ | |
55 | XFontStruct *fontPtr; /* Information about text font, or NULL. */ | |
56 | XColor *fgColorPtr; /* Text color in normal mode. */ | |
57 | GC textGC; /* For drawing normal text. */ | |
58 | Tk_3DBorder selBorder; /* Border and background for selected | |
59 | * characters. */ | |
60 | int selBorderWidth; /* Width of border around selection. */ | |
61 | XColor *selFgColorPtr; /* Foreground color for selected text. */ | |
62 | GC selTextGC; /* For drawing selected text. */ | |
63 | Tk_3DBorder cursorBorder; /* Used to draw vertical bar for insertion | |
64 | * cursor. */ | |
65 | int cursorWidth; /* Total width of insert cursor. */ | |
66 | int cursorBorderWidth; /* Width of 3-D border around insert cursor. */ | |
67 | int cursorOnTime; /* Number of milliseconds cursor should spend | |
68 | * in "on" state for each blink. */ | |
69 | int cursorOffTime; /* Number of milliseconds cursor should spend | |
70 | * in "off" state for each blink. */ | |
71 | Tk_TimerToken cursorBlinkHandler; | |
72 | /* Timer handler used to blink cursor on and | |
73 | * off. */ | |
74 | int avgWidth; /* Width of average character. */ | |
75 | int prefWidth; /* Desired width of window, measured in | |
76 | * average characters. */ | |
77 | int offset; /* 0 if window is flat, or borderWidth if | |
78 | * raised or sunken. */ | |
79 | int leftIndex; /* Index of left-most character visible in | |
80 | * window. */ | |
81 | int cursorPos; /* Index of character before which next | |
82 | * typed character will be inserted. */ | |
83 | ||
84 | /* | |
85 | * Information about what's selected, if any. | |
86 | */ | |
87 | ||
88 | int selectFirst; /* Index of first selected character (-1 means | |
89 | * nothing selected. */ | |
90 | int selectLast; /* Index of last selected character (-1 means | |
91 | * nothing selected. */ | |
92 | int selectAnchor; /* Fixed end of selection (i.e. "select to" | |
93 | * operation will use this as one end of the | |
94 | * selection). */ | |
95 | int exportSelection; /* Non-zero means tie internal entry selection | |
96 | * to X selection. */ | |
97 | ||
98 | /* | |
99 | * Information for scanning: | |
100 | */ | |
101 | ||
102 | int scanMarkX; /* X-position at which scan started (e.g. | |
103 | * button was pressed here). */ | |
104 | int scanMarkIndex; /* Index of character that was at left of | |
105 | * window when scan started. */ | |
106 | ||
107 | /* | |
108 | * Miscellaneous information: | |
109 | */ | |
110 | ||
111 | Cursor cursor; /* Current cursor for window, or None. */ | |
112 | char *scrollCmd; /* Command prefix for communicating with | |
113 | * scrollbar(s). Malloc'ed. NULL means | |
114 | * no command to issue. */ | |
115 | int flags; /* Miscellaneous flags; see below for | |
116 | * definitions. */ | |
117 | } Entry; | |
118 | ||
119 | /* | |
120 | * Assigned bits of "flags" fields of Entry structures, and what those | |
121 | * bits mean: | |
122 | * | |
123 | * REDRAW_PENDING: Non-zero means a DoWhenIdle handler has | |
124 | * already been queued to redisplay the entry. | |
125 | * BORDER_NEEDED: Non-zero means 3-D border must be redrawn | |
126 | * around window during redisplay. Normally | |
127 | * only text portion needs to be redrawn. | |
128 | * CURSOR_ON: Non-zero means cursor is displayed at | |
129 | * present. 0 means it isn't displayed. | |
130 | * GOT_FOCUS: Non-zero means this window has the input | |
131 | * focus. | |
132 | */ | |
133 | ||
134 | #define REDRAW_PENDING 1 | |
135 | #define BORDER_NEEDED 2 | |
136 | #define CURSOR_ON 4 | |
137 | #define GOT_FOCUS 8 | |
138 | ||
139 | /* | |
140 | * Information used for argv parsing. | |
141 | */ | |
142 | ||
143 | static Tk_ConfigSpec configSpecs[] = { | |
144 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
145 | DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder), | |
146 | TK_CONFIG_COLOR_ONLY}, | |
147 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
148 | DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder), | |
149 | TK_CONFIG_MONO_ONLY}, | |
150 | {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL, | |
151 | (char *) NULL, 0, 0}, | |
152 | {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL, | |
153 | (char *) NULL, 0, 0}, | |
154 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
155 | DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0}, | |
156 | {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", | |
157 | DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK}, | |
158 | {TK_CONFIG_BORDER, "-cursorbackground", "cursorBackground", "Foreground", | |
159 | DEF_ENTRY_CURSOR_BG, Tk_Offset(Entry, cursorBorder), 0}, | |
160 | {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth", | |
161 | DEF_ENTRY_CURSOR_BD_COLOR, Tk_Offset(Entry, cursorBorderWidth), | |
162 | TK_CONFIG_COLOR_ONLY}, | |
163 | {TK_CONFIG_PIXELS, "-cursorborderwidth", "cursorBorderWidth", "BorderWidth", | |
164 | DEF_ENTRY_CURSOR_BD_MONO, Tk_Offset(Entry, cursorBorderWidth), | |
165 | TK_CONFIG_MONO_ONLY}, | |
166 | {TK_CONFIG_INT, "-cursorofftime", "cursorOffTime", "OffTime", | |
167 | DEF_ENTRY_CURSOR_OFF_TIME, Tk_Offset(Entry, cursorOffTime), 0}, | |
168 | {TK_CONFIG_INT, "-cursorontime", "cursorOnTime", "OnTime", | |
169 | DEF_ENTRY_CURSOR_ON_TIME, Tk_Offset(Entry, cursorOnTime), 0}, | |
170 | {TK_CONFIG_PIXELS, "-cursorwidth", "cursorWidth", "CursorWidth", | |
171 | DEF_ENTRY_CURSOR_WIDTH, Tk_Offset(Entry, cursorWidth), 0}, | |
172 | {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection", | |
173 | "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, | |
174 | Tk_Offset(Entry, exportSelection), 0}, | |
175 | {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL, | |
176 | (char *) NULL, 0, 0}, | |
177 | {TK_CONFIG_FONT, "-font", "font", "Font", | |
178 | DEF_ENTRY_FONT, Tk_Offset(Entry, fontPtr), 0}, | |
179 | {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground", | |
180 | DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0}, | |
181 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
182 | DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0}, | |
183 | {TK_CONFIG_STRING, "-scrollcommand", "scrollCommand", "ScrollCommand", | |
184 | DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd), 0}, | |
185 | {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", | |
186 | DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder), | |
187 | TK_CONFIG_COLOR_ONLY}, | |
188 | {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground", | |
189 | DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder), | |
190 | TK_CONFIG_MONO_ONLY}, | |
191 | {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", | |
192 | DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth), | |
193 | TK_CONFIG_COLOR_ONLY}, | |
194 | {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth", | |
195 | DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth), | |
196 | TK_CONFIG_MONO_ONLY}, | |
197 | {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", | |
198 | DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr), | |
199 | TK_CONFIG_COLOR_ONLY}, | |
200 | {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background", | |
201 | DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr), | |
202 | TK_CONFIG_MONO_ONLY}, | |
203 | {TK_CONFIG_UID, "-state", "state", "State", | |
204 | DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0}, | |
205 | {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable", | |
206 | DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName), | |
207 | TK_CONFIG_NULL_OK}, | |
208 | {TK_CONFIG_INT, "-width", "width", "Width", | |
209 | DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0}, | |
210 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
211 | (char *) NULL, 0, 0} | |
212 | }; | |
213 | ||
214 | /* | |
215 | * Flags for GetEntryIndex procedure: | |
216 | */ | |
217 | ||
218 | #define ZERO_OK 1 | |
219 | #define LAST_PLUS_ONE_OK 2 | |
220 | ||
221 | /* | |
222 | * Forward declarations for procedures defined later in this file: | |
223 | */ | |
224 | ||
225 | static int ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp, | |
226 | Entry *entryPtr, int argc, char **argv, | |
227 | int flags)); | |
228 | static void DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index, | |
229 | int count)); | |
230 | static void DestroyEntry _ANSI_ARGS_((ClientData clientData)); | |
231 | static void DisplayEntry _ANSI_ARGS_((ClientData clientData)); | |
232 | static int GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp, | |
233 | Entry *entryPtr, char *string, int *indexPtr)); | |
234 | static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index, | |
235 | char *string)); | |
236 | static void EntryBlinkProc _ANSI_ARGS_((ClientData clientData)); | |
237 | static void EntryEventProc _ANSI_ARGS_((ClientData clientData, | |
238 | XEvent *eventPtr)); | |
239 | static void EntryFocusProc _ANSI_ARGS_ ((ClientData clientData, | |
240 | int gotFocus)); | |
241 | static int EntryFetchSelection _ANSI_ARGS_((ClientData clientData, | |
242 | int offset, char *buffer, int maxBytes)); | |
243 | static void EntryLostSelection _ANSI_ARGS_(( | |
244 | ClientData clientData)); | |
245 | static void EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr)); | |
246 | static void EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y)); | |
247 | static void EntrySetValue _ANSI_ARGS_((Entry *entryPtr, | |
248 | char *value)); | |
249 | static void EntrySelectTo _ANSI_ARGS_(( | |
250 | Entry *entryPtr, int index)); | |
251 | static char * EntryTextVarProc _ANSI_ARGS_((ClientData clientData, | |
252 | Tcl_Interp *interp, char *name1, char *name2, | |
253 | int flags)); | |
254 | static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr)); | |
255 | static int EntryWidgetCmd _ANSI_ARGS_((ClientData clientData, | |
256 | Tcl_Interp *interp, int argc, char **argv)); | |
257 | \f | |
258 | /* | |
259 | *-------------------------------------------------------------- | |
260 | * | |
261 | * Tk_EntryCmd -- | |
262 | * | |
263 | * This procedure is invoked to process the "entry" Tcl | |
264 | * command. See the user documentation for details on what | |
265 | * it does. | |
266 | * | |
267 | * Results: | |
268 | * A standard Tcl result. | |
269 | * | |
270 | * Side effects: | |
271 | * See the user documentation. | |
272 | * | |
273 | *-------------------------------------------------------------- | |
274 | */ | |
275 | ||
276 | int | |
277 | Tk_EntryCmd(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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 | ||
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 | \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 */ | |
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 | } |