home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkEntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-30  |  54.9 KB  |  1,797 lines

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