home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / tcl / tkstep0.3b3 / tkstep0 / tkstep / tkEntry.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-08  |  69.8 KB  |  2,286 lines

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