]> www.wagner.pp.ru Git - oss/ck.git/blob - ckEntry.c
Ck console graphics toolkit
[oss/ck.git] / ckEntry.c
1 /* 
2  * ckEntry.c --
3  *
4  *      This module implements entry widgets for the
5  *      toolkit.  An entry displays a string and allows
6  *      the string to be edited.
7  *
8  * Copyright (c) 1990-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  * Copyright (c) 1995 Christian Werner
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  */
15
16 #include "ckPort.h"
17 #include "ck.h"
18 #include "default.h"
19
20 /*
21  * A data structure of the following type is kept for each entry
22  * widget managed by this file:
23  */
24
25 typedef struct {
26     CkWindow *winPtr;           /* Window that embodies the entry. NULL
27                                  * means that the window has been destroyed
28                                  * but the data structures haven't yet been
29                                  * cleaned up.*/
30     Tcl_Interp *interp;         /* Interpreter associated with entry. */
31     Tcl_Command widgetCmd;      /* Token for entry's widget command. */
32 #if CK_USE_UTF
33     int numBytes;               /* Number of bytes in string. */
34 #endif
35     int numChars;               /* Number of non-NULL characters in
36                                  * string (may be 0). */
37     char *string;               /* Pointer to storage for string;
38                                  * NULL-terminated;  malloc-ed. */
39     char *textVarName;          /* Name of variable (malloc'ed) or NULL.
40                                  * If non-NULL, entry's string tracks the
41                                  * contents of this variable and vice versa. */
42     Ck_Uid state;               /* Normal or disabled.  Entry is read-only
43                                  * when disabled. */
44
45     /*
46      * Information used when displaying widget:
47      */
48
49     int normalBg;               /* Normal background color. */
50     int normalFg;               /* Normal foreground color. */
51     int normalAttr;             /* Normal video attributes. */
52     int selBg;                  /* Select background color. */
53     int selFg;                  /* Select foreground color. */
54     int selAttr;                /* Select video attributes. */
55     Ck_Justify justify;         /* Justification to use for text within
56                                  * window. */
57     int leftX;                  /* X position at which leftIndex is drawn
58                                  * (varies depending on justify). */
59     int leftIndex;              /* Index of left-most character visible in
60                                  * window. */
61     int tabOrigin;              /* Origin for tabs (left edge of string[0]). */
62     int insertPos;              /* Index of character before which next
63                                  * typed character will be inserted. */
64     char *showChar;             /* Value of -show option.  If non-NULL, first
65                                  * character is used for displaying all
66                                  * characters in entry.  Malloc'ed. */
67     char *displayString;        /* If non-NULL, points to string with same
68                                  * length as string but whose characters
69                                  * are all equal to showChar.  Malloc'ed. */
70     int prefWidth;              /* Preferred width for window. */
71
72     /*
73      * Information about what's selected, if any.
74      */
75
76     int selectFirst;            /* Index of first selected character (-1 means
77                                  * nothing selected. */
78     int selectLast;             /* Index of last selected character (-1 means
79                                  * nothing selected. */
80     int selectAnchor;           /* Fixed end of selection (i.e. "select to"
81                                  * operation will use this as one end of the
82                                  * selection). */
83
84     /*
85      * Miscellaneous information:
86      */
87
88     char *takeFocus;            /* Value of -takefocus option;  not used in
89                                  * the C code, but used by keyboard traversal
90                                  * scripts.  Malloc'ed, but may be NULL. */
91     char *scrollCmd;            /* Command prefix for communicating with
92                                  * scrollbar(s).  Malloc'ed.  NULL means
93                                  * no command to issue. */
94     int flags;                  /* Miscellaneous flags;  see below for
95                                  * definitions. */
96 } Entry;
97
98 /*
99  * Assigned bits of "flags" fields of Entry structures, and what those
100  * bits mean:
101  *
102  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler has
103  *                              already been queued to redisplay the entry.
104  * GOT_FOCUS:                   Non-zero means this window has the input
105  *                              focus.
106  * UPDATE_SCROLLBAR:            Non-zero means scrollbar should be updated
107  *                              during next redisplay operation.
108  */
109
110 #define REDRAW_PENDING          1
111 #define GOT_FOCUS               2
112 #define UPDATE_SCROLLBAR        4
113
114 /*
115  * Information used for argv parsing.
116  */
117
118 static Ck_ConfigSpec configSpecs[] = {
119     {CK_CONFIG_ATTR, "-attributes", "attributes", "Attributes",
120         DEF_ENTRY_ATTR, Ck_Offset(Entry, normalAttr), 0},
121     {CK_CONFIG_COLOR, "-background", "background", "Background",
122         DEF_ENTRY_BG_COLOR, Ck_Offset(Entry, normalBg),
123         CK_CONFIG_COLOR_ONLY},
124     {CK_CONFIG_COLOR, "-background", "background", "Background",
125         DEF_ENTRY_BG_MONO, Ck_Offset(Entry, normalBg),
126         CK_CONFIG_MONO_ONLY},
127     {CK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
128         (char *) NULL, 0, 0},
129     {CK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
130         (char *) NULL, 0, 0},
131     {CK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
132         DEF_ENTRY_FG, Ck_Offset(Entry, normalFg), 0},
133     {CK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
134         DEF_ENTRY_JUSTIFY, Ck_Offset(Entry, justify), 0},
135     {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
136         "SelectAttributes", DEF_ENTRY_SELECT_ATTR_COLOR,
137         Ck_Offset(Entry, selAttr), CK_CONFIG_COLOR_ONLY},
138     {CK_CONFIG_ATTR, "-selectattributes", "selectAttributes",
139         "SelectAttributes", DEF_ENTRY_SELECT_ATTR_MONO,
140         Ck_Offset(Entry, selAttr), CK_CONFIG_MONO_ONLY},
141     {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
142         DEF_ENTRY_SELECT_BG_COLOR, Ck_Offset(Entry, selBg),
143         CK_CONFIG_COLOR_ONLY},
144     {CK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground",
145         DEF_ENTRY_SELECT_BG_MONO, Ck_Offset(Entry, selBg),
146         CK_CONFIG_MONO_ONLY},
147     {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
148         DEF_ENTRY_SELECT_FG_COLOR, Ck_Offset(Entry, selFg),
149         CK_CONFIG_COLOR_ONLY},
150     {CK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
151         DEF_ENTRY_SELECT_FG_MONO, Ck_Offset(Entry, selFg),
152         CK_CONFIG_MONO_ONLY},
153     {CK_CONFIG_STRING, "-show", "show", "Show",
154         DEF_ENTRY_SHOW, Ck_Offset(Entry, showChar), CK_CONFIG_NULL_OK},
155     {CK_CONFIG_UID, "-state", "state", "State",
156         DEF_ENTRY_STATE, Ck_Offset(Entry, state), 0},
157     {CK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
158         DEF_ENTRY_TAKE_FOCUS, Ck_Offset(Entry, takeFocus), CK_CONFIG_NULL_OK},
159     {CK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
160         DEF_ENTRY_TEXT_VARIABLE, Ck_Offset(Entry, textVarName),
161         CK_CONFIG_NULL_OK},
162     {CK_CONFIG_INT, "-width", "width", "Width",
163         DEF_ENTRY_WIDTH, Ck_Offset(Entry, prefWidth), 0},
164     {CK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
165         DEF_ENTRY_SCROLL_COMMAND, Ck_Offset(Entry, scrollCmd),
166         CK_CONFIG_NULL_OK},
167     {CK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
168         (char *) NULL, 0, 0}
169 };
170
171 /*
172  * Flags for GetEntryIndex procedure:
173  */
174
175 #define ZERO_OK                 1
176 #define LAST_PLUS_ONE_OK        2
177
178 /*
179  * Forward declarations for procedures defined later in this file:
180  */
181
182 static int              ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
183                             Entry *entryPtr, int argc, char **argv,
184                             int flags));
185 static void             DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
186                             int count));
187 static void             DestroyEntry _ANSI_ARGS_((ClientData clientData));
188 static void             DisplayEntry _ANSI_ARGS_((ClientData clientData));
189 static void             EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
190 static void             EntryEventProc _ANSI_ARGS_((ClientData clientData,
191                             CkEvent *eventPtr));
192 static void             EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
193                             int gotFocus));
194 static void             EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
195 static void             EntryCmdDeletedProc _ANSI_ARGS_((
196                             ClientData clientData));
197 static void             EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
198                             char *value));
199 static void             EntrySelectTo _ANSI_ARGS_((
200                             Entry *entryPtr, int index));
201 static char *           EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
202                             Tcl_Interp *interp, char *name1, char *name2,
203                             int flags));
204 static void             EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
205 static void             EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
206                             double *firstPtr, double *lastPtr));
207 static int              EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
208                             Tcl_Interp *interp, int argc, char **argv));
209 static int              GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
210                             Entry *entryPtr, char *string, int *indexPtr));
211 static void             InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
212                             char *string));
213 \f
214 /*
215  *--------------------------------------------------------------
216  *
217  * Ck_EntryCmd --
218  *
219  *      This procedure is invoked to process the "entry" Tcl
220  *      command.  See the user documentation for details on what
221  *      it does.
222  *
223  * Results:
224  *      A standard Tcl result.
225  *
226  * Side effects:
227  *      See the user documentation.
228  *
229  *--------------------------------------------------------------
230  */
231
232 int
233 Ck_EntryCmd(clientData, interp, argc, argv)
234     ClientData clientData;      /* Main window associated with
235                                  * interpreter. */
236     Tcl_Interp *interp;         /* Current interpreter. */
237     int argc;                   /* Number of arguments. */
238     char **argv;                /* Argument strings. */
239 {
240     CkWindow *mainPtr = (CkWindow *) clientData;
241     register Entry *entryPtr;
242     CkWindow *new;
243
244     if (argc < 2) {
245         Tcl_AppendResult(interp, "wrong # args:  should be \"",
246                 argv[0], " pathName ?options?\"", (char *) NULL);
247         return TCL_ERROR;
248     }
249
250     new = Ck_CreateWindowFromPath(interp, mainPtr, argv[1], 0);
251     if (new == NULL) {
252         return TCL_ERROR;
253     }
254
255     /*
256      * Initialize the fields of the structure that won't be initialized
257      * by ConfigureEntry, or that ConfigureEntry requires to be
258      * initialized already (e.g. resource pointers).
259      */
260
261     entryPtr = (Entry *) ckalloc(sizeof (Entry));
262     entryPtr->winPtr = new;
263     entryPtr->interp = interp;
264     entryPtr->widgetCmd = Tcl_CreateCommand(interp,
265         entryPtr->winPtr->pathName, EntryWidgetCmd,
266             (ClientData) entryPtr, EntryCmdDeletedProc);
267 #if CK_USE_UTF
268     entryPtr->numBytes = 0;
269 #endif
270     entryPtr->numChars = 0;
271     entryPtr->string = (char *) ckalloc(1);
272     entryPtr->string[0] = '\0';
273     entryPtr->textVarName = NULL;
274     entryPtr->state = ckNormalUid;
275     entryPtr->normalBg = 0;
276     entryPtr->normalFg = 0;
277     entryPtr->normalAttr = 0;
278     entryPtr->selBg = 0;
279     entryPtr->selFg = 0;
280     entryPtr->selAttr = 0;
281     entryPtr->justify = CK_JUSTIFY_LEFT;
282     entryPtr->prefWidth = 0;
283     entryPtr->leftIndex = 0;
284     entryPtr->tabOrigin = 0;
285     entryPtr->insertPos = 0;
286     entryPtr->showChar = NULL;
287     entryPtr->displayString = NULL;
288     entryPtr->prefWidth = 1;
289     entryPtr->selectFirst = -1;
290     entryPtr->selectLast = -1;
291     entryPtr->selectAnchor = 0;
292     entryPtr->takeFocus = NULL;
293     entryPtr->scrollCmd = NULL;
294     entryPtr->flags = 0;
295
296     Ck_SetClass(entryPtr->winPtr, "Entry");
297     Ck_CreateEventHandler(entryPtr->winPtr,
298             CK_EV_EXPOSE | CK_EV_MAP | CK_EV_DESTROY,
299             EntryEventProc, (ClientData) entryPtr);
300     if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
301         goto error;
302     }
303
304     interp->result = entryPtr->winPtr->pathName;
305     return TCL_OK;
306
307     error:
308     Ck_DestroyWindow(entryPtr->winPtr);
309     return TCL_ERROR;
310 }
311 \f
312 /*
313  *--------------------------------------------------------------
314  *
315  * EntryWidgetCmd --
316  *
317  *      This procedure is invoked to process the Tcl command
318  *      that corresponds to a widget managed by this module.
319  *      See the user documentation for details on what it does.
320  *
321  * Results:
322  *      A standard Tcl result.
323  *
324  * Side effects:
325  *      See the user documentation.
326  *
327  *--------------------------------------------------------------
328  */
329
330 static int
331 EntryWidgetCmd(clientData, interp, argc, argv)
332     ClientData clientData;              /* Information about entry widget. */
333     Tcl_Interp *interp;                 /* Current interpreter. */
334     int argc;                           /* Number of arguments. */
335     char **argv;                        /* Argument strings. */
336 {
337     register Entry *entryPtr = (Entry *) clientData;
338     int result = TCL_OK;
339     size_t length;
340     int c;
341
342     if (argc < 2) {
343         Tcl_AppendResult(interp, "wrong # args: should be \"",
344                 argv[0], " option ?arg arg ...?\"", (char *) NULL);
345         return TCL_ERROR;
346     }
347     Ck_Preserve((ClientData) entryPtr);
348     c = argv[1][0];
349     length = strlen(argv[1]);
350     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
351         && (length >= 2)) {
352         if (argc != 3) {
353             Tcl_AppendResult(interp, "wrong # args: should be \"",
354                     argv[0], " cget option\"",
355                     (char *) NULL);
356             goto error;
357         }
358         result = Ck_ConfigureValue(interp, entryPtr->winPtr, configSpecs,
359                 (char *) entryPtr, argv[2], 0);
360     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
361             && (length >= 2)) {
362         if (argc == 2) {
363             result = Ck_ConfigureInfo(interp, entryPtr->winPtr, configSpecs,
364                     (char *) entryPtr, (char *) NULL, 0);
365         } else if (argc == 3) {
366             result = Ck_ConfigureInfo(interp, entryPtr->winPtr, configSpecs,
367                     (char *) entryPtr, argv[2], 0);
368         } else {
369             result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
370                     CK_CONFIG_ARGV_ONLY);
371         }
372     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
373         int first, last;
374
375         if ((argc < 3) || (argc > 4)) {
376             Tcl_AppendResult(interp, "wrong # args: should be \"",
377                     argv[0], " delete firstIndex ?lastIndex?\"",
378                     (char *) NULL);
379             goto error;
380         }
381         if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
382             goto error;
383         }
384         if (argc == 3) {
385             last = first+1;
386         } else {
387             if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
388                 goto error;
389             }
390         }
391         if ((last >= first) && (entryPtr->state == ckNormalUid)) {
392             DeleteChars(entryPtr, first, last-first);
393         }
394     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
395         if (argc != 2) {
396             Tcl_AppendResult(interp, "wrong # args: should be \"",
397                     argv[0], " get\"", (char *) NULL);
398             goto error;
399         }
400         interp->result = entryPtr->string;
401     } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
402             && (length >= 2)) {
403         if (argc != 3) {
404             Tcl_AppendResult(interp, "wrong # args: should be \"",
405                     argv[0], " icursor pos\"",
406                     (char *) NULL);
407             goto error;
408         }
409         if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
410                 != TCL_OK) {
411             goto error;
412         }
413         EventuallyRedraw(entryPtr);
414     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
415             && (length >= 3)) {
416         int index;
417
418         if (argc != 3) {
419             Tcl_AppendResult(interp, "wrong # args: should be \"",
420                     argv[0], " index string\"", (char *) NULL);
421             goto error;
422         }
423         if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
424             goto error;
425         }
426         sprintf(interp->result, "%d", index);
427     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
428             && (length >= 3)) {
429         int index;
430
431         if (argc != 4) {
432             Tcl_AppendResult(interp, "wrong # args: should be \"",
433                     argv[0], " insert index text\"",
434                     (char *) NULL);
435             goto error;
436         }
437         if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
438             goto error;
439         }
440         if (entryPtr->state == ckNormalUid) {
441             InsertChars(entryPtr, index, argv[3]);
442         }
443     } else if ((c == 's') && (length >= 2)
444             && (strncmp(argv[1], "selection", length) == 0)) {
445         int index, index2;
446
447         if (argc < 3) {
448             Tcl_AppendResult(interp, "wrong # args: should be \"",
449                     argv[0], " select option ?index?\"", (char *) NULL);
450             goto error;
451         }
452         length = strlen(argv[2]);
453         c = argv[2][0];
454         if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
455             if (argc != 3) {
456                 Tcl_AppendResult(interp, "wrong # args: should be \"",
457                         argv[0], " selection clear\"", (char *) NULL);
458                 goto error;
459             }
460             if (entryPtr->selectFirst != -1) {
461                 entryPtr->selectFirst = entryPtr->selectLast = -1;
462                 EventuallyRedraw(entryPtr);
463             }
464             goto done;
465         } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
466             if (argc != 3) {
467                 Tcl_AppendResult(interp, "wrong # args: should be \"",
468                         argv[0], " selection present\"", (char *) NULL);
469                 goto error;
470             }
471             if (entryPtr->selectFirst == -1) {
472                 interp->result = "0";
473             } else {
474                 interp->result = "1";
475             }
476             goto done;
477         }
478         if (argc >= 4) {
479             if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
480                 goto error;
481             }
482         }
483         if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
484             if (argc != 4) {
485                 Tcl_AppendResult(interp, "wrong # args: should be \"",
486                         argv[0], " selection adjust index\"",
487                         (char *) NULL);
488                 goto error;
489             }
490             if (entryPtr->selectFirst >= 0) {
491                 int half1, half2;
492
493                 half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
494                 half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
495                 if (index < half1) {
496                     entryPtr->selectAnchor = entryPtr->selectLast;
497                 } else if (index > half2) {
498                     entryPtr->selectAnchor = entryPtr->selectFirst;
499                 } else {
500                     /*
501                      * We're at about the halfway point in the selection;
502                      * just keep the existing anchor.
503                      */
504                 }
505             }
506             EntrySelectTo(entryPtr, index);
507         } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
508             if (argc != 4) {
509                 Tcl_AppendResult(interp, "wrong # args: should be \"",
510                         argv[0], " selection from index\"",
511                         (char *) NULL);
512                 goto error;
513             }
514             entryPtr->selectAnchor = index;
515         } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
516             if (argc != 5) {
517                 Tcl_AppendResult(interp, "wrong # args: should be \"",
518                         argv[0], " selection range start end\"",
519                         (char *) NULL);
520                 goto error;
521             }
522             if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
523                 goto error;
524             }
525             if (index >= index2) {
526                 entryPtr->selectFirst = entryPtr->selectLast = -1;
527             } else {
528                 entryPtr->selectFirst = index;
529                 entryPtr->selectLast = index2;
530             }
531             EventuallyRedraw(entryPtr);
532         } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
533             if (argc != 4) {
534                 Tcl_AppendResult(interp, "wrong # args: should be \"",
535                         argv[0], " selection to index\"",
536                         (char *) NULL);
537                 goto error;
538             }
539             EntrySelectTo(entryPtr, index);
540         } else {
541             Tcl_AppendResult(interp, "bad selection option \"", argv[2],
542                     "\": must be adjust, clear, from, present, range, or to",
543                     (char *) NULL);
544             goto error;
545         }
546     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
547         int index, type, count, charsPerPage;
548         double fraction, first, last;
549
550         if (argc == 2) {
551             EntryVisibleRange(entryPtr, &first, &last);
552             sprintf(interp->result, "%g %g", first, last);
553             goto done;
554         } else if (argc == 3) {
555             if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
556                 goto error;
557             }
558         } else {
559             type = Ck_GetScrollInfo(interp, argc, argv, &fraction, &count);
560             index = entryPtr->leftIndex;
561             switch (type) {
562                 case CK_SCROLL_ERROR:
563                     goto error;
564                 case CK_SCROLL_MOVETO:
565                     index = (int) (fraction * entryPtr->numChars);
566                     break;
567                 case CK_SCROLL_PAGES:
568                     charsPerPage = entryPtr->winPtr->width - 2;
569                     if (charsPerPage < 1)
570                         charsPerPage = 1;
571                     index += charsPerPage*count;
572                     break;
573                 case CK_SCROLL_UNITS:
574                     index += count;
575                     break;
576             }
577         }
578         if (index >= entryPtr->numChars) {
579             index = entryPtr->numChars-1;
580         }
581         if (index < 0) {
582             index = 0;
583         }
584         entryPtr->leftIndex = index;
585         entryPtr->flags |= UPDATE_SCROLLBAR;
586         EntryComputeGeometry(entryPtr);
587         EventuallyRedraw(entryPtr);
588     } else {
589         Tcl_AppendResult(interp, "bad option \"", argv[1],
590                 "\": must be cget, configure, delete, get, ",
591                 "icursor, index, insert, selection, or xview",
592                 (char *) NULL);
593         goto error;
594     }
595 done:
596     Ck_Release((ClientData) entryPtr);
597     return result;
598
599 error:
600     Ck_Release((ClientData) entryPtr);
601     return TCL_ERROR;
602 }
603 \f
604 /*
605  *----------------------------------------------------------------------
606  *
607  * DestroyEntry --
608  *
609  *      This procedure is invoked by Ck_EventuallyFree or Ck_Release
610  *      to clean up the internal structure of an entry at a safe time
611  *      (when no-one is using it anymore).
612  *
613  * Results:
614  *      None.
615  *
616  * Side effects:
617  *      Everything associated with the entry is freed up.
618  *
619  *----------------------------------------------------------------------
620  */
621
622 static void
623 DestroyEntry(clientData)
624     ClientData clientData;                      /* Info about entry widget. */
625 {
626     Entry *entryPtr = (Entry *) clientData;
627
628     /*
629      * Free up all the stuff that requires special handling, then
630      * let Ck_FreeOptions handle all the standard option-related
631      * stuff.
632      */
633
634     ckfree(entryPtr->string);
635     if (entryPtr->textVarName != NULL) {
636         Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
637                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
638                 EntryTextVarProc, (ClientData) entryPtr);
639     }
640     if (entryPtr->displayString != NULL) {
641         ckfree(entryPtr->displayString);
642     }
643     Ck_FreeOptions(configSpecs, (char *) entryPtr, 0);
644     ckfree((char *) entryPtr);
645 }
646 \f
647 /*
648  *----------------------------------------------------------------------
649  *
650  * EntryCmdDeletedProc --
651  *
652  *      This procedure is invoked when a widget command is deleted.  If
653  *      the widget isn't already in the process of being destroyed,
654  *      this command destroys it.
655  *
656  * Results:
657  *      None.
658  *
659  * Side effects:
660  *      The widget is destroyed.
661  *
662  *----------------------------------------------------------------------
663  */
664
665 static void
666 EntryCmdDeletedProc(clientData)
667     ClientData clientData;      /* Pointer to widget record for widget. */
668 {
669     Entry *entryPtr = (Entry *) clientData;
670     CkWindow *winPtr = entryPtr->winPtr;
671
672     /*
673      * This procedure could be invoked either because the window was
674      * destroyed and the command was then deleted (in which case winPtr
675      * is NULL) or because the command was deleted, and then this procedure
676      * destroys the widget.
677      */
678
679     if (winPtr != NULL) {
680         entryPtr->winPtr = NULL;
681         Ck_DestroyWindow(winPtr);
682     }
683 }
684 \f
685 /*
686  *----------------------------------------------------------------------
687  *
688  * ConfigureEntry --
689  *
690  *      This procedure is called to process an argv/argc list, plus
691  *      the Tk option database, in order to configure (or reconfigure)
692  *      an entry widget.
693  *
694  * Results:
695  *      The return value is a standard Tcl result.  If TCL_ERROR is
696  *      returned, then interp->result contains an error message.
697  *
698  * Side effects:
699  *      Configuration information, such as colors, border width,
700  *      etc. get set for entryPtr;  old resources get freed,
701  *      if there were any.
702  *
703  *----------------------------------------------------------------------
704  */
705
706 static int
707 ConfigureEntry(interp, entryPtr, argc, argv, flags)
708     Tcl_Interp *interp;         /* Used for error reporting. */
709     register Entry *entryPtr;   /* Information about widget;  may or may
710                                  * not already have values for some fields. */
711     int argc;                   /* Number of valid entries in argv. */
712     char **argv;                /* Arguments. */
713     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
714 {
715     /*
716      * Eliminate any existing trace on a variable monitored by the entry.
717      */
718
719     if (entryPtr->textVarName != NULL) {
720         Tcl_UntraceVar(interp, entryPtr->textVarName, 
721                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
722                 EntryTextVarProc, (ClientData) entryPtr);
723     }
724
725     if (Ck_ConfigureWidget(interp, entryPtr->winPtr, configSpecs,
726             argc, argv, (char *) entryPtr, flags) != TCL_OK) {
727         return TCL_ERROR;
728     }
729
730     /*
731      * If the entry is tied to the value of a variable, then set up
732      * a trace on the variable's value, create the variable if it doesn't
733      * exist, and set the entry's value from the variable's value.
734      */
735
736     if (entryPtr->textVarName != NULL) {
737         char *value;
738
739         value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
740         if (value == NULL) {
741             Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
742                     TCL_GLOBAL_ONLY);
743         } else {
744             EntrySetValue(entryPtr, value);
745         }
746         Tcl_TraceVar(interp, entryPtr->textVarName,
747                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
748                 EntryTextVarProc, (ClientData) entryPtr);
749     }
750
751     /*
752      * A few other options also need special processing, such as parsing
753      * the geometry and setting the colors.
754      */
755
756     if ((entryPtr->state != ckNormalUid)
757             && (entryPtr->state != ckDisabledUid)) {
758         Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
759                 "\":  must be normal or disabled", (char *) NULL);
760         entryPtr->state = ckNormalUid;
761         return TCL_ERROR;
762     }
763
764     /*
765      * Recompute the window's geometry and arrange for it to be
766      * redisplayed.
767      */
768
769     EntryComputeGeometry(entryPtr);
770     entryPtr->flags |= UPDATE_SCROLLBAR;
771     EventuallyRedraw(entryPtr);
772     return TCL_OK;
773 }
774 \f
775 /*
776  *--------------------------------------------------------------
777  *
778  * DisplayEntry --
779  *
780  *      This procedure redraws the contents of an entry window.
781  *
782  * Results:
783  *      None.
784  *
785  * Side effects:
786  *      Information appears on the screen.
787  *
788  *--------------------------------------------------------------
789  */
790
791 static void
792 DisplayEntry(clientData)
793     ClientData clientData;      /* Information about window. */
794 {
795     register Entry *entryPtr = (Entry *) clientData;
796     CkWindow *winPtr = entryPtr->winPtr;
797     int y, startX, leftIndex, selectFirst, selectLast, insertPos, dummy;
798     char *displayString;
799
800     entryPtr->flags &= ~REDRAW_PENDING;
801     if ((entryPtr->winPtr == NULL) || !(winPtr->flags & CK_MAPPED))
802         return;
803
804     /*
805      * Update the scrollbar if that's needed.
806      */
807
808     if (entryPtr->flags & UPDATE_SCROLLBAR) {
809         EntryUpdateScrollbar(entryPtr);
810     }
811
812     /*
813      * Compute x-coordinate of the pixel just after last visible
814      * one, plus vertical position of baseline of text.
815      */
816
817     y = winPtr->height / 2;
818
819     if (entryPtr->displayString == NULL) {
820         displayString = entryPtr->string;
821     } else {
822         displayString = entryPtr->displayString;
823     }
824
825     Ck_SetWindowAttr(winPtr, entryPtr->normalFg, entryPtr->normalBg,
826         entryPtr->normalAttr);
827     Ck_ClearToBot(winPtr, 0, 0);
828
829 #if CK_USE_UTF
830     leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
831         displayString;
832     selectFirst = Tcl_UtfAtIndex(displayString, entryPtr->selectFirst) -
833         displayString;
834     selectLast = Tcl_UtfAtIndex(displayString, entryPtr->selectLast) -
835         displayString;
836     insertPos = Tcl_UtfAtIndex(displayString, entryPtr->insertPos) -
837         displayString;
838 #else
839     leftIndex = entryPtr->leftIndex;
840     selectFirst = entryPtr->selectFirst;
841     selectLast = entryPtr->selectLast;
842     insertPos = entryPtr->insertPos;
843 #endif
844
845     CkDisplayChars(winPtr->mainPtr, winPtr->window,
846         displayString + leftIndex,
847         strlen(displayString) - leftIndex,
848         entryPtr->leftX, y, entryPtr->tabOrigin,
849         CK_NEWLINES_NOT_SPECIAL);
850
851     if (entryPtr->selectLast >= entryPtr->leftIndex) {
852         if (entryPtr->selectFirst < entryPtr->leftIndex) {
853             startX = 0;
854         } else {
855             CkMeasureChars(winPtr->mainPtr,
856                 displayString + leftIndex,
857                 selectFirst - leftIndex, entryPtr->leftX,
858                 winPtr->width, entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL,
859                 &startX, &dummy);
860         }
861         if (startX < winPtr->width) {
862             Ck_SetWindowAttr(winPtr, entryPtr->selFg, entryPtr->selBg,
863                 entryPtr->selAttr);
864             wmove(winPtr->window, y, startX + entryPtr->leftX);
865             CkDisplayChars(winPtr->mainPtr, winPtr->window,
866                 displayString + selectFirst,
867                 selectLast - selectFirst,
868                 entryPtr->leftX + startX, y, entryPtr->tabOrigin,
869                 CK_NEWLINES_NOT_SPECIAL);
870             Ck_SetWindowAttr(winPtr, entryPtr->normalFg, entryPtr->normalBg,
871                 entryPtr->normalAttr);
872         }
873     }
874
875     CkMeasureChars(winPtr->mainPtr, displayString + leftIndex,
876         insertPos - leftIndex, entryPtr->leftX,
877         winPtr->width, entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL,
878         &startX, &dummy);
879
880     if (startX >= 0 && startX < winPtr->width) {
881         wmove(winPtr->window, y, startX);
882         if (entryPtr->state == ckNormalUid)
883             Ck_SetHWCursor(winPtr, 1);
884         else
885             Ck_SetHWCursor(winPtr, 0);
886     } else {
887         wmove(winPtr->window, y, 0);
888         Ck_SetHWCursor(winPtr, 0);
889     }
890
891     Ck_EventuallyRefresh(winPtr);
892 }
893 \f
894 /*
895  *----------------------------------------------------------------------
896  *
897  * EntryComputeGeometry --
898  *
899  *      This procedure is invoked to recompute information about where
900  *      in its window an entry's string will be displayed.  It also
901  *      computes the requested size for the window.
902  *
903  * Results:
904  *      None.
905  *
906  * Side effects:
907  *      The tabOrigin fields are recomputed for entryPtr,
908  *      and leftIndex may be adjusted.  Ck_GeometryRequest is called
909  *      to register the desired dimensions for the window.
910  *
911  *----------------------------------------------------------------------
912  */
913
914 static void
915 EntryComputeGeometry(entryPtr)
916     Entry *entryPtr;                    /* Widget record for entry. */
917 {
918     int totalLength, overflow, maxOffScreen;
919     int width, i, rightX, dummy;
920     char *p, *displayString;
921     CkWindow *winPtr = entryPtr->winPtr;
922
923     /*
924      * If we're displaying a special character instead of the value of
925      * the entry, recompute the displayString.
926      */
927
928     if (entryPtr->displayString != NULL) {
929         ckfree(entryPtr->displayString);
930         entryPtr->displayString = NULL;
931     }
932     if (entryPtr->showChar != NULL) {
933 #if CK_USE_UTF
934         int ulen;
935
936         entryPtr->displayString = (char *) ckalloc(entryPtr->numChars * 3 + 1);
937         ulen = Tcl_UtfNext(entryPtr->showChar) - entryPtr->showChar;
938         for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
939                 i--) {
940             memcpy(p, entryPtr->showChar, ulen);
941             p += ulen;
942         }
943 #else
944         entryPtr->displayString = (char *) ckalloc(entryPtr->numChars + 1);
945         for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
946                 i--, p++) {
947             *p = entryPtr->showChar[0];
948         }
949 #endif
950         *p = 0;
951         displayString = entryPtr->displayString;
952     } else {
953         displayString = entryPtr->string;
954     }
955
956     /*
957      * Recompute where the leftmost character on the display will
958      * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
959      * so that we don't let characters hang off the edge of the
960      * window unless the entire window is full.
961      */
962
963     CkMeasureChars(winPtr->mainPtr, displayString, strlen(displayString),
964         0, INT_MAX, 0,
965         CK_NEWLINES_NOT_SPECIAL, &totalLength, &dummy);
966     if (entryPtr->insertPos == entryPtr->numChars)
967         totalLength += 1;
968     overflow = totalLength - entryPtr->winPtr->width;
969     if (overflow < 0) {
970         entryPtr->leftIndex = 0;
971         if (entryPtr->justify == CK_JUSTIFY_LEFT) {
972             entryPtr->leftX = 0;
973         } else if (entryPtr->justify == CK_JUSTIFY_RIGHT) {
974             entryPtr->leftX = entryPtr->winPtr->width - totalLength;
975         } else {
976             entryPtr->leftX = (entryPtr->winPtr->width - totalLength) / 2;
977         }
978         entryPtr->tabOrigin = entryPtr->leftX;
979     } else {
980         int leftIndex;
981
982         /*
983          * The whole string can't fit in the window.  Compute the
984          * maximum number of characters that may be off-screen to
985          * the left without leaving empty space on the right of the
986          * window, then don't let leftIndex be any greater than that.
987          */
988
989         maxOffScreen = CkMeasureChars(winPtr->mainPtr,
990             displayString, strlen(displayString),
991             0, overflow, 0, CK_NEWLINES_NOT_SPECIAL|CK_PARTIAL_OK, &rightX,
992             &dummy);
993         if (rightX < overflow) {
994             maxOffScreen += 1;
995         }
996         if (entryPtr->leftIndex > maxOffScreen) {
997             entryPtr->leftIndex = maxOffScreen;
998         }
999 #if CK_USE_UTF
1000         leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
1001             displayString;
1002 #else
1003         leftIndex = entryPtr->leftIndex;
1004 #endif
1005         CkMeasureChars(winPtr->mainPtr, displayString, leftIndex,
1006             0, INT_MAX, 0,
1007             CK_NEWLINES_NOT_SPECIAL|CK_PARTIAL_OK, &rightX, &dummy);
1008         entryPtr->leftX = 0;
1009         entryPtr->tabOrigin = entryPtr->leftX - rightX;
1010     }
1011
1012     if (entryPtr->prefWidth > 0) {
1013         width = entryPtr->prefWidth;
1014     } else if (totalLength == 0) {
1015         width = 1;
1016     } else {
1017         width = totalLength;
1018     }
1019     Ck_GeometryRequest(entryPtr->winPtr, width, 1);
1020 }
1021 \f
1022 /*
1023  *----------------------------------------------------------------------
1024  *
1025  * InsertChars --
1026  *
1027  *      Add new characters to an entry widget.
1028  *
1029  * Results:
1030  *      None.
1031  *
1032  * Side effects:
1033  *      New information gets added to entryPtr;  it will be redisplayed
1034  *      soon, but not necessarily immediately.
1035  *
1036  *----------------------------------------------------------------------
1037  */
1038
1039 static void
1040 InsertChars(entryPtr, index, string)
1041     register Entry *entryPtr;   /* Entry that is to get the new
1042                                  * elements. */
1043     int index;                  /* Add the new elements before this
1044                                  * element. */
1045     char *string;               /* New characters to add (NULL-terminated
1046                                  * string). */
1047 {
1048     int length, clength;
1049     char *new;
1050 #if CK_USE_UTF
1051     int inspos;
1052 #endif
1053
1054     length = strlen(string);
1055     if (length == 0) {
1056         return;
1057     }
1058 #if CK_USE_UTF
1059     clength = Tcl_NumUtfChars(string, -1);
1060     new = (char *) ckalloc((unsigned) (entryPtr->numBytes + length + 1));
1061     inspos = Tcl_UtfAtIndex(entryPtr->string, index) - entryPtr->string;
1062     strncpy(new, entryPtr->string, (size_t) inspos);
1063     strcpy(new+inspos, string);
1064     strcpy(new+inspos+length, entryPtr->string+inspos);
1065     ckfree(entryPtr->string);
1066     entryPtr->string = new;
1067     entryPtr->numChars += clength;
1068     entryPtr->numBytes += length;
1069 #else
1070     clength = length;
1071     new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
1072     strncpy(new, entryPtr->string, (size_t) index);
1073     strcpy(new+index, string);
1074     strcpy(new+index+length, entryPtr->string+index);
1075     ckfree(entryPtr->string);
1076     entryPtr->string = new;
1077     entryPtr->numChars += length;
1078 #endif
1079
1080     /*
1081      * Inserting characters invalidates all indexes into the string.
1082      * Touch up the indexes so that they still refer to the same
1083      * characters (at new positions).  When updating the selection
1084      * end-points, don't include the new text in the selection unless
1085      * it was completely surrounded by the selection.
1086      */
1087
1088     if (entryPtr->selectFirst >= index) {
1089         entryPtr->selectFirst += clength;
1090     }
1091     if (entryPtr->selectLast > index) {
1092         entryPtr->selectLast += clength;
1093     }
1094     if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
1095         entryPtr->selectAnchor += clength;
1096     }
1097     if (entryPtr->leftIndex > index) {
1098         entryPtr->leftIndex += clength;
1099     }
1100     if (entryPtr->insertPos >= index) {
1101         entryPtr->insertPos += clength;
1102     }
1103
1104     if (entryPtr->textVarName != NULL) {
1105         Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1106                 TCL_GLOBAL_ONLY);
1107     }
1108     entryPtr->flags |= UPDATE_SCROLLBAR;
1109     EntryComputeGeometry(entryPtr);
1110     EventuallyRedraw(entryPtr);
1111 }
1112 \f
1113 /*
1114  *----------------------------------------------------------------------
1115  *
1116  * DeleteChars --
1117  *
1118  *      Remove one or more characters from an entry widget.
1119  *
1120  * Results:
1121  *      None.
1122  *
1123  * Side effects:
1124  *      Memory gets freed, the entry gets modified and (eventually)
1125  *      redisplayed.
1126  *
1127  *----------------------------------------------------------------------
1128  */
1129
1130 static void
1131 DeleteChars(entryPtr, index, count)
1132     register Entry *entryPtr;   /* Entry widget to modify. */
1133     int index;                  /* Index of first character to delete. */
1134     int count;                  /* How many characters to delete. */
1135 {
1136     char *new;
1137 #if CK_USE_UTF
1138     int delpos, delcount;
1139 #endif
1140
1141     if ((index + count) > entryPtr->numChars) {
1142         count = entryPtr->numChars - index;
1143     }
1144     if (count <= 0) {
1145         return;
1146     }
1147
1148 #if CK_USE_UTF
1149     delpos = Tcl_UtfAtIndex(entryPtr->string, index) - entryPtr->string;
1150     delcount = Tcl_UtfAtIndex(entryPtr->string + delpos, count) -
1151                    (entryPtr->string + delpos);
1152     new = (char *) ckalloc((unsigned) (entryPtr->numBytes + 1 - delcount));
1153     strncpy(new, entryPtr->string, (size_t) delpos);
1154     strcpy(new+delpos, entryPtr->string+delpos+delcount);
1155     entryPtr->numChars = Tcl_NumUtfChars(new, -1);
1156     entryPtr->numBytes = strlen(new);
1157 #else
1158     new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
1159     strncpy(new, entryPtr->string, (size_t) index);
1160     strcpy(new+index, entryPtr->string+index+count);
1161     entryPtr->numChars -= count;
1162 #endif
1163     ckfree(entryPtr->string);
1164     entryPtr->string = new;
1165
1166     /*
1167      * Deleting characters results in the remaining characters being
1168      * renumbered.  Update the various indexes into the string to reflect
1169      * this change.
1170      */
1171
1172     if (entryPtr->selectFirst >= index) {
1173         if (entryPtr->selectFirst >= (index+count)) {
1174             entryPtr->selectFirst -= count;
1175         } else {
1176             entryPtr->selectFirst = index;
1177         }
1178     }
1179     if (entryPtr->selectLast >= index) {
1180         if (entryPtr->selectLast >= (index+count)) {
1181             entryPtr->selectLast -= count;
1182         } else {
1183             entryPtr->selectLast = index;
1184         }
1185     }
1186     if (entryPtr->selectLast <= entryPtr->selectFirst) {
1187         entryPtr->selectFirst = entryPtr->selectLast = -1;
1188     }
1189     if (entryPtr->selectAnchor >= index) {
1190         if (entryPtr->selectAnchor >= (index+count)) {
1191             entryPtr->selectAnchor -= count;
1192         } else {
1193             entryPtr->selectAnchor = index;
1194         }
1195     }
1196     if (entryPtr->leftIndex > index) {
1197         if (entryPtr->leftIndex >= (index+count)) {
1198             entryPtr->leftIndex -= count;
1199         } else {
1200             entryPtr->leftIndex = index;
1201         }
1202     }
1203     if (entryPtr->insertPos >= index) {
1204         if (entryPtr->insertPos >= (index+count)) {
1205             entryPtr->insertPos -= count;
1206         } else {
1207             entryPtr->insertPos = index;
1208         }
1209     }
1210
1211     if (entryPtr->textVarName != NULL) {
1212         Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, entryPtr->string,
1213                 TCL_GLOBAL_ONLY);
1214     }
1215     entryPtr->flags |= UPDATE_SCROLLBAR;
1216     EntryComputeGeometry(entryPtr);
1217     EventuallyRedraw(entryPtr);
1218 }
1219 \f
1220 /*
1221  *----------------------------------------------------------------------
1222  *
1223  * EntrySetValue --
1224  *
1225  *      Replace the contents of a text entry with a given value.  This
1226  *      procedure is invoked when updating the entry from the entry's
1227  *      associated variable.
1228  *
1229  * Results:
1230  *      None.
1231  *
1232  * Side effects:
1233  *      The string displayed in the entry will change.  Any selection
1234  *      in the entry is lost and the insertion point gets set to the
1235  *      end of the entry.  Note: this procedure does *not* update the
1236  *      entry's associated variable, since that could result in an
1237  *      infinite loop.
1238  *
1239  *----------------------------------------------------------------------
1240  */
1241
1242 static void
1243 EntrySetValue(entryPtr, value)
1244     register Entry *entryPtr;           /* Entry whose value is to be
1245                                          * changed. */
1246     char *value;                        /* New text to display in entry. */
1247 {
1248     ckfree(entryPtr->string);
1249 #if CK_USE_UTF
1250     entryPtr->numBytes = strlen(value);
1251     entryPtr->numChars = Tcl_NumUtfChars(value, -1);
1252     entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numBytes + 1));
1253 #else
1254     entryPtr->numChars = strlen(value);
1255     entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
1256 #endif
1257     strcpy(entryPtr->string, value);
1258     entryPtr->selectFirst = entryPtr->selectLast = -1;
1259     entryPtr->leftIndex = 0;
1260     entryPtr->insertPos = entryPtr->numChars;
1261
1262     entryPtr->flags |= UPDATE_SCROLLBAR;
1263     EntryComputeGeometry(entryPtr);
1264     EventuallyRedraw(entryPtr);
1265 }
1266 \f
1267 /*
1268  *--------------------------------------------------------------
1269  *
1270  * EntryEventProc --
1271  *
1272  *      This procedure is invoked by the dispatcher for various
1273  *      events on entryes.
1274  *
1275  * Results:
1276  *      None.
1277  *
1278  * Side effects:
1279  *      When the window gets deleted, internal structures get
1280  *      cleaned up.  When it gets exposed, it is redisplayed.
1281  *
1282  *--------------------------------------------------------------
1283  */
1284
1285 static void
1286 EntryEventProc(clientData, eventPtr)
1287     ClientData clientData;      /* Information about window. */
1288     CkEvent *eventPtr;          /* Information about event. */
1289 {
1290     Entry *entryPtr = (Entry *) clientData;
1291
1292     if (eventPtr->type == CK_EV_EXPOSE) {
1293         Ck_Preserve((ClientData) entryPtr);
1294         entryPtr->flags |= UPDATE_SCROLLBAR;
1295         EntryComputeGeometry(entryPtr);
1296         EventuallyRedraw(entryPtr);
1297         Ck_Release((ClientData) entryPtr);
1298     } else if (eventPtr->type == CK_EV_DESTROY) {
1299         if (entryPtr->winPtr != NULL) {
1300             entryPtr->winPtr = NULL;
1301             Tcl_DeleteCommand(entryPtr->interp,
1302                     Tcl_GetCommandName(entryPtr->interp, entryPtr->widgetCmd));
1303         }
1304         if (entryPtr->flags & REDRAW_PENDING) {
1305             Tk_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1306         }
1307         Ck_EventuallyFree((ClientData) entryPtr, (Ck_FreeProc *) DestroyEntry);
1308     } else if (eventPtr->type == CK_EV_FOCUSIN) {
1309         EntryFocusProc(entryPtr, 1);
1310     } else if (eventPtr->type == CK_EV_FOCUSOUT) {
1311         EntryFocusProc(entryPtr, 0);
1312     }
1313 }
1314 \f
1315 /*
1316  *--------------------------------------------------------------
1317  *
1318  * GetEntryIndex --
1319  *
1320  *      Parse an index into an entry and return either its value
1321  *      or an error.
1322  *
1323  * Results:
1324  *      A standard Tcl result.  If all went well, then *indexPtr is
1325  *      filled in with the index (into entryPtr) corresponding to
1326  *      string.  The index value is guaranteed to lie between 0 and
1327  *      the number of characters in the string, inclusive.  If an
1328  *      error occurs then an error message is left in interp->result.
1329  *
1330  * Side effects:
1331  *      None.
1332  *
1333  *--------------------------------------------------------------
1334  */
1335
1336 static int
1337 GetEntryIndex(interp, entryPtr, string, indexPtr)
1338     Tcl_Interp *interp;         /* For error messages. */
1339     Entry *entryPtr;            /* Entry for which the index is being
1340                                  * specified. */
1341     char *string;               /* Specifies character in entryPtr. */
1342     int *indexPtr;              /* Where to store converted index. */
1343 {
1344     size_t length;
1345     int dummy;
1346     CkWindow *winPtr = entryPtr->winPtr;
1347
1348     length = strlen(string);
1349
1350     if (string[0] == 'a') {
1351         if (strncmp(string, "anchor", length) == 0) {
1352             *indexPtr = entryPtr->selectAnchor;
1353         } else {
1354             badIndex:
1355
1356             /*
1357              * Some of the paths here leave messages in interp->result,
1358              * so we have to clear it out before storing our own message.
1359              */
1360
1361             Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
1362             Tcl_AppendResult(interp, "bad entry index \"", string,
1363                     "\"", (char *) NULL);
1364             return TCL_ERROR;
1365         }
1366     } else if (string[0] == 'e') {
1367         if (strncmp(string, "end", length) == 0) {
1368             *indexPtr = entryPtr->numChars;
1369         } else {
1370             goto badIndex;
1371         }
1372     } else if (string[0] == 'i') {
1373         if (strncmp(string, "insert", length) == 0) {
1374             *indexPtr = entryPtr->insertPos;
1375         } else {
1376             goto badIndex;
1377         }
1378     } else if (string[0] == 's') {
1379         if (entryPtr->selectFirst == -1) {
1380             interp->result = "selection isn't in entry";
1381             return TCL_ERROR;
1382         }
1383         if (length < 5) {
1384             goto badIndex;
1385         }
1386         if (strncmp(string, "sel.first", length) == 0) {
1387             *indexPtr = entryPtr->selectFirst;
1388         } else if (strncmp(string, "sel.last", length) == 0) {
1389             *indexPtr = entryPtr->selectLast;
1390         } else {
1391             goto badIndex;
1392         }
1393     } else if (string[0] == '@') {
1394         int x, roundUp;
1395
1396         if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
1397             goto badIndex;
1398         }
1399         if (x < 0) {
1400             x = 0;
1401         }
1402         roundUp = 0;
1403         if (x >= entryPtr->winPtr->width) {
1404             x = entryPtr->winPtr->width - 1;
1405             roundUp = 1;
1406         }
1407         if (entryPtr->numChars == 0) {
1408             *indexPtr = 0;
1409         } else {
1410             char *string = (entryPtr->displayString == NULL) ?
1411                 entryPtr->string : entryPtr->displayString;
1412
1413             *indexPtr = CkMeasureChars(winPtr->mainPtr, string, strlen(string),
1414                 entryPtr->tabOrigin, x,
1415                 entryPtr->tabOrigin, CK_NEWLINES_NOT_SPECIAL, &dummy, &dummy);
1416 #if 0
1417             *indexPtr = entryPtr->leftX + entryPtr->leftIndex + x;
1418 #endif
1419         }
1420         if (*indexPtr >= entryPtr->numChars)
1421             *indexPtr = entryPtr->numChars;
1422     } else {
1423         if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
1424             goto badIndex;
1425         }
1426         if (*indexPtr < 0){
1427             *indexPtr = 0;
1428         } else if (*indexPtr > entryPtr->numChars) {
1429             *indexPtr = entryPtr->numChars;
1430         }
1431     }
1432     return TCL_OK;
1433 }
1434 \f
1435 /*
1436  *----------------------------------------------------------------------
1437  *
1438  * EntrySelectTo --
1439  *
1440  *      Modify the selection by moving its un-anchored end.  This could
1441  *      make the selection either larger or smaller.
1442  *
1443  * Results:
1444  *      None.
1445  *
1446  * Side effects:
1447  *      The selection changes.
1448  *
1449  *----------------------------------------------------------------------
1450  */
1451
1452 static void
1453 EntrySelectTo(entryPtr, index)
1454     register Entry *entryPtr;           /* Information about widget. */
1455     int index;                          /* Index of element that is to
1456                                          * become the "other" end of the
1457                                          * selection. */
1458 {
1459     int newFirst, newLast;
1460
1461     /*
1462      * Pick new starting and ending points for the selection.
1463      */
1464
1465     if (entryPtr->selectAnchor > entryPtr->numChars) {
1466         entryPtr->selectAnchor = entryPtr->numChars;
1467     }
1468     if (entryPtr->selectAnchor <= index) {
1469         newFirst = entryPtr->selectAnchor;
1470         newLast = index;
1471     } else {
1472         newFirst = index;
1473         newLast = entryPtr->selectAnchor;
1474         if (newLast < 0) {
1475             newFirst = newLast = -1;
1476         }
1477     }
1478     if ((entryPtr->selectFirst == newFirst)
1479             && (entryPtr->selectLast == newLast)) {
1480         return;
1481     }
1482     entryPtr->selectFirst = newFirst;
1483     entryPtr->selectLast = newLast;
1484     EventuallyRedraw(entryPtr);
1485 }
1486 \f
1487 /*
1488  *----------------------------------------------------------------------
1489  *
1490  * EventuallyRedraw --
1491  *
1492  *      Ensure that an entry is eventually redrawn on the display.
1493  *
1494  * Results:
1495  *      None.
1496  *
1497  * Side effects:
1498  *      Information gets redisplayed.  Right now we don't do selective
1499  *      redisplays:  the whole window will be redrawn.  This doesn't
1500  *      seem to hurt performance noticeably, but if it does then this
1501  *      could be changed.
1502  *
1503  *----------------------------------------------------------------------
1504  */
1505
1506 static void
1507 EventuallyRedraw(entryPtr)
1508     Entry *entryPtr;            /* Information about widget. */
1509 {
1510     if ((entryPtr->winPtr == NULL) || !(entryPtr->winPtr->flags & CK_MAPPED)) {
1511         return;
1512     }
1513
1514     /*
1515      * Right now we don't do selective redisplays:  the whole window
1516      * will be redrawn.  This doesn't seem to hurt performance noticeably,
1517      * but if it does then this could be changed.
1518      */
1519
1520     if (!(entryPtr->flags & REDRAW_PENDING)) {
1521         entryPtr->flags |= REDRAW_PENDING;
1522         Tk_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
1523     }
1524 }
1525 \f
1526 /*
1527  *----------------------------------------------------------------------
1528  *
1529  * EntryVisibleRange --
1530  *
1531  *      Return information about the range of the entry that is
1532  *      currently visible.
1533  *
1534  * Results:
1535  *      *firstPtr and *lastPtr are modified to hold fractions between
1536  *      0 and 1 identifying the range of characters visible in the
1537  *      entry.
1538  *
1539  * Side effects:
1540  *      None.
1541  *
1542  *----------------------------------------------------------------------
1543  */
1544
1545 static void
1546 EntryVisibleRange(entryPtr, firstPtr, lastPtr)
1547     Entry *entryPtr;                    /* Information about widget. */
1548     double *firstPtr;                   /* Return position of first visible
1549                                          * character in widget. */
1550     double *lastPtr;                    /* Return position of char just after
1551                                          * last visible one. */
1552 {
1553     char *displayString;
1554     int charsInWindow, endX, dummy;
1555     CkWindow *winPtr = entryPtr->winPtr;
1556
1557     if (entryPtr->displayString == NULL) {
1558         displayString = entryPtr->string;
1559     } else {
1560         displayString = entryPtr->displayString;
1561     }
1562     if (entryPtr->numChars == 0) {
1563         *firstPtr = 0.0;
1564         *lastPtr = 1.0;
1565     } else {
1566         int leftIndex, total;
1567
1568 #if CK_USE_UTF
1569         leftIndex = Tcl_UtfAtIndex(displayString, entryPtr->leftIndex) -
1570             displayString;
1571         total = entryPtr->numBytes - leftIndex;
1572 #else
1573         leftIndex = entryPtr->leftIndex;
1574         total = entryPtr->numChars - leftIndex;
1575 #endif
1576         charsInWindow = CkMeasureChars(winPtr->mainPtr,
1577             displayString + leftIndex, total, 0,
1578             entryPtr->winPtr->width, 0,
1579             CK_AT_LEAST_ONE|CK_NEWLINES_NOT_SPECIAL, &endX, &dummy);
1580         *firstPtr = ((double) leftIndex)/entryPtr->numChars;
1581         *lastPtr = ((double) (leftIndex + charsInWindow))
1582                 /entryPtr->numChars;
1583     }
1584 }
1585 \f
1586 /*
1587  *----------------------------------------------------------------------
1588  *
1589  * EntryUpdateScrollbar --
1590  *
1591  *      This procedure is invoked whenever information has changed in
1592  *      an entry in a way that would invalidate a scrollbar display.
1593  *      If there is an associated scrollbar, then this procedure updates
1594  *      it by invoking a Tcl command.
1595  *
1596  * Results:
1597  *      None.
1598  *
1599  * Side effects:
1600  *      A Tcl command is invoked, and an additional command may be
1601  *      invoked to process errors in the command.
1602  *
1603  *----------------------------------------------------------------------
1604  */
1605
1606 static void
1607 EntryUpdateScrollbar(entryPtr)
1608     Entry *entryPtr;                    /* Information about widget. */
1609 {
1610     char args[100];
1611     int code;
1612     double first, last;
1613
1614     if (entryPtr->scrollCmd == NULL) {
1615         return;
1616     }
1617
1618     EntryVisibleRange(entryPtr, &first, &last);
1619     sprintf(args, " %g %g", first, last);
1620     code = Tcl_VarEval(entryPtr->interp, entryPtr->scrollCmd, args,
1621             (char *) NULL);
1622     if (code != TCL_OK) {
1623         Tcl_AddErrorInfo(entryPtr->interp,
1624                 "\n    (horizontal scrolling command executed by entry)");
1625         Tk_BackgroundError(entryPtr->interp);
1626     }
1627     Tcl_SetResult(entryPtr->interp, (char *) NULL, TCL_STATIC);
1628 }
1629 \f
1630 /*
1631  *----------------------------------------------------------------------
1632  *
1633  * EntryFocusProc --
1634  *
1635  *      This procedure is called whenever the entry gets or loses the
1636  *      input focus.  It's also called whenever the window is reconfigured
1637  *      while it has the focus.
1638  *
1639  * Results:
1640  *      None.
1641  *
1642  * Side effects:
1643  *      The cursor gets turned on or off.
1644  *
1645  *----------------------------------------------------------------------
1646  */
1647
1648 static void
1649 EntryFocusProc(entryPtr, gotFocus)
1650     Entry *entryPtr;            /* Entry that got or lost focus. */
1651     int gotFocus;               /* 1 means window is getting focus, 0 means
1652                                  * it's losing it. */
1653 {
1654     if (gotFocus)
1655         entryPtr->flags |= GOT_FOCUS;
1656     else
1657         entryPtr->flags &= ~GOT_FOCUS;
1658 }
1659 \f
1660 /*
1661  *--------------------------------------------------------------
1662  *
1663  * EntryTextVarProc --
1664  *
1665  *      This procedure is invoked when someone changes the variable
1666  *      whose contents are to be displayed in an entry.
1667  *
1668  * Results:
1669  *      NULL is always returned.
1670  *
1671  * Side effects:
1672  *      The text displayed in the entry will change to match the
1673  *      variable.
1674  *
1675  *--------------------------------------------------------------
1676  */
1677
1678 static char *
1679 EntryTextVarProc(clientData, interp, name1, name2, flags)
1680     ClientData clientData;      /* Information about button. */
1681     Tcl_Interp *interp;         /* Interpreter containing variable. */
1682     char *name1;                /* Not used. */
1683     char *name2;                /* Not used. */
1684     int flags;                  /* Information about what happened. */
1685 {
1686     register Entry *entryPtr = (Entry *) clientData;
1687     char *value;
1688
1689     /*
1690      * If the variable is unset, then immediately recreate it unless
1691      * the whole interpreter is going away.
1692      */
1693
1694     if (flags & TCL_TRACE_UNSETS) {
1695         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1696             Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
1697                     TCL_GLOBAL_ONLY);
1698             Tcl_TraceVar(interp, entryPtr->textVarName,
1699                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1700                     EntryTextVarProc, clientData);
1701         }
1702         return (char *) NULL;
1703     }
1704
1705     /*
1706      * Update the entry's text with the value of the variable, unless
1707      * the entry already has that value (this happens when the variable
1708      * changes value because we changed it because someone typed in
1709      * the entry).
1710      */
1711
1712     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
1713     if (value == NULL) {
1714         value = "";
1715     }
1716     if (strcmp(value, entryPtr->string) != 0) {
1717         EntrySetValue(entryPtr, value);
1718     }
1719     return (char *) NULL;
1720 }
1721
1722
1723
1724