home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkEntry.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  71KB  |  2,256 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-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkEntry.c 1.101 96/03/21 13:55:45
  15.  */
  16.  
  17. #include "default.h"
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20.  
  21. /*
  22.  * A data structure of the following type is kept for each entry
  23.  * widget managed by this file:
  24.  */
  25.  
  26. typedef struct {
  27.     Tk_Window tkwin;        /* Window that embodies the entry. NULL
  28.                  * means that the window has been destroyed
  29.                  * but the data structures haven't yet been
  30.                  * cleaned up.*/
  31.     Display *display;        /* Display containing widget.  Used, among
  32.                  * other things, so that resources can be
  33.                  * freed even after tkwin has gone away. */
  34.     Tcl_Interp *interp;        /* Interpreter associated with entry. */
  35.     Tcl_Command widgetCmd;    /* Token for entry's widget command. */
  36.     int numChars;        /* Number of non-NULL characters in
  37.                  * string (may be 0). */
  38.     char *string;        /* Pointer to storage for string;
  39.                  * NULL-terminated;  malloc-ed. */
  40.     char *textVarName;        /* Name of variable (malloc'ed) or NULL.
  41.                  * If non-NULL, entry's string tracks the
  42.                  * contents of this variable and vice versa. */
  43.     Tk_Uid state;        /* Normal or disabled.  Entry is read-only
  44.                  * when disabled. */
  45.  
  46.     /*
  47.      * Information used when displaying widget:
  48.      */
  49.  
  50.     Tk_3DBorder normalBorder;    /* Used for drawing border around whole
  51.                  * window, plus used for background. */
  52.     int borderWidth;        /* Width of 3-D border around window. */
  53.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  54.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  55.     XColor *fgColorPtr;        /* Text color in normal mode. */
  56.     GC textGC;            /* For drawing normal text. */
  57.     Tk_3DBorder selBorder;    /* Border and background for selected
  58.                  * characters. */
  59.     int selBorderWidth;        /* Width of border around selection. */
  60.     XColor *selFgColorPtr;    /* Foreground color for selected text. */
  61.     GC selTextGC;        /* For drawing selected text. */
  62.     Tk_3DBorder insertBorder;    /* Used to draw vertical bar for insertion
  63.                  * cursor. */
  64.     int insertWidth;        /* Total width of insert cursor. */
  65.     int insertBorderWidth;    /* Width of 3-D border around insert cursor. */
  66.     int insertOnTime;        /* Number of milliseconds cursor should spend
  67.                  * in "on" state for each blink. */
  68.     int insertOffTime;        /* Number of milliseconds cursor should spend
  69.                  * in "off" state for each blink. */
  70.     Tcl_TimerToken insertBlinkHandler;
  71.                 /* Timer handler used to blink cursor on and
  72.                  * off. */
  73.     int highlightWidth;        /* Width in pixels of highlight to draw
  74.                  * around widget when it has the focus.
  75.                  * <= 0 means don't draw a highlight. */
  76.     XColor *highlightBgColorPtr;
  77.                 /* Color for drawing traversal highlight
  78.                  * area when highlight is off. */
  79.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  80.     GC highlightGC;        /* For drawing traversal highlight. */
  81.     Tk_Justify justify;        /* Justification to use for text within
  82.                  * window. */
  83.     int avgWidth;        /* Width of average character. */
  84.     int prefWidth;        /* Desired width of window, measured in
  85.                  * average characters. */
  86.     int inset;            /* Number of pixels on the left and right
  87.                  * sides that are taken up by XPAD, borderWidth
  88.                  * (if any), and highlightWidth (if any). */
  89.     int leftIndex;        /* Index of left-most character visible in
  90.                  * window. */
  91.     int leftX;            /* X position at which leftIndex is drawn
  92.                  * (varies depending on justify). */
  93.     int tabOrigin;        /* Origin for tabs (left edge of string[0]). */
  94.     int insertPos;        /* Index of character before which next
  95.                  * typed character will be inserted. */
  96.     char *showChar;        /* Value of -show option.  If non-NULL, first
  97.                  * character is used for displaying all
  98.                  * characters in entry.  Malloc'ed. */
  99.     char *displayString;    /* If non-NULL, points to string with same
  100.                  * length as string but whose characters
  101.                  * are all equal to showChar.  Malloc'ed. */
  102.  
  103.     /*
  104.      * Information about what's selected, if any.
  105.      */
  106.  
  107.     int selectFirst;        /* Index of first selected character (-1 means
  108.                  * nothing selected. */
  109.     int selectLast;        /* Index of last selected character (-1 means
  110.                  * nothing selected. */
  111.     int selectAnchor;        /* Fixed end of selection (i.e. "select to"
  112.                  * operation will use this as one end of the
  113.                  * selection). */
  114.     int exportSelection;    /* Non-zero means tie internal entry selection
  115.                  * to X selection. */
  116.  
  117.     /*
  118.      * Information for scanning:
  119.      */
  120.  
  121.     int scanMarkX;        /* X-position at which scan started (e.g.
  122.                  * button was pressed here). */
  123.     int scanMarkIndex;        /* Index of character that was at left of
  124.                  * window when scan started. */
  125.  
  126.     /*
  127.      * Miscellaneous information:
  128.      */
  129.  
  130.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  131.     char *takeFocus;        /* Value of -takefocus option;  not used in
  132.                  * the C code, but used by keyboard traversal
  133.                  * scripts.  Malloc'ed, but may be NULL. */
  134.     char *scrollCmd;        /* Command prefix for communicating with
  135.                  * scrollbar(s).  Malloc'ed.  NULL means
  136.                  * no command to issue. */
  137.     int flags;            /* Miscellaneous flags;  see below for
  138.                  * definitions. */
  139. } Entry;
  140.  
  141. /*
  142.  * Assigned bits of "flags" fields of Entry structures, and what those
  143.  * bits mean:
  144.  *
  145.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler has
  146.  *                already been queued to redisplay the entry.
  147.  * BORDER_NEEDED:        Non-zero means 3-D border must be redrawn
  148.  *                around window during redisplay.  Normally
  149.  *                only text portion needs to be redrawn.
  150.  * CURSOR_ON:            Non-zero means insert cursor is displayed at
  151.  *                present.  0 means it isn't displayed.
  152.  * GOT_FOCUS:            Non-zero means this window has the input
  153.  *                focus.
  154.  * UPDATE_SCROLLBAR:        Non-zero means scrollbar should be updated
  155.  *                during next redisplay operation.
  156.  */
  157.  
  158. #define REDRAW_PENDING        1
  159. #define BORDER_NEEDED        2
  160. #define CURSOR_ON        4
  161. #define GOT_FOCUS        8
  162. #define UPDATE_SCROLLBAR    16
  163.  
  164. /*
  165.  * The following macro defines how many extra pixels to leave on each
  166.  * side of the text in the entry.
  167.  */
  168.  
  169. #define XPAD 1
  170. #define YPAD 1
  171.  
  172. /*
  173.  * Information used for argv parsing.
  174.  */
  175.  
  176. static Tk_ConfigSpec configSpecs[] = {
  177.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  178.     DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
  179.     TK_CONFIG_COLOR_ONLY},
  180.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  181.     DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
  182.     TK_CONFIG_MONO_ONLY},
  183.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  184.     (char *) NULL, 0, 0},
  185.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  186.     (char *) NULL, 0, 0},
  187.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  188.     DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
  189.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  190.     DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
  191.     {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
  192.     "ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
  193.     Tk_Offset(Entry, exportSelection), 0},
  194.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  195.     (char *) NULL, 0, 0},
  196.     {TK_CONFIG_FONT, "-font", "font", "Font",
  197.     DEF_ENTRY_FONT, Tk_Offset(Entry, fontPtr), 0},
  198.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  199.     DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
  200.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  201.     "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
  202.     Tk_Offset(Entry, highlightBgColorPtr), 0},
  203.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  204.     DEF_ENTRY_HIGHLIGHT, Tk_Offset(Entry, highlightColorPtr), 0},
  205.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  206.     "HighlightThickness",
  207.     DEF_ENTRY_HIGHLIGHT_WIDTH, Tk_Offset(Entry, highlightWidth), 0},
  208.     {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
  209.     DEF_ENTRY_INSERT_BG, Tk_Offset(Entry, insertBorder), 0},
  210.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  211.     DEF_ENTRY_INSERT_BD_COLOR, Tk_Offset(Entry, insertBorderWidth),
  212.     TK_CONFIG_COLOR_ONLY},
  213.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  214.     DEF_ENTRY_INSERT_BD_MONO, Tk_Offset(Entry, insertBorderWidth),
  215.     TK_CONFIG_MONO_ONLY},
  216.     {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
  217.     DEF_ENTRY_INSERT_OFF_TIME, Tk_Offset(Entry, insertOffTime), 0},
  218.     {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
  219.     DEF_ENTRY_INSERT_ON_TIME, Tk_Offset(Entry, insertOnTime), 0},
  220.     {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
  221.     DEF_ENTRY_INSERT_WIDTH, Tk_Offset(Entry, insertWidth), 0},
  222.     {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
  223.     DEF_ENTRY_JUSTIFY, Tk_Offset(Entry, justify), 0},
  224.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  225.     DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
  226.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  227.     DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
  228.     TK_CONFIG_COLOR_ONLY},
  229.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  230.     DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
  231.     TK_CONFIG_MONO_ONLY},
  232.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  233.     DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
  234.     TK_CONFIG_COLOR_ONLY},
  235.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  236.     DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
  237.     TK_CONFIG_MONO_ONLY},
  238.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  239.     DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
  240.     TK_CONFIG_COLOR_ONLY},
  241.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  242.     DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
  243.     TK_CONFIG_MONO_ONLY},
  244.     {TK_CONFIG_STRING, "-show", "show", "Show",
  245.     DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
  246.     {TK_CONFIG_UID, "-state", "state", "State",
  247.     DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
  248.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  249.     DEF_ENTRY_TAKE_FOCUS, Tk_Offset(Entry, takeFocus), TK_CONFIG_NULL_OK},
  250.     {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
  251.     DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
  252.     TK_CONFIG_NULL_OK},
  253.     {TK_CONFIG_INT, "-width", "width", "Width",
  254.     DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
  255.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  256.     DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd),
  257.     TK_CONFIG_NULL_OK},
  258.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  259.     (char *) NULL, 0, 0}
  260. };
  261.  
  262. /*
  263.  * Flags for GetEntryIndex procedure:
  264.  */
  265.  
  266. #define ZERO_OK            1
  267. #define LAST_PLUS_ONE_OK    2
  268.  
  269. /*
  270.  * Forward declarations for procedures defined later in this file:
  271.  */
  272.  
  273. static int        ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
  274.                 Entry *entryPtr, int argc, char **argv,
  275.                 int flags));
  276. static void        DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
  277.                 int count));
  278. static void        DestroyEntry _ANSI_ARGS_((char *memPtr));
  279. static void        DisplayEntry _ANSI_ARGS_((ClientData clientData));
  280. static void        EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
  281. static void        EntryCmdDeletedProc _ANSI_ARGS_((
  282.                 ClientData clientData));
  283. static void        EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
  284. static void        EntryEventProc _ANSI_ARGS_((ClientData clientData,
  285.                 XEvent *eventPtr));
  286. static void        EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
  287.                 int gotFocus));
  288. static int        EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
  289.                 int offset, char *buffer, int maxBytes));
  290. static void        EntryLostSelection _ANSI_ARGS_((
  291.                 ClientData clientData));
  292. static void        EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
  293. static void        EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
  294. static void        EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
  295.                 char *value));
  296. static void        EntrySelectTo _ANSI_ARGS_((
  297.                 Entry *entryPtr, int index));
  298. static char *        EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
  299.                 Tcl_Interp *interp, char *name1, char *name2,
  300.                 int flags));
  301. static void        EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
  302. static void        EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
  303. static void        EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
  304.                 double *firstPtr, double *lastPtr));
  305. static int        EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
  306.                 Tcl_Interp *interp, int argc, char **argv));
  307. static int        GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
  308.                 Entry *entryPtr, char *string, int *indexPtr));
  309. static void        InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
  310.                 char *string));
  311.  
  312. /*
  313.  *--------------------------------------------------------------
  314.  *
  315.  * Tk_EntryCmd --
  316.  *
  317.  *    This procedure is invoked to process the "entry" Tcl
  318.  *    command.  See the user documentation for details on what
  319.  *    it does.
  320.  *
  321.  * Results:
  322.  *    A standard Tcl result.
  323.  *
  324.  * Side effects:
  325.  *    See the user documentation.
  326.  *
  327.  *--------------------------------------------------------------
  328.  */
  329.  
  330. int
  331. Tk_EntryCmd(clientData, interp, argc, argv)
  332.     ClientData clientData;    /* Main window associated with
  333.                  * interpreter. */
  334.     Tcl_Interp *interp;        /* Current interpreter. */
  335.     int argc;            /* Number of arguments. */
  336.     char **argv;        /* Argument strings. */
  337. {
  338.     Tk_Window tkwin = (Tk_Window) clientData;
  339.     register Entry *entryPtr;
  340.     Tk_Window new;
  341.  
  342.     if (argc < 2) {
  343.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  344.         argv[0], " pathName ?options?\"", (char *) NULL);
  345.     return TCL_ERROR;
  346.     }
  347.  
  348.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  349.     if (new == NULL) {
  350.     return TCL_ERROR;
  351.     }
  352.  
  353.     /*
  354.      * Initialize the fields of the structure that won't be initialized
  355.      * by ConfigureEntry, or that ConfigureEntry requires to be
  356.      * initialized already (e.g. resource pointers).
  357.      */
  358.  
  359.     entryPtr = (Entry *) ckalloc(sizeof(Entry));
  360.     entryPtr->tkwin = new;
  361.     entryPtr->display = Tk_Display(new);
  362.     entryPtr->interp = interp;
  363.     entryPtr->widgetCmd = Tcl_CreateCommand(interp,
  364.         Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
  365.         (ClientData) entryPtr, EntryCmdDeletedProc);
  366.     entryPtr->numChars = 0;
  367.     entryPtr->string = (char *) ckalloc(1);
  368.     entryPtr->string[0] = '\0';
  369.     entryPtr->textVarName = NULL;
  370.     entryPtr->state = tkNormalUid;
  371.     entryPtr->normalBorder = NULL;
  372.     entryPtr->borderWidth = 0;
  373.     entryPtr->relief = TK_RELIEF_FLAT;
  374.     entryPtr->fontPtr = NULL;
  375.     entryPtr->fgColorPtr = NULL;
  376.     entryPtr->textGC = None;
  377.     entryPtr->selBorder = NULL;
  378.     entryPtr->selBorderWidth = 0;
  379.     entryPtr->selFgColorPtr = NULL;
  380.     entryPtr->selTextGC = None;
  381.     entryPtr->insertBorder = NULL;
  382.     entryPtr->insertWidth = 0;
  383.     entryPtr->insertBorderWidth = 0;
  384.     entryPtr->insertOnTime = 0;
  385.     entryPtr->insertOffTime = 0;
  386.     entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
  387.     entryPtr->highlightWidth = 0;
  388.     entryPtr->highlightBgColorPtr = NULL;
  389.     entryPtr->highlightColorPtr = NULL;
  390.     entryPtr->justify = TK_JUSTIFY_LEFT;
  391.     entryPtr->avgWidth = 1;
  392.     entryPtr->prefWidth = 0;
  393.     entryPtr->inset = XPAD;
  394.     entryPtr->leftIndex = 0;
  395.     entryPtr->leftX = 0;
  396.     entryPtr->tabOrigin = 0;
  397.     entryPtr->insertPos = 0;
  398.     entryPtr->showChar = NULL;
  399.     entryPtr->displayString = NULL;
  400.     entryPtr->selectFirst = -1;
  401.     entryPtr->selectLast = -1;
  402.     entryPtr->selectAnchor = 0;
  403.     entryPtr->exportSelection = 1;
  404.     entryPtr->scanMarkX = 0;
  405.     entryPtr->scanMarkIndex = 0;
  406.     entryPtr->cursor = None;
  407.     entryPtr->takeFocus = NULL;
  408.     entryPtr->scrollCmd = NULL;
  409.     entryPtr->flags = 0;
  410.  
  411.     Tk_SetClass(entryPtr->tkwin, "Entry");
  412.     Tk_CreateEventHandler(entryPtr->tkwin,
  413.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  414.         EntryEventProc, (ClientData) entryPtr);
  415.     Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
  416.         EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
  417.     if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
  418.     goto error;
  419.     }
  420.  
  421.     interp->result = Tk_PathName(entryPtr->tkwin);
  422.     return TCL_OK;
  423.  
  424.     error:
  425.     Tk_DestroyWindow(entryPtr->tkwin);
  426.     return TCL_ERROR;
  427. }
  428.  
  429. /*
  430.  *--------------------------------------------------------------
  431.  *
  432.  * EntryWidgetCmd --
  433.  *
  434.  *    This procedure is invoked to process the Tcl command
  435.  *    that corresponds to a widget managed by this module.
  436.  *    See the user documentation for details on what it does.
  437.  *
  438.  * Results:
  439.  *    A standard Tcl result.
  440.  *
  441.  * Side effects:
  442.  *    See the user documentation.
  443.  *
  444.  *--------------------------------------------------------------
  445.  */
  446.  
  447. static int
  448. EntryWidgetCmd(clientData, interp, argc, argv)
  449.     ClientData clientData;        /* Information about entry widget. */
  450.     Tcl_Interp *interp;            /* Current interpreter. */
  451.     int argc;                /* Number of arguments. */
  452.     char **argv;            /* Argument strings. */
  453. {
  454.     register Entry *entryPtr = (Entry *) clientData;
  455.     int result = TCL_OK;
  456.     size_t length;
  457.     int c, height;
  458.  
  459.     if (argc < 2) {
  460.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  461.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  462.     return TCL_ERROR;
  463.     }
  464.     Tcl_Preserve((ClientData) entryPtr);
  465.     c = argv[1][0];
  466.     length = strlen(argv[1]);
  467.     if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
  468.     int index, x1, x2;
  469.  
  470.     if (argc != 3) {
  471.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  472.             argv[0], " bbox index\"",
  473.             (char *) NULL);
  474.         goto error;
  475.     }
  476.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  477.         goto error;
  478.     }
  479.     if ((index == entryPtr->numChars) && (index > 0)) {
  480.         index--;
  481.     }
  482.     TkMeasureChars(entryPtr->fontPtr,
  483.         (entryPtr->displayString == NULL) ? entryPtr->string
  484.         : entryPtr->displayString, index, entryPtr->tabOrigin,
  485.         1000000, entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL,
  486.         &x1);
  487.     if (index < entryPtr->numChars) {
  488.         TkMeasureChars(entryPtr->fontPtr,
  489.         (entryPtr->displayString == NULL) ? entryPtr->string
  490.         : entryPtr->displayString, index+1, entryPtr->tabOrigin,
  491.         1000000, entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL,
  492.         &x2);
  493.     } else {
  494.         x2 = x1;
  495.     }
  496.     height = entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent;
  497.     sprintf(interp->result, "%d %d %d %d", x1, 
  498.         (Tk_Height(entryPtr->tkwin) - height)/2, x2-x1, height);
  499.     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  500.         && (length >= 2)) {
  501.     if (argc != 3) {
  502.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  503.             argv[0], " cget option\"",
  504.             (char *) NULL);
  505.         goto error;
  506.     }
  507.     result = Tk_ConfigureValue(interp, entryPtr->tkwin, configSpecs,
  508.         (char *) entryPtr, argv[2], 0);
  509.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  510.         && (length >= 2)) {
  511.     if (argc == 2) {
  512.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  513.             (char *) entryPtr, (char *) NULL, 0);
  514.     } else if (argc == 3) {
  515.         result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
  516.             (char *) entryPtr, argv[2], 0);
  517.     } else {
  518.         result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
  519.             TK_CONFIG_ARGV_ONLY);
  520.     }
  521.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
  522.     int first, last;
  523.  
  524.     if ((argc < 3) || (argc > 4)) {
  525.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  526.             argv[0], " delete firstIndex ?lastIndex?\"",
  527.             (char *) NULL);
  528.         goto error;
  529.     }
  530.     if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
  531.         goto error;
  532.     }
  533.     if (argc == 3) {
  534.         last = first+1;
  535.     } else {
  536.         if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
  537.         goto error;
  538.         }
  539.     }
  540.     if ((last >= first) && (entryPtr->state == tkNormalUid)) {
  541.         DeleteChars(entryPtr, first, last-first);
  542.     }
  543.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  544.     if (argc != 2) {
  545.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  546.             argv[0], " get\"", (char *) NULL);
  547.         goto error;
  548.     }
  549.     interp->result = entryPtr->string;
  550.     } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
  551.         && (length >= 2)) {
  552.     if (argc != 3) {
  553.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  554.             argv[0], " icursor pos\"",
  555.             (char *) NULL);
  556.         goto error;
  557.     }
  558.     if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
  559.         != TCL_OK) {
  560.         goto error;
  561.     }
  562.     EventuallyRedraw(entryPtr);
  563.     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
  564.         && (length >= 3)) {
  565.     int index;
  566.  
  567.     if (argc != 3) {
  568.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  569.             argv[0], " index string\"", (char *) NULL);
  570.         goto error;
  571.     }
  572.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  573.         goto error;
  574.     }
  575.     sprintf(interp->result, "%d", index);
  576.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
  577.         && (length >= 3)) {
  578.     int index;
  579.  
  580.     if (argc != 4) {
  581.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  582.             argv[0], " insert index text\"",
  583.             (char *) NULL);
  584.         goto error;
  585.     }
  586.     if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  587.         goto error;
  588.     }
  589.     if (entryPtr->state == tkNormalUid) {
  590.         InsertChars(entryPtr, index, argv[3]);
  591.     }
  592.     } else if ((c == 's') && (length >= 2)
  593.         && (strncmp(argv[1], "scan", length) == 0)) {
  594.     int x;
  595.  
  596.     if (argc != 4) {
  597.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  598.             argv[0], " scan mark|dragto x\"", (char *) NULL);
  599.         goto error;
  600.     }
  601.     if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
  602.         goto error;
  603.     }
  604.     if ((argv[2][0] == 'm')
  605.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  606.         entryPtr->scanMarkX = x;
  607.         entryPtr->scanMarkIndex = entryPtr->leftIndex;
  608.     } else if ((argv[2][0] == 'd')
  609.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  610.         EntryScanTo(entryPtr, x);
  611.     } else {
  612.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  613.             "\": must be mark or dragto", (char *) NULL);
  614.         goto error;
  615.     }
  616.     } else if ((c == 's') && (length >= 2)
  617.         && (strncmp(argv[1], "selection", length) == 0)) {
  618.     int index, index2;
  619.  
  620.     if (argc < 3) {
  621.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  622.             argv[0], " select option ?index?\"", (char *) NULL);
  623.         goto error;
  624.     }
  625.     length = strlen(argv[2]);
  626.     c = argv[2][0];
  627.     if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
  628.         if (argc != 3) {
  629.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  630.             argv[0], " selection clear\"", (char *) NULL);
  631.         goto error;
  632.         }
  633.         if (entryPtr->selectFirst != -1) {
  634.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  635.         EventuallyRedraw(entryPtr);
  636.         }
  637.         goto done;
  638.     } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
  639.         if (argc != 3) {
  640.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  641.             argv[0], " selection present\"", (char *) NULL);
  642.         goto error;
  643.         }
  644.         if (entryPtr->selectFirst == -1) {
  645.         interp->result = "0";
  646.         } else {
  647.         interp->result = "1";
  648.         }
  649.         goto done;
  650.     }
  651.     if (argc >= 4) {
  652.         if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
  653.         goto error;
  654.         }
  655.     }
  656.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  657.         if (argc != 4) {
  658.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  659.             argv[0], " selection adjust index\"",
  660.             (char *) NULL);
  661.         goto error;
  662.         }
  663.         if (entryPtr->selectFirst >= 0) {
  664.         int half1, half2;
  665.  
  666.         half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2;
  667.         half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2;
  668.         if (index < half1) {
  669.             entryPtr->selectAnchor = entryPtr->selectLast;
  670.         } else if (index > half2) {
  671.             entryPtr->selectAnchor = entryPtr->selectFirst;
  672.         } else {
  673.             /*
  674.              * We're at about the halfway point in the selection;
  675.              * just keep the existing anchor.
  676.              */
  677.         }
  678.         }
  679.         EntrySelectTo(entryPtr, index);
  680.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  681.         if (argc != 4) {
  682.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  683.             argv[0], " selection from index\"",
  684.             (char *) NULL);
  685.         goto error;
  686.         }
  687.         entryPtr->selectAnchor = index;
  688.     } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
  689.         if (argc != 5) {
  690.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  691.             argv[0], " selection range start end\"",
  692.             (char *) NULL);
  693.         goto error;
  694.         }
  695.         if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
  696.         goto error;
  697.         }
  698.         if (index >= index2) {
  699.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  700.         } else {
  701.         if ((entryPtr->selectFirst == -1)
  702.             && (entryPtr->exportSelection)) {
  703.             Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, 
  704.                 EntryLostSelection, (ClientData) entryPtr);
  705.         }
  706.         entryPtr->selectFirst = index;
  707.         entryPtr->selectLast = index2;
  708.         }
  709.         if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
  710.         Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
  711.             EntryLostSelection, (ClientData) entryPtr);
  712.         }
  713.         EventuallyRedraw(entryPtr);
  714.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  715.         if (argc != 4) {
  716.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  717.             argv[0], " selection to index\"",
  718.             (char *) NULL);
  719.         goto error;
  720.         }
  721.         EntrySelectTo(entryPtr, index);
  722.     } else {
  723.         Tcl_AppendResult(interp, "bad selection option \"", argv[2],
  724.             "\": must be adjust, clear, from, present, range, or to",
  725.             (char *) NULL);
  726.         goto error;
  727.     }
  728.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  729.     int index, type, count, charsPerPage;
  730.     double fraction, first, last;
  731.  
  732.     if (argc == 2) {
  733.         EntryVisibleRange(entryPtr, &first, &last);
  734.         sprintf(interp->result, "%g %g", first, last);
  735.         goto done;
  736.     } else if (argc == 3) {
  737.         if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
  738.         goto error;
  739.         }
  740.     } else {
  741.         type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  742.         index = entryPtr->leftIndex;
  743.         switch (type) {
  744.         case TK_SCROLL_ERROR:
  745.             goto error;
  746.         case TK_SCROLL_MOVETO:
  747.             index = (fraction * entryPtr->numChars) + 0.5;
  748.             break;
  749.         case TK_SCROLL_PAGES:
  750.             charsPerPage = ((Tk_Width(entryPtr->tkwin)
  751.                 - 2*entryPtr->inset) / entryPtr->avgWidth) - 2;
  752.             if (charsPerPage < 1) {
  753.             charsPerPage = 1;
  754.             }
  755.             index += charsPerPage*count;
  756.             break;
  757.         case TK_SCROLL_UNITS:
  758.             index += count;
  759.             break;
  760.         }
  761.     }
  762.     if (index >= entryPtr->numChars) {
  763.         index = entryPtr->numChars-1;
  764.     }
  765.     if (index < 0) {
  766.         index = 0;
  767.     }
  768.     entryPtr->leftIndex = index;
  769.     entryPtr->flags |= UPDATE_SCROLLBAR;
  770.     EntryComputeGeometry(entryPtr);
  771.     EventuallyRedraw(entryPtr);
  772.     } else {
  773.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  774.         "\": must be bbox, cget, configure, delete, get, ",
  775.         "icursor, index, insert, scan, selection, or xview",
  776.         (char *) NULL);
  777.     goto error;
  778.     }
  779.     done:
  780.     Tcl_Release((ClientData) entryPtr);
  781.     return result;
  782.  
  783.     error:
  784.     Tcl_Release((ClientData) entryPtr);
  785.     return TCL_ERROR;
  786. }
  787.  
  788. /*
  789.  *----------------------------------------------------------------------
  790.  *
  791.  * DestroyEntry --
  792.  *
  793.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  794.  *    to clean up the internal structure of an entry at a safe time
  795.  *    (when no-one is using it anymore).
  796.  *
  797.  * Results:
  798.  *    None.
  799.  *
  800.  * Side effects:
  801.  *    Everything associated with the entry is freed up.
  802.  *
  803.  *----------------------------------------------------------------------
  804.  */
  805.  
  806. static void
  807. DestroyEntry(memPtr)
  808.     char *memPtr;        /* Info about entry widget. */
  809. {
  810.     register Entry *entryPtr = (Entry *) memPtr;
  811.  
  812.     /*
  813.      * Free up all the stuff that requires special handling, then
  814.      * let Tk_FreeOptions handle all the standard option-related
  815.      * stuff.
  816.      */
  817.  
  818.     ckfree(entryPtr->string);
  819.     if (entryPtr->textVarName != NULL) {
  820.     Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
  821.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  822.         EntryTextVarProc, (ClientData) entryPtr);
  823.     }
  824.     if (entryPtr->textGC != None) {
  825.     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
  826.     }
  827.     if (entryPtr->selTextGC != None) {
  828.     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
  829.     }
  830.     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
  831.     if (entryPtr->displayString != NULL) {
  832.     ckfree(entryPtr->displayString);
  833.     }
  834.     Tk_FreeOptions(configSpecs, (char *) entryPtr, entryPtr->display, 0);
  835.     ckfree((char *) entryPtr);
  836. }
  837.  
  838. /*
  839.  *----------------------------------------------------------------------
  840.  *
  841.  * ConfigureEntry --
  842.  *
  843.  *    This procedure is called to process an argv/argc list, plus
  844.  *    the Tk option database, in order to configure (or reconfigure)
  845.  *    an entry widget.
  846.  *
  847.  * Results:
  848.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  849.  *    returned, then interp->result contains an error message.
  850.  *
  851.  * Side effects:
  852.  *    Configuration information, such as colors, border width,
  853.  *    etc. get set for entryPtr;  old resources get freed,
  854.  *    if there were any.
  855.  *
  856.  *----------------------------------------------------------------------
  857.  */
  858.  
  859. static int
  860. ConfigureEntry(interp, entryPtr, argc, argv, flags)
  861.     Tcl_Interp *interp;        /* Used for error reporting. */
  862.     register Entry *entryPtr;    /* Information about widget;  may or may
  863.                  * not already have values for some fields. */
  864.     int argc;            /* Number of valid entries in argv. */
  865.     char **argv;        /* Arguments. */
  866.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  867. {
  868.     XGCValues gcValues;
  869.     GC new;
  870.     int oldExport;
  871.  
  872.     /*
  873.      * Eliminate any existing trace on a variable monitored by the entry.
  874.      */
  875.  
  876.     if (entryPtr->textVarName != NULL) {
  877.     Tcl_UntraceVar(interp, entryPtr->textVarName, 
  878.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  879.         EntryTextVarProc, (ClientData) entryPtr);
  880.     }
  881.  
  882.     oldExport = entryPtr->exportSelection;
  883.     if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
  884.         argc, argv, (char *) entryPtr, flags) != TCL_OK) {
  885.     return TCL_ERROR;
  886.     }
  887.  
  888.     /*
  889.      * If the entry is tied to the value of a variable, then set up
  890.      * a trace on the variable's value, create the variable if it doesn't
  891.      * exist, and set the entry's value from the variable's value.
  892.      */
  893.  
  894.     if (entryPtr->textVarName != NULL) {
  895.     char *value;
  896.  
  897.     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
  898.     if (value == NULL) {
  899.         EntryValueChanged(entryPtr);
  900.     } else {
  901.         EntrySetValue(entryPtr, value);
  902.     }
  903.     Tcl_TraceVar(interp, entryPtr->textVarName,
  904.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  905.         EntryTextVarProc, (ClientData) entryPtr);
  906.     }
  907.  
  908.     /*
  909.      * A few other options also need special processing, such as parsing
  910.      * the geometry and setting the background from a 3-D border.
  911.      */
  912.  
  913.     if ((entryPtr->state != tkNormalUid)
  914.         && (entryPtr->state != tkDisabledUid)) {
  915.     Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
  916.         "\": must be normal or disabled", (char *) NULL);
  917.     entryPtr->state = tkNormalUid;
  918.     return TCL_ERROR;
  919.     }
  920.  
  921.     Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
  922.  
  923.     gcValues.foreground = entryPtr->fgColorPtr->pixel;
  924.     gcValues.font = entryPtr->fontPtr->fid;
  925.     gcValues.graphics_exposures = False;
  926.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
  927.         &gcValues);
  928.     if (entryPtr->textGC != None) {
  929.     Tk_FreeGC(entryPtr->display, entryPtr->textGC);
  930.     }
  931.     entryPtr->textGC = new;
  932.  
  933.     gcValues.foreground = entryPtr->selFgColorPtr->pixel;
  934.     gcValues.font = entryPtr->fontPtr->fid;
  935.     new = Tk_GetGC(entryPtr->tkwin, GCForeground|GCFont, &gcValues);
  936.     if (entryPtr->selTextGC != None) {
  937.     Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
  938.     }
  939.     entryPtr->selTextGC = new;
  940.  
  941.     if (entryPtr->insertWidth <= 0) {
  942.     entryPtr->insertWidth = 2;
  943.     }
  944.     if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
  945.     entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
  946.     }
  947.  
  948.     /*
  949.      * Restart the cursor timing sequence in case the on-time or off-time
  950.      * just changed.
  951.      */
  952.  
  953.     if (entryPtr->flags & GOT_FOCUS) {
  954.     EntryFocusProc(entryPtr, 1);
  955.     }
  956.  
  957.     /*
  958.      * Claim the selection if we've suddenly started exporting it.
  959.      */
  960.  
  961.     if (entryPtr->exportSelection && (!oldExport)
  962.         && (entryPtr->selectFirst != -1)) {
  963.     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
  964.         (ClientData) entryPtr);
  965.     }
  966.  
  967.     /*
  968.      * Recompute the window's geometry and arrange for it to be
  969.      * redisplayed.
  970.      */
  971.  
  972.     Tk_SetInternalBorder(entryPtr->tkwin,
  973.         entryPtr->borderWidth + entryPtr->highlightWidth);
  974.     if (entryPtr->highlightWidth <= 0) {
  975.     entryPtr->highlightWidth = 0;
  976.     }
  977.     entryPtr->inset = entryPtr->highlightWidth + entryPtr->borderWidth + XPAD;
  978.     entryPtr->avgWidth = XTextWidth(entryPtr->fontPtr, "0", 1);
  979.     EntryComputeGeometry(entryPtr);
  980.     entryPtr->flags |= UPDATE_SCROLLBAR;
  981.     EventuallyRedraw(entryPtr);
  982.     return TCL_OK;
  983. }
  984.  
  985. /*
  986.  *--------------------------------------------------------------
  987.  *
  988.  * DisplayEntry --
  989.  *
  990.  *    This procedure redraws the contents of an entry window.
  991.  *
  992.  * Results:
  993.  *    None.
  994.  *
  995.  * Side effects:
  996.  *    Information appears on the screen.
  997.  *
  998.  *--------------------------------------------------------------
  999.  */
  1000.  
  1001. static void
  1002. DisplayEntry(clientData)
  1003.     ClientData clientData;    /* Information about window. */
  1004. {
  1005.     register Entry *entryPtr = (Entry *) clientData;
  1006.     register Tk_Window tkwin = entryPtr->tkwin;
  1007.     int baseY, selStartX, selEndX, index, cursorX;
  1008.     int xBound, count;
  1009.     Pixmap pixmap;
  1010.     char *displayString;
  1011.  
  1012.     entryPtr->flags &= ~REDRAW_PENDING;
  1013.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  1014.     return;
  1015.     }
  1016.  
  1017.     /*
  1018.      * Update the scrollbar if that's needed.
  1019.      */
  1020.  
  1021.     if (entryPtr->flags & UPDATE_SCROLLBAR) {
  1022.     EntryUpdateScrollbar(entryPtr);
  1023.     }
  1024.  
  1025.     /*
  1026.      * In order to avoid screen flashes, this procedure redraws the
  1027.      * textual area of the entry into off-screen memory, then copies
  1028.      * it back on-screen in a single operation.  This means there's
  1029.      * no point in time where the on-screen image has been cleared.
  1030.      */
  1031.  
  1032.     pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
  1033.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1034.  
  1035.     /*
  1036.      * Compute x-coordinate of the pixel just after last visible
  1037.      * one, plus vertical position of baseline of text.
  1038.      */
  1039.  
  1040.     xBound = Tk_Width(tkwin) - entryPtr->inset;
  1041.     baseY = (Tk_Height(tkwin) + entryPtr->fontPtr->ascent
  1042.         - entryPtr->fontPtr->descent)/2;
  1043.  
  1044.     /*
  1045.      * Draw the background in three layers.  From bottom to top the
  1046.      * layers are:  normal background, selection background, and
  1047.      * insertion cursor background.
  1048.      */
  1049.  
  1050.     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1051.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  1052.     if (entryPtr->displayString == NULL) {
  1053.     displayString = entryPtr->string;
  1054.     } else {
  1055.     displayString = entryPtr->displayString;
  1056.     }
  1057.     if (entryPtr->selectLast > entryPtr->leftIndex) {
  1058.     if (entryPtr->selectFirst <= entryPtr->leftIndex) {
  1059.         selStartX = entryPtr->leftX;
  1060.         index = entryPtr->leftIndex;
  1061.     } else {
  1062.         (void) TkMeasureChars(entryPtr->fontPtr,
  1063.             displayString + entryPtr->leftIndex,
  1064.             entryPtr->selectFirst - entryPtr->leftIndex,
  1065.             entryPtr->leftX, xBound, entryPtr->tabOrigin,
  1066.             TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selStartX);
  1067.         index = entryPtr->selectFirst;
  1068.     }
  1069.     if ((selStartX - entryPtr->selBorderWidth) < xBound) {
  1070.         (void) TkMeasureChars(entryPtr->fontPtr,
  1071.             displayString + index, entryPtr->selectLast - index,
  1072.             selStartX, xBound, entryPtr->tabOrigin,
  1073.             TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &selEndX);
  1074.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
  1075.             selStartX - entryPtr->selBorderWidth,
  1076.             baseY - entryPtr->fontPtr->ascent
  1077.                 - entryPtr->selBorderWidth,
  1078.             (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
  1079.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent
  1080.                 + 2*entryPtr->selBorderWidth,
  1081.             entryPtr->selBorderWidth, TK_RELIEF_RAISED);
  1082.     } else {
  1083.         selEndX = xBound;
  1084.     }
  1085.     }
  1086.  
  1087.     /*
  1088.      * Draw a special background for the insertion cursor, overriding
  1089.      * even the selection background.  As a special hack to keep the
  1090.      * cursor visible when the insertion cursor color is the same as
  1091.      * the color for selected text (e.g., on mono displays), write
  1092.      * background in the cursor area (instead of nothing) when the
  1093.      * cursor isn't on.  Otherwise the selection would hide the cursor.
  1094.      */
  1095.  
  1096.     if ((entryPtr->insertPos >= entryPtr->leftIndex)
  1097.         && (entryPtr->state == tkNormalUid)
  1098.         && (entryPtr->flags & GOT_FOCUS)) {
  1099.     (void) TkMeasureChars(entryPtr->fontPtr,
  1100.         displayString + entryPtr->leftIndex,
  1101.         entryPtr->insertPos - entryPtr->leftIndex, entryPtr->leftX,
  1102.         xBound + entryPtr->insertWidth, entryPtr->tabOrigin,
  1103.         TK_PARTIAL_OK|TK_NEWLINES_NOT_SPECIAL, &cursorX);
  1104.     cursorX -= (entryPtr->insertWidth)/2;
  1105.     if (cursorX < xBound) {
  1106.         if (entryPtr->flags & CURSOR_ON) {
  1107.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
  1108.             cursorX, baseY - entryPtr->fontPtr->ascent,
  1109.             entryPtr->insertWidth,
  1110.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  1111.             entryPtr->insertBorderWidth, TK_RELIEF_RAISED);
  1112.         } else if (entryPtr->insertBorder == entryPtr->selBorder) {
  1113.         Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1114.             cursorX, baseY - entryPtr->fontPtr->ascent,
  1115.             entryPtr->insertWidth,
  1116.             entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent,
  1117.             0, TK_RELIEF_FLAT);
  1118.         }
  1119.     }
  1120.     }
  1121.  
  1122.     /*
  1123.      * Draw the text in three pieces:  first the piece to the left of
  1124.      * the selection, then the selection, then the piece to the right
  1125.      * of the selection.
  1126.      */
  1127.  
  1128.     if (entryPtr->selectLast <= entryPtr->leftIndex) {
  1129.     TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1130.         entryPtr->fontPtr, displayString + entryPtr->leftIndex,
  1131.         entryPtr->numChars - entryPtr->leftIndex, entryPtr->leftX,
  1132.         baseY, entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL);
  1133.     } else {
  1134.     count = entryPtr->selectFirst - entryPtr->leftIndex;
  1135.     if (count > 0) {
  1136.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1137.             entryPtr->fontPtr, displayString + entryPtr->leftIndex,
  1138.             count, entryPtr->leftX, baseY, entryPtr->tabOrigin,
  1139.             TK_NEWLINES_NOT_SPECIAL);
  1140.         index = entryPtr->selectFirst;
  1141.     } else {
  1142.         index = entryPtr->leftIndex;
  1143.     }
  1144.     count = entryPtr->selectLast - index;
  1145.     if ((selStartX < xBound) && (count > 0)) {
  1146.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->selTextGC,
  1147.             entryPtr->fontPtr, displayString + index, count,
  1148.             selStartX, baseY, entryPtr->tabOrigin,
  1149.             TK_NEWLINES_NOT_SPECIAL);
  1150.     }
  1151.     count = entryPtr->numChars - entryPtr->selectLast;
  1152.     if ((selEndX < xBound) && (count > 0)) {
  1153.         TkDisplayChars(entryPtr->display, pixmap, entryPtr->textGC,
  1154.             entryPtr->fontPtr,
  1155.             displayString + entryPtr->selectLast,
  1156.             count, selEndX, baseY, entryPtr->tabOrigin,
  1157.             TK_NEWLINES_NOT_SPECIAL);
  1158.     }
  1159.     }
  1160.  
  1161.     /*
  1162.      * Draw the border and focus highlight last, so they will overwrite
  1163.      * any text that extends past the viewable part of the window.
  1164.      */
  1165.  
  1166.     if (entryPtr->relief != TK_RELIEF_FLAT) {
  1167.     Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
  1168.         entryPtr->highlightWidth, entryPtr->highlightWidth,
  1169.         Tk_Width(tkwin) - 2*entryPtr->highlightWidth,
  1170.         Tk_Height(tkwin) - 2*entryPtr->highlightWidth,
  1171.         entryPtr->borderWidth, entryPtr->relief);
  1172.     }
  1173.     if (entryPtr->highlightWidth != 0) {
  1174.     GC gc;
  1175.  
  1176.     if (entryPtr->flags & GOT_FOCUS) {
  1177.         gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
  1178.     } else {
  1179.         gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
  1180.     }
  1181.     Tk_DrawFocusHighlight(tkwin, gc, entryPtr->highlightWidth, pixmap);
  1182.     }
  1183.  
  1184.     /*
  1185.      * Everything's been redisplayed;  now copy the pixmap onto the screen
  1186.      * and free up the pixmap.
  1187.      */
  1188.  
  1189.     XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
  1190.         0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
  1191.         0, 0);
  1192.     Tk_FreePixmap(entryPtr->display, pixmap);
  1193.     entryPtr->flags &= ~BORDER_NEEDED;
  1194. }
  1195.  
  1196. /*
  1197.  *----------------------------------------------------------------------
  1198.  *
  1199.  * EntryComputeGeometry --
  1200.  *
  1201.  *    This procedure is invoked to recompute information about where
  1202.  *    in its window an entry's string will be displayed.  It also
  1203.  *    computes the requested size for the window.
  1204.  *
  1205.  * Results:
  1206.  *    None.
  1207.  *
  1208.  * Side effects:
  1209.  *    The leftX and tabOrigin fields are recomputed for entryPtr,
  1210.  *    and leftIndex may be adjusted.  Tk_GeometryRequest is called
  1211.  *    to register the desired dimensions for the window.
  1212.  *
  1213.  *----------------------------------------------------------------------
  1214.  */
  1215.  
  1216. static void
  1217. EntryComputeGeometry(entryPtr)
  1218.     Entry *entryPtr;            /* Widget record for entry. */
  1219. {
  1220.     int totalLength, overflow, maxOffScreen, rightX;
  1221.     int fontHeight, height, width, i;
  1222.     char *p, *displayString;
  1223.  
  1224.     /*
  1225.      * If we're displaying a special character instead of the value of
  1226.      * the entry, recompute the displayString.
  1227.      */
  1228.  
  1229.     if (entryPtr->displayString != NULL) {
  1230.     ckfree(entryPtr->displayString);
  1231.     entryPtr->displayString = NULL;
  1232.     }
  1233.     if (entryPtr->showChar != NULL) {
  1234.     entryPtr->displayString = (char *) ckalloc((unsigned)
  1235.         (entryPtr->numChars + 1));
  1236.     for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
  1237.         i--, p++) {
  1238.         *p = entryPtr->showChar[0];
  1239.     }
  1240.     *p = 0;
  1241.     displayString = entryPtr->displayString;
  1242.     } else {
  1243.     displayString = entryPtr->string;
  1244.     }
  1245.  
  1246.     /*
  1247.      * Recompute where the leftmost character on the display will
  1248.      * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
  1249.      * so that we don't let characters hang off the edge of the
  1250.      * window unless the entire window is full.
  1251.      */
  1252.  
  1253.     TkMeasureChars(entryPtr->fontPtr, displayString, entryPtr->numChars,
  1254.         0, INT_MAX, 0, TK_NEWLINES_NOT_SPECIAL, &totalLength);
  1255.     overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
  1256.     if (overflow <= 0) {
  1257.     entryPtr->leftIndex = 0;
  1258.     if (entryPtr->justify == TK_JUSTIFY_LEFT) {
  1259.         entryPtr->leftX = entryPtr->inset;
  1260.     } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
  1261.         entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
  1262.             - totalLength;
  1263.     } else {
  1264.         entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
  1265.     }
  1266.     entryPtr->tabOrigin = entryPtr->leftX;
  1267.     } else {
  1268.     /*
  1269.      * The whole string can't fit in the window.  Compute the
  1270.      * maximum number of characters that may be off-screen to
  1271.      * the left without leaving empty space on the right of the
  1272.      * window, then don't let leftIndex be any greater than that.
  1273.      */
  1274.  
  1275.     maxOffScreen = TkMeasureChars(entryPtr->fontPtr, displayString,
  1276.         entryPtr->numChars, 0, overflow, 0,
  1277.         TK_NEWLINES_NOT_SPECIAL|TK_PARTIAL_OK, &rightX);
  1278.     if (rightX < overflow) {
  1279.         maxOffScreen += 1;
  1280.     }
  1281.     if (entryPtr->leftIndex > maxOffScreen) {
  1282.         entryPtr->leftIndex = maxOffScreen;
  1283.     }
  1284.     TkMeasureChars(entryPtr->fontPtr, displayString,
  1285.         entryPtr->leftIndex, 0, INT_MAX, 0,
  1286.         TK_NEWLINES_NOT_SPECIAL|TK_PARTIAL_OK, &rightX);
  1287.     entryPtr->leftX = entryPtr->inset;
  1288.     entryPtr->tabOrigin = entryPtr->leftX - rightX;
  1289.     }
  1290.  
  1291.     fontHeight = entryPtr->fontPtr->ascent + entryPtr->fontPtr->descent;
  1292.     height = fontHeight + 2*entryPtr->inset + 2*(YPAD-XPAD);
  1293.     if (entryPtr->prefWidth > 0) {
  1294.     width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
  1295.     } else {
  1296.     if (totalLength == 0) {
  1297.         width = entryPtr->avgWidth + 2*entryPtr->inset;
  1298.     } else {
  1299.         width = totalLength + 2*entryPtr->inset;
  1300.     }
  1301.     }
  1302.     Tk_GeometryRequest(entryPtr->tkwin, width, height);
  1303. }
  1304.  
  1305. /*
  1306.  *----------------------------------------------------------------------
  1307.  *
  1308.  * InsertChars --
  1309.  *
  1310.  *    Add new characters to an entry widget.
  1311.  *
  1312.  * Results:
  1313.  *    None.
  1314.  *
  1315.  * Side effects:
  1316.  *    New information gets added to entryPtr;  it will be redisplayed
  1317.  *    soon, but not necessarily immediately.
  1318.  *
  1319.  *----------------------------------------------------------------------
  1320.  */
  1321.  
  1322. static void
  1323. InsertChars(entryPtr, index, string)
  1324.     register Entry *entryPtr;    /* Entry that is to get the new
  1325.                  * elements. */
  1326.     int index;            /* Add the new elements before this
  1327.                  * element. */
  1328.     char *string;        /* New characters to add (NULL-terminated
  1329.                  * string). */
  1330. {
  1331.     int length;
  1332.     char *new;
  1333.  
  1334.     length = strlen(string);
  1335.     if (length == 0) {
  1336.     return;
  1337.     }
  1338.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
  1339.     strncpy(new, entryPtr->string, (size_t) index);
  1340.     strcpy(new+index, string);
  1341.     strcpy(new+index+length, entryPtr->string+index);
  1342.     ckfree(entryPtr->string);
  1343.     entryPtr->string = new;
  1344.     entryPtr->numChars += length;
  1345.  
  1346.     /*
  1347.      * Inserting characters invalidates all indexes into the string.
  1348.      * Touch up the indexes so that they still refer to the same
  1349.      * characters (at new positions).  When updating the selection
  1350.      * end-points, don't include the new text in the selection unless
  1351.      * it was completely surrounded by the selection.
  1352.      */
  1353.  
  1354.     if (entryPtr->selectFirst >= index) {
  1355.     entryPtr->selectFirst += length;
  1356.     }
  1357.     if (entryPtr->selectLast > index) {
  1358.     entryPtr->selectLast += length;
  1359.     }
  1360.     if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
  1361.     entryPtr->selectAnchor += length;
  1362.     }
  1363.     if (entryPtr->leftIndex > index) {
  1364.     entryPtr->leftIndex += length;
  1365.     }
  1366.     if (entryPtr->insertPos >= index) {
  1367.     entryPtr->insertPos += length;
  1368.     }
  1369.     EntryValueChanged(entryPtr);
  1370. }
  1371.  
  1372. /*
  1373.  *----------------------------------------------------------------------
  1374.  *
  1375.  * DeleteChars --
  1376.  *
  1377.  *    Remove one or more characters from an entry widget.
  1378.  *
  1379.  * Results:
  1380.  *    None.
  1381.  *
  1382.  * Side effects:
  1383.  *    Memory gets freed, the entry gets modified and (eventually)
  1384.  *    redisplayed.
  1385.  *
  1386.  *----------------------------------------------------------------------
  1387.  */
  1388.  
  1389. static void
  1390. DeleteChars(entryPtr, index, count)
  1391.     register Entry *entryPtr;    /* Entry widget to modify. */
  1392.     int index;            /* Index of first character to delete. */
  1393.     int count;            /* How many characters to delete. */
  1394. {
  1395.     char *new;
  1396.  
  1397.     if ((index + count) > entryPtr->numChars) {
  1398.     count = entryPtr->numChars - index;
  1399.     }
  1400.     if (count <= 0) {
  1401.     return;
  1402.     }
  1403.  
  1404.     new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
  1405.     strncpy(new, entryPtr->string, (size_t) index);
  1406.     strcpy(new+index, entryPtr->string+index+count);
  1407.     ckfree(entryPtr->string);
  1408.     entryPtr->string = new;
  1409.     entryPtr->numChars -= count;
  1410.  
  1411.     /*
  1412.      * Deleting characters results in the remaining characters being
  1413.      * renumbered.  Update the various indexes into the string to reflect
  1414.      * this change.
  1415.      */
  1416.  
  1417.     if (entryPtr->selectFirst >= index) {
  1418.     if (entryPtr->selectFirst >= (index+count)) {
  1419.         entryPtr->selectFirst -= count;
  1420.     } else {
  1421.         entryPtr->selectFirst = index;
  1422.     }
  1423.     }
  1424.     if (entryPtr->selectLast >= index) {
  1425.     if (entryPtr->selectLast >= (index+count)) {
  1426.         entryPtr->selectLast -= count;
  1427.     } else {
  1428.         entryPtr->selectLast = index;
  1429.     }
  1430.     }
  1431.     if (entryPtr->selectLast <= entryPtr->selectFirst) {
  1432.     entryPtr->selectFirst = entryPtr->selectLast = -1;
  1433.     }
  1434.     if (entryPtr->selectAnchor >= index) {
  1435.     if (entryPtr->selectAnchor >= (index+count)) {
  1436.         entryPtr->selectAnchor -= count;
  1437.     } else {
  1438.         entryPtr->selectAnchor = index;
  1439.     }
  1440.     }
  1441.     if (entryPtr->leftIndex > index) {
  1442.     if (entryPtr->leftIndex >= (index+count)) {
  1443.         entryPtr->leftIndex -= count;
  1444.     } else {
  1445.         entryPtr->leftIndex = index;
  1446.     }
  1447.     }
  1448.     if (entryPtr->insertPos >= index) {
  1449.     if (entryPtr->insertPos >= (index+count)) {
  1450.         entryPtr->insertPos -= count;
  1451.     } else {
  1452.         entryPtr->insertPos = index;
  1453.     }
  1454.     }
  1455.     EntryValueChanged(entryPtr);
  1456. }
  1457.  
  1458. /*
  1459.  *----------------------------------------------------------------------
  1460.  *
  1461.  * EntryValueChanged --
  1462.  *
  1463.  *    This procedure is invoked when characters are inserted into
  1464.  *    an entry or deleted from it.  It updates the entry's associated
  1465.  *    variable, if there is one, and does other bookkeeping such
  1466.  *    as arranging for redisplay.
  1467.  *
  1468.  * Results:
  1469.  *    None.
  1470.  *
  1471.  * Side effects:
  1472.  *    None.
  1473.  *
  1474.  *----------------------------------------------------------------------
  1475.  */
  1476.  
  1477. static void
  1478. EntryValueChanged(entryPtr)
  1479.     Entry *entryPtr;        /* Entry whose value just changed. */
  1480. {
  1481.     char *newValue;
  1482.  
  1483.     if (entryPtr->textVarName == NULL) {
  1484.     newValue = NULL;
  1485.     } else {
  1486.     newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
  1487.         entryPtr->string, TCL_GLOBAL_ONLY);
  1488.     }
  1489.  
  1490.     if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
  1491.     /*
  1492.      * The value of the variable is different than what we asked for.
  1493.      * This means that a trace on the variable modified it.  In this
  1494.      * case our trace procedure wasn't invoked since the modification
  1495.      * came while a trace was already active on the variable.  So,
  1496.      * update our value to reflect the variable's latest value.
  1497.      */
  1498.  
  1499.     EntrySetValue(entryPtr, newValue);
  1500.     } else {
  1501.     /*
  1502.      * Arrange for redisplay.
  1503.      */
  1504.  
  1505.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1506.     EntryComputeGeometry(entryPtr);
  1507.     EventuallyRedraw(entryPtr);
  1508.     }
  1509. }
  1510.  
  1511. /*
  1512.  *----------------------------------------------------------------------
  1513.  *
  1514.  * EntrySetValue --
  1515.  *
  1516.  *    Replace the contents of a text entry with a given value.  This
  1517.  *    procedure is invoked when updating the entry from the entry's
  1518.  *    associated variable.
  1519.  *
  1520.  * Results:
  1521.  *    None.
  1522.  *
  1523.  * Side effects:
  1524.  *    The string displayed in the entry will change.  The selection,
  1525.  *    insertion point, and view may have to be adjusted to keep them
  1526.  *    within the bounds of the new string.  Note: this procedure does
  1527.  *    *not* update the entry's associated variable, since that could
  1528.  *    result in an infinite loop.
  1529.  *
  1530.  *----------------------------------------------------------------------
  1531.  */
  1532.  
  1533. static void
  1534. EntrySetValue(entryPtr, value)
  1535.     register Entry *entryPtr;        /* Entry whose value is to be
  1536.                      * changed. */
  1537.     char *value;            /* New text to display in entry. */
  1538. {
  1539.     ckfree(entryPtr->string);
  1540.     entryPtr->numChars = strlen(value);
  1541.     entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
  1542.     strcpy(entryPtr->string, value);
  1543.     if (entryPtr->selectFirst != -1) {
  1544.     if (entryPtr->selectFirst >= entryPtr->numChars) {
  1545.         entryPtr->selectFirst = entryPtr->selectLast = -1;
  1546.     } else if (entryPtr->selectLast > entryPtr->numChars) {
  1547.         entryPtr->selectLast = entryPtr->numChars;
  1548.     }
  1549.     }
  1550.     if (entryPtr->leftIndex >= entryPtr->numChars) {
  1551.     entryPtr->leftIndex = entryPtr->numChars-1;
  1552.     }
  1553.     if (entryPtr->insertPos > entryPtr->numChars) {
  1554.     entryPtr->insertPos = entryPtr->numChars;
  1555.     }
  1556.  
  1557.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1558.     EntryComputeGeometry(entryPtr);
  1559.     EventuallyRedraw(entryPtr);
  1560. }
  1561.  
  1562. /*
  1563.  *--------------------------------------------------------------
  1564.  *
  1565.  * EntryEventProc --
  1566.  *
  1567.  *    This procedure is invoked by the Tk dispatcher for various
  1568.  *    events on entryes.
  1569.  *
  1570.  * Results:
  1571.  *    None.
  1572.  *
  1573.  * Side effects:
  1574.  *    When the window gets deleted, internal structures get
  1575.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1576.  *
  1577.  *--------------------------------------------------------------
  1578.  */
  1579.  
  1580. static void
  1581. EntryEventProc(clientData, eventPtr)
  1582.     ClientData clientData;    /* Information about window. */
  1583.     XEvent *eventPtr;        /* Information about event. */
  1584. {
  1585.     Entry *entryPtr = (Entry *) clientData;
  1586.     if (eventPtr->type == Expose) {
  1587.     EventuallyRedraw(entryPtr);
  1588.     entryPtr->flags |= BORDER_NEEDED;
  1589.     } else if (eventPtr->type == DestroyNotify) {
  1590.     if (entryPtr->tkwin != NULL) {
  1591.         entryPtr->tkwin = NULL;
  1592.         Tcl_DeleteCommand(entryPtr->interp,
  1593.             Tcl_GetCommandName(entryPtr->interp, entryPtr->widgetCmd));
  1594.     }
  1595.     if (entryPtr->flags & REDRAW_PENDING) {
  1596.         Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
  1597.     }
  1598.     Tcl_EventuallyFree((ClientData) entryPtr, DestroyEntry);
  1599.     } else if (eventPtr->type == ConfigureNotify) {
  1600.     Tcl_Preserve((ClientData) entryPtr);
  1601.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1602.     EntryComputeGeometry(entryPtr);
  1603.     EventuallyRedraw(entryPtr);
  1604.     Tcl_Release((ClientData) entryPtr);
  1605.     } else if (eventPtr->type == FocusIn) {
  1606.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1607.         EntryFocusProc(entryPtr, 1);
  1608.     }
  1609.     } else if (eventPtr->type == FocusOut) {
  1610.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1611.         EntryFocusProc(entryPtr, 0);
  1612.     }
  1613.     }
  1614. }
  1615.  
  1616. /*
  1617.  *----------------------------------------------------------------------
  1618.  *
  1619.  * EntryCmdDeletedProc --
  1620.  *
  1621.  *    This procedure is invoked when a widget command is deleted.  If
  1622.  *    the widget isn't already in the process of being destroyed,
  1623.  *    this command destroys it.
  1624.  *
  1625.  * Results:
  1626.  *    None.
  1627.  *
  1628.  * Side effects:
  1629.  *    The widget is destroyed.
  1630.  *
  1631.  *----------------------------------------------------------------------
  1632.  */
  1633.  
  1634. static void
  1635. EntryCmdDeletedProc(clientData)
  1636.     ClientData clientData;    /* Pointer to widget record for widget. */
  1637. {
  1638.     Entry *entryPtr = (Entry *) clientData;
  1639.     Tk_Window tkwin = entryPtr->tkwin;
  1640.  
  1641.     /*
  1642.      * This procedure could be invoked either because the window was
  1643.      * destroyed and the command was then deleted (in which case tkwin
  1644.      * is NULL) or because the command was deleted, and then this procedure
  1645.      * destroys the widget.
  1646.      */
  1647.  
  1648.     if (tkwin != NULL) {
  1649.     entryPtr->tkwin = NULL;
  1650.     Tk_DestroyWindow(tkwin);
  1651.     }
  1652. }
  1653.  
  1654. /*
  1655.  *--------------------------------------------------------------
  1656.  *
  1657.  * GetEntryIndex --
  1658.  *
  1659.  *    Parse an index into an entry and return either its value
  1660.  *    or an error.
  1661.  *
  1662.  * Results:
  1663.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1664.  *    filled in with the index (into entryPtr) corresponding to
  1665.  *    string.  The index value is guaranteed to lie between 0 and
  1666.  *    the number of characters in the string, inclusive.  If an
  1667.  *    error occurs then an error message is left in interp->result.
  1668.  *
  1669.  * Side effects:
  1670.  *    None.
  1671.  *
  1672.  *--------------------------------------------------------------
  1673.  */
  1674.  
  1675. static int
  1676. GetEntryIndex(interp, entryPtr, string, indexPtr)
  1677.     Tcl_Interp *interp;        /* For error messages. */
  1678.     Entry *entryPtr;        /* Entry for which the index is being
  1679.                  * specified. */
  1680.     char *string;        /* Specifies character in entryPtr. */
  1681.     int *indexPtr;        /* Where to store converted index. */
  1682. {
  1683.     size_t length;
  1684.  
  1685.     length = strlen(string);
  1686.  
  1687.     if (string[0] == 'a') {
  1688.     if (strncmp(string, "anchor", length) == 0) {
  1689.         *indexPtr = entryPtr->selectAnchor;
  1690.     } else {
  1691.         badIndex:
  1692.  
  1693.         /*
  1694.          * Some of the paths here leave messages in interp->result,
  1695.          * so we have to clear it out before storing our own message.
  1696.          */
  1697.  
  1698.         Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  1699.         Tcl_AppendResult(interp, "bad entry index \"", string,
  1700.             "\"", (char *) NULL);
  1701.         return TCL_ERROR;
  1702.     }
  1703.     } else if (string[0] == 'e') {
  1704.     if (strncmp(string, "end", length) == 0) {
  1705.         *indexPtr = entryPtr->numChars;
  1706.     } else {
  1707.         goto badIndex;
  1708.     }
  1709.     } else if (string[0] == 'i') {
  1710.     if (strncmp(string, "insert", length) == 0) {
  1711.         *indexPtr = entryPtr->insertPos;
  1712.     } else {
  1713.         goto badIndex;
  1714.     }
  1715.     } else if (string[0] == 's') {
  1716.     if (entryPtr->selectFirst == -1) {
  1717.         interp->result = "selection isn't in entry";
  1718.         return TCL_ERROR;
  1719.     }
  1720.     if (length < 5) {
  1721.         goto badIndex;
  1722.     }
  1723.     if (strncmp(string, "sel.first", length) == 0) {
  1724.         *indexPtr = entryPtr->selectFirst;
  1725.     } else if (strncmp(string, "sel.last", length) == 0) {
  1726.         *indexPtr = entryPtr->selectLast;
  1727.     } else {
  1728.         goto badIndex;
  1729.     }
  1730.     } else if (string[0] == '@') {
  1731.     int x, dummy, roundUp;
  1732.  
  1733.     if (Tcl_GetInt(interp, string+1, &x) != TCL_OK) {
  1734.         goto badIndex;
  1735.     }
  1736.     if (x < entryPtr->inset) {
  1737.         x = entryPtr->inset;
  1738.     }
  1739.     roundUp = 0;
  1740.     if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
  1741.         x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
  1742.         roundUp = 1;
  1743.     }
  1744.     if (entryPtr->numChars == 0) {
  1745.         *indexPtr = 0;
  1746.     } else {
  1747.         *indexPtr = TkMeasureChars(entryPtr->fontPtr,
  1748.             (entryPtr->displayString == NULL) ? entryPtr->string
  1749.             : entryPtr->displayString,
  1750.             entryPtr->numChars, entryPtr->tabOrigin, x,
  1751.             entryPtr->tabOrigin, TK_NEWLINES_NOT_SPECIAL, &dummy);
  1752.     }
  1753.  
  1754.     /*
  1755.      * Special trick:  if the x-position was off-screen to the right,
  1756.      * round the index up to refer to the character just after the
  1757.      * last visible one on the screen.  This is needed to enable the
  1758.      * last character to be selected, for example.
  1759.      */
  1760.  
  1761.     if (roundUp && (*indexPtr < entryPtr->numChars)) {
  1762.         *indexPtr += 1;
  1763.     }
  1764.     } else {
  1765.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1766.         goto badIndex;
  1767.     }
  1768.     if (*indexPtr < 0){
  1769.         *indexPtr = 0;
  1770.     } else if (*indexPtr > entryPtr->numChars) {
  1771.         *indexPtr = entryPtr->numChars;
  1772.     }
  1773.     }
  1774.     return TCL_OK;
  1775. }
  1776.  
  1777. /*
  1778.  *----------------------------------------------------------------------
  1779.  *
  1780.  * EntryScanTo --
  1781.  *
  1782.  *    Given a y-coordinate (presumably of the curent mouse location)
  1783.  *    drag the view in the window to implement the scan operation.
  1784.  *
  1785.  * Results:
  1786.  *    None.
  1787.  *
  1788.  * Side effects:
  1789.  *    The view in the window may change.
  1790.  *
  1791.  *----------------------------------------------------------------------
  1792.  */
  1793.  
  1794. static void
  1795. EntryScanTo(entryPtr, x)
  1796.     register Entry *entryPtr;        /* Information about widget. */
  1797.     int x;                /* X-coordinate to use for scan
  1798.                      * operation. */
  1799. {
  1800.     int newLeftIndex;
  1801.  
  1802.     /*
  1803.      * Compute new leftIndex for entry by amplifying the difference
  1804.      * between the current position and the place where the scan
  1805.      * started (the "mark" position).  If we run off the left or right
  1806.      * side of the entry, then reset the mark point so that the current
  1807.      * position continues to correspond to the edge of the window.
  1808.      * This means that the picture will start dragging as soon as the
  1809.      * mouse reverses direction (without this reset, might have to slide
  1810.      * mouse a long ways back before the picture starts moving again).
  1811.      */
  1812.  
  1813.     newLeftIndex = entryPtr->scanMarkIndex
  1814.         - (10*(x - entryPtr->scanMarkX))/entryPtr->avgWidth;
  1815.     if (newLeftIndex >= entryPtr->numChars) {
  1816.     newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars-1;
  1817.     entryPtr->scanMarkX = x;
  1818.     }
  1819.     if (newLeftIndex < 0) {
  1820.     newLeftIndex = entryPtr->scanMarkIndex = 0;
  1821.     entryPtr->scanMarkX = x;
  1822.     } 
  1823.     if (newLeftIndex != entryPtr->leftIndex) {
  1824.     entryPtr->leftIndex = newLeftIndex;
  1825.     entryPtr->flags |= UPDATE_SCROLLBAR;
  1826.     EntryComputeGeometry(entryPtr);
  1827.     EventuallyRedraw(entryPtr);
  1828.     }
  1829. }
  1830.  
  1831. /*
  1832.  *----------------------------------------------------------------------
  1833.  *
  1834.  * EntrySelectTo --
  1835.  *
  1836.  *    Modify the selection by moving its un-anchored end.  This could
  1837.  *    make the selection either larger or smaller.
  1838.  *
  1839.  * Results:
  1840.  *    None.
  1841.  *
  1842.  * Side effects:
  1843.  *    The selection changes.
  1844.  *
  1845.  *----------------------------------------------------------------------
  1846.  */
  1847.  
  1848. static void
  1849. EntrySelectTo(entryPtr, index)
  1850.     register Entry *entryPtr;        /* Information about widget. */
  1851.     int index;                /* Index of element that is to
  1852.                      * become the "other" end of the
  1853.                      * selection. */
  1854. {
  1855.     int newFirst, newLast;
  1856.  
  1857.     /*
  1858.      * Grab the selection if we don't own it already.
  1859.      */
  1860.  
  1861.     if ((entryPtr->selectFirst == -1) && (entryPtr->exportSelection)) {
  1862.     Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
  1863.         (ClientData) entryPtr);
  1864.     }
  1865.  
  1866.     /*
  1867.      * Pick new starting and ending points for the selection.
  1868.      */
  1869.  
  1870.     if (entryPtr->selectAnchor > entryPtr->numChars) {
  1871.     entryPtr->selectAnchor = entryPtr->numChars;
  1872.     }
  1873.     if (entryPtr->selectAnchor <= index) {
  1874.     newFirst = entryPtr->selectAnchor;
  1875.     newLast = index;
  1876.     } else {
  1877.     newFirst = index;
  1878.     newLast = entryPtr->selectAnchor;
  1879.     if (newLast < 0) {
  1880.         newFirst = newLast = -1;
  1881.     }
  1882.     }
  1883.     if ((entryPtr->selectFirst == newFirst)
  1884.         && (entryPtr->selectLast == newLast)) {
  1885.     return;
  1886.     }
  1887.     entryPtr->selectFirst = newFirst;
  1888.     entryPtr->selectLast = newLast;
  1889.     EventuallyRedraw(entryPtr);
  1890. }
  1891.  
  1892. /*
  1893.  *----------------------------------------------------------------------
  1894.  *
  1895.  * EntryFetchSelection --
  1896.  *
  1897.  *    This procedure is called back by Tk when the selection is
  1898.  *    requested by someone.  It returns part or all of the selection
  1899.  *    in a buffer provided by the caller.
  1900.  *
  1901.  * Results:
  1902.  *    The return value is the number of non-NULL bytes stored
  1903.  *    at buffer.  Buffer is filled (or partially filled) with a
  1904.  *    NULL-terminated string containing part or all of the selection,
  1905.  *    as given by offset and maxBytes.
  1906.  *
  1907.  * Side effects:
  1908.  *    None.
  1909.  *
  1910.  *----------------------------------------------------------------------
  1911.  */
  1912.  
  1913. static int
  1914. EntryFetchSelection(clientData, offset, buffer, maxBytes)
  1915.     ClientData clientData;        /* Information about entry widget. */
  1916.     int offset;                /* Offset within selection of first
  1917.                      * character to be returned. */
  1918.     char *buffer;            /* Location in which to place
  1919.                      * selection. */
  1920.     int maxBytes;            /* Maximum number of bytes to place
  1921.                      * at buffer, not including terminating
  1922.                      * NULL character. */
  1923. {
  1924.     Entry *entryPtr = (Entry *) clientData;
  1925.     int count;
  1926.     char *displayString;
  1927.  
  1928.     if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
  1929.     return -1;
  1930.     }
  1931.     count = entryPtr->selectLast - entryPtr->selectFirst - offset;
  1932.     if (count > maxBytes) {
  1933.     count = maxBytes;
  1934.     }
  1935.     if (count <= 0) {
  1936.     return 0;
  1937.     }
  1938.     if (entryPtr->displayString == NULL) {
  1939.     displayString = entryPtr->string;
  1940.     } else {
  1941.     displayString = entryPtr->displayString;
  1942.     }
  1943.     strncpy(buffer, displayString + entryPtr->selectFirst + offset,
  1944.         (size_t) count);
  1945.     buffer[count] = '\0';
  1946.     return count;
  1947. }
  1948.  
  1949. /*
  1950.  *----------------------------------------------------------------------
  1951.  *
  1952.  * EntryLostSelection --
  1953.  *
  1954.  *    This procedure is called back by Tk when the selection is
  1955.  *    grabbed away from an entry widget.
  1956.  *
  1957.  * Results:
  1958.  *    None.
  1959.  *
  1960.  * Side effects:
  1961.  *    The existing selection is unhighlighted, and the window is
  1962.  *    marked as not containing a selection.
  1963.  *
  1964.  *----------------------------------------------------------------------
  1965.  */
  1966.  
  1967. static void
  1968. EntryLostSelection(clientData)
  1969.     ClientData clientData;        /* Information about entry widget. */
  1970. {
  1971.     Entry *entryPtr = (Entry *) clientData;
  1972.  
  1973.     if ((entryPtr->selectFirst != -1) && entryPtr->exportSelection) {
  1974.     entryPtr->selectFirst = -1;
  1975.     entryPtr->selectLast = -1;
  1976.     EventuallyRedraw(entryPtr);
  1977.     }
  1978. }
  1979.  
  1980. /*
  1981.  *----------------------------------------------------------------------
  1982.  *
  1983.  * EventuallyRedraw --
  1984.  *
  1985.  *    Ensure that an entry is eventually redrawn on the display.
  1986.  *
  1987.  * Results:
  1988.  *    None.
  1989.  *
  1990.  * Side effects:
  1991.  *    Information gets redisplayed.  Right now we don't do selective
  1992.  *    redisplays:  the whole window will be redrawn.  This doesn't
  1993.  *    seem to hurt performance noticeably, but if it does then this
  1994.  *    could be changed.
  1995.  *
  1996.  *----------------------------------------------------------------------
  1997.  */
  1998.  
  1999. static void
  2000. EventuallyRedraw(entryPtr)
  2001.     register Entry *entryPtr;        /* Information about widget. */
  2002. {
  2003.     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
  2004.     return;
  2005.     }
  2006.  
  2007.     /*
  2008.      * Right now we don't do selective redisplays:  the whole window
  2009.      * will be redrawn.  This doesn't seem to hurt performance noticeably,
  2010.      * but if it does then this could be changed.
  2011.      */
  2012.  
  2013.     if (!(entryPtr->flags & REDRAW_PENDING)) {
  2014.     entryPtr->flags |= REDRAW_PENDING;
  2015.     Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
  2016.     }
  2017. }
  2018.  
  2019. /*
  2020.  *----------------------------------------------------------------------
  2021.  *
  2022.  * EntryVisibleRange --
  2023.  *
  2024.  *    Return information about the range of the entry that is
  2025.  *    currently visible.
  2026.  *
  2027.  * Results:
  2028.  *    *firstPtr and *lastPtr are modified to hold fractions between
  2029.  *    0 and 1 identifying the range of characters visible in the
  2030.  *    entry.
  2031.  *
  2032.  * Side effects:
  2033.  *    None.
  2034.  *
  2035.  *----------------------------------------------------------------------
  2036.  */
  2037.  
  2038. static void
  2039. EntryVisibleRange(entryPtr, firstPtr, lastPtr)
  2040.     Entry *entryPtr;            /* Information about widget. */
  2041.     double *firstPtr;            /* Return position of first visible
  2042.                      * character in widget. */
  2043.     double *lastPtr;            /* Return position of char just after
  2044.                      * last visible one. */
  2045. {
  2046.     char *displayString;
  2047.     int charsInWindow, endX;
  2048.  
  2049.     if (entryPtr->displayString == NULL) {
  2050.     displayString = entryPtr->string;
  2051.     } else {
  2052.     displayString = entryPtr->displayString;
  2053.     }
  2054.     if (entryPtr->numChars == 0) {
  2055.     *firstPtr = 0.0;
  2056.     *lastPtr = 1.0;
  2057.     } else {
  2058.     charsInWindow = TkMeasureChars(entryPtr->fontPtr,
  2059.         displayString + entryPtr->leftIndex,
  2060.         entryPtr->numChars - entryPtr->leftIndex, entryPtr->inset,
  2061.         Tk_Width(entryPtr->tkwin) - entryPtr->inset, entryPtr->inset,
  2062.         TK_AT_LEAST_ONE|TK_NEWLINES_NOT_SPECIAL, &endX);
  2063.     *firstPtr = ((double) entryPtr->leftIndex)/entryPtr->numChars;
  2064.     *lastPtr = ((double) (entryPtr->leftIndex + charsInWindow))
  2065.         /entryPtr->numChars;
  2066.     }
  2067. }
  2068.  
  2069. /*
  2070.  *----------------------------------------------------------------------
  2071.  *
  2072.  * EntryUpdateScrollbar --
  2073.  *
  2074.  *    This procedure is invoked whenever information has changed in
  2075.  *    an entry in a way that would invalidate a scrollbar display.
  2076.  *    If there is an associated scrollbar, then this procedure updates
  2077.  *    it by invoking a Tcl command.
  2078.  *
  2079.  * Results:
  2080.  *    None.
  2081.  *
  2082.  * Side effects:
  2083.  *    A Tcl command is invoked, and an additional command may be
  2084.  *    invoked to process errors in the command.
  2085.  *
  2086.  *----------------------------------------------------------------------
  2087.  */
  2088.  
  2089. static void
  2090. EntryUpdateScrollbar(entryPtr)
  2091.     Entry *entryPtr;            /* Information about widget. */
  2092. {
  2093.     char args[100];
  2094.     int code;
  2095.     double first, last;
  2096.     Tcl_Interp *interp;
  2097.  
  2098.     if (entryPtr->scrollCmd == NULL) {
  2099.     return;
  2100.     }
  2101.  
  2102.     interp = entryPtr->interp;
  2103.     Tcl_Preserve((ClientData) interp);
  2104.     EntryVisibleRange(entryPtr, &first, &last);
  2105.     sprintf(args, " %g %g", first, last);
  2106.     code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
  2107.     if (code != TCL_OK) {
  2108.     Tcl_AddErrorInfo(interp,
  2109.         "\n    (horizontal scrolling command executed by entry)");
  2110.     Tcl_BackgroundError(interp);
  2111.     }
  2112.     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
  2113.     Tcl_Release((ClientData) interp);
  2114. }
  2115.  
  2116. /*
  2117.  *----------------------------------------------------------------------
  2118.  *
  2119.  * EntryBlinkProc --
  2120.  *
  2121.  *    This procedure is called as a timer handler to blink the
  2122.  *    insertion cursor off and on.
  2123.  *
  2124.  * Results:
  2125.  *    None.
  2126.  *
  2127.  * Side effects:
  2128.  *    The cursor gets turned on or off, redisplay gets invoked,
  2129.  *    and this procedure reschedules itself.
  2130.  *
  2131.  *----------------------------------------------------------------------
  2132.  */
  2133.  
  2134. static void
  2135. EntryBlinkProc(clientData)
  2136.     ClientData clientData;    /* Pointer to record describing entry. */
  2137. {
  2138.     register Entry *entryPtr = (Entry *) clientData;
  2139.  
  2140.     if (!(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
  2141.     return;
  2142.     }
  2143.     if (entryPtr->flags & CURSOR_ON) {
  2144.     entryPtr->flags &= ~CURSOR_ON;
  2145.     entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  2146.         entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
  2147.     } else {
  2148.     entryPtr->flags |= CURSOR_ON;
  2149.     entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  2150.         entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
  2151.     }
  2152.     EventuallyRedraw(entryPtr);
  2153. }
  2154.  
  2155. /*
  2156.  *----------------------------------------------------------------------
  2157.  *
  2158.  * EntryFocusProc --
  2159.  *
  2160.  *    This procedure is called whenever the entry gets or loses the
  2161.  *    input focus.  It's also called whenever the window is reconfigured
  2162.  *    while it has the focus.
  2163.  *
  2164.  * Results:
  2165.  *    None.
  2166.  *
  2167.  * Side effects:
  2168.  *    The cursor gets turned on or off.
  2169.  *
  2170.  *----------------------------------------------------------------------
  2171.  */
  2172.  
  2173. static void
  2174. EntryFocusProc(entryPtr, gotFocus)
  2175.     register Entry *entryPtr;    /* Entry that got or lost focus. */
  2176.     int gotFocus;        /* 1 means window is getting focus, 0 means
  2177.                  * it's losing it. */
  2178. {
  2179.     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
  2180.     if (gotFocus) {
  2181.     entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
  2182.     if (entryPtr->insertOffTime != 0) {
  2183.         entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  2184.             entryPtr->insertOnTime, EntryBlinkProc,
  2185.             (ClientData) entryPtr);
  2186.     }
  2187.     } else {
  2188.     entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
  2189.     entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
  2190.     }
  2191.     EventuallyRedraw(entryPtr);
  2192. }
  2193.  
  2194. /*
  2195.  *--------------------------------------------------------------
  2196.  *
  2197.  * EntryTextVarProc --
  2198.  *
  2199.  *    This procedure is invoked when someone changes the variable
  2200.  *    whose contents are to be displayed in an entry.
  2201.  *
  2202.  * Results:
  2203.  *    NULL is always returned.
  2204.  *
  2205.  * Side effects:
  2206.  *    The text displayed in the entry will change to match the
  2207.  *    variable.
  2208.  *
  2209.  *--------------------------------------------------------------
  2210.  */
  2211.  
  2212.     /* ARGSUSED */
  2213. static char *
  2214. EntryTextVarProc(clientData, interp, name1, name2, flags)
  2215.     ClientData clientData;    /* Information about button. */
  2216.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  2217.     char *name1;        /* Not used. */
  2218.     char *name2;        /* Not used. */
  2219.     int flags;            /* Information about what happened. */
  2220. {
  2221.     register Entry *entryPtr = (Entry *) clientData;
  2222.     char *value;
  2223.  
  2224.     /*
  2225.      * If the variable is unset, then immediately recreate it unless
  2226.      * the whole interpreter is going away.
  2227.      */
  2228.  
  2229.     if (flags & TCL_TRACE_UNSETS) {
  2230.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  2231.         Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
  2232.             TCL_GLOBAL_ONLY);
  2233.         Tcl_TraceVar(interp, entryPtr->textVarName,
  2234.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  2235.             EntryTextVarProc, clientData);
  2236.     }
  2237.     return (char *) NULL;
  2238.     }
  2239.  
  2240.     /*
  2241.      * Update the entry's text with the value of the variable, unless
  2242.      * the entry already has that value (this happens when the variable
  2243.      * changes value because we changed it because someone typed in
  2244.      * the entry).
  2245.      */
  2246.  
  2247.     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
  2248.     if (value == NULL) {
  2249.     value = "";
  2250.     }
  2251.     if (strcmp(value, entryPtr->string) != 0) {
  2252.     EntrySetValue(entryPtr, value);
  2253.     }
  2254.     return (char *) NULL;
  2255. }
  2256.