home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkEntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  69.3 KB  |  2,307 lines  |  [TEXT/CWIE]

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