Annotation of micropolis/src/tk/tkentry.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.