/[dtapublic]/projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkentry.c
ViewVC logotype

Diff of /projs/ets/trunk/src/c_tk_base_7_5_w_mods/tkentry.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

projs/trunk/shared_source/tk_base/tkentry.c revision 42 by dashley, Fri Oct 14 01:50:00 2016 UTC projs/dtats/trunk/shared_source/c_tk_base_7_5_w_mods/tkentry.c revision 98 by dashley, Sun Dec 18 00:57:31 2016 UTC
# Line 1  Line 1 
 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tk_base/tkentry.c,v 1.1.1.1 2001/06/13 04:59:57 dtashley Exp $ */  
   
 /*  
  * tkEntry.c --  
  *  
  *      This module implements entry widgets for the Tk  
  *      toolkit.  An entry displays a string and allows  
  *      the string to be edited.  
  *  
  * Copyright (c) 1990-1994 The Regents of the University of California.  
  * Copyright (c) 1994-1997 Sun Microsystems, Inc.  
  *  
  * See the file "license.terms" for information on usage and redistribution  
  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.  
  *  
  * RCS: @(#) $Id: tkentry.c,v 1.1.1.1 2001/06/13 04:59:57 dtashley Exp $  
  */  
   
 #include "tkInt.h"  
 #include "default.h"  
   
 /*  
  * A data structure of the following type is kept for each entry  
  * widget managed by this file:  
  */  
   
 typedef struct {  
     Tk_Window tkwin;            /* Window that embodies the entry. NULL  
                                  * means that the window has been destroyed  
                                  * but the data structures haven't yet been  
                                  * cleaned up.*/  
     Display *display;           /* Display containing widget.  Used, among  
                                  * other things, so that resources can be  
                                  * freed even after tkwin has gone away. */  
     Tcl_Interp *interp;         /* Interpreter associated with entry. */  
     Tcl_Command widgetCmd;      /* Token for entry's widget command. */  
     Tk_OptionTable optionTable; /* Table that defines configuration options  
                                  * available for this widget. */  
   
   
     /*  
      * Fields that are set by widget commands other than "configure".  
      */  
       
     char *string;               /* Pointer to storage for string;  
                                  * NULL-terminated;  malloc-ed. */  
     int insertPos;              /* Character index before which next typed  
                                  * character will be inserted. */  
   
     /*  
      * Information about what's selected, if any.  
      */  
   
     int selectFirst;            /* Character index of first selected  
                                  * character (-1 means nothing selected. */  
     int selectLast;             /* Character index just after last selected  
                                  * character (-1 means nothing selected. */  
     int selectAnchor;           /* Fixed end of selection (i.e. "select to"  
                                  * operation will use this as one end of the  
                                  * selection). */  
   
     /*  
      * Information for scanning:  
      */  
   
     int scanMarkX;              /* X-position at which scan started (e.g.  
                                  * button was pressed here). */  
     int scanMarkIndex;          /* Character index of character that was at  
                                  * left of window when scan started. */  
   
     /*  
      * Configuration settings that are updated by Tk_ConfigureWidget.  
      */  
   
     Tk_3DBorder normalBorder;   /* Used for drawing border around whole  
                                  * window, plus used for background. */  
     int borderWidth;            /* Width of 3-D border around window. */  
     Tk_Cursor cursor;           /* Current cursor for window, or None. */  
     int exportSelection;        /* Non-zero means tie internal entry selection  
                                  * to X selection. */  
     Tk_Font tkfont;             /* Information about text font, or NULL. */  
     XColor *fgColorPtr;         /* Text color in normal mode. */  
     XColor *highlightBgColorPtr;/* Color for drawing traversal highlight  
                                  * area when highlight is off. */  
     XColor *highlightColorPtr;  /* Color for drawing traversal highlight. */  
     int highlightWidth;         /* Width in pixels of highlight to draw  
                                  * around widget when it has the focus.  
                                  * <= 0 means don't draw a highlight. */  
     Tk_3DBorder insertBorder;   /* Used to draw vertical bar for insertion  
                                  * cursor. */  
     int insertBorderWidth;      /* Width of 3-D border around insert cursor. */  
     int insertOffTime;          /* Number of milliseconds cursor should spend  
                                  * in "off" state for each blink. */  
     int insertOnTime;           /* Number of milliseconds cursor should spend  
                                  * in "on" state for each blink. */  
     int insertWidth;            /* Total width of insert cursor. */  
     Tk_Justify justify;         /* Justification to use for text within  
                                  * window. */  
     int relief;                 /* 3-D effect: TK_RELIEF_RAISED, etc. */  
     Tk_3DBorder selBorder;      /* Border and background for selected  
                                  * characters. */  
     int selBorderWidth;         /* Width of border around selection. */  
     XColor *selFgColorPtr;      /* Foreground color for selected text. */  
     char *showChar;             /* Value of -show option.  If non-NULL, first  
                                  * character is used for displaying all  
                                  * characters in entry.  Malloc'ed. */  
     int state;                  /* Normal or disabled.  Entry is read-only  
                                  * when disabled. */  
     char *textVarName;          /* Name of variable (malloc'ed) or NULL.  
                                  * If non-NULL, entry's string tracks the  
                                  * contents of this variable and vice versa. */  
     char *takeFocus;            /* Value of -takefocus option;  not used in  
                                  * the C code, but used by keyboard traversal  
                                  * scripts.  Malloc'ed, but may be NULL. */  
     int prefWidth;              /* Desired width of window, measured in  
                                  * average characters. */  
     char *scrollCmd;            /* Command prefix for communicating with  
                                  * scrollbar(s).  Malloc'ed.  NULL means  
                                  * no command to issue. */  
   
     /*  
      * Fields whose values are derived from the current values of the  
      * configuration settings above.  
      */  
   
     int numBytes;               /* Length of string in bytes. */  
     int numChars;               /* Length of string in characters.  Both  
                                  * string and displayString have the same  
                                  * character length, but may have different  
                                  * byte lengths due to being made from  
                                  * different UTF-8 characters. */  
     char *displayString;        /* String to use when displaying.  This may  
                                  * be a pointer to string, or a pointer to  
                                  * malloced memory with the same character  
                                  * length as string but whose characters  
                                  * are all equal to showChar. */  
     int numDisplayBytes;        /* Length of displayString in bytes. */  
     int inset;                  /* Number of pixels on the left and right  
                                  * sides that are taken up by XPAD, borderWidth  
                                  * (if any), and highlightWidth (if any). */  
     Tk_TextLayout textLayout;   /* Cached text layout information. */  
     int layoutX, layoutY;       /* Origin for layout. */  
     int leftX;                  /* X position at which character at leftIndex  
                                  * is drawn (varies depending on justify). */  
     int leftIndex;              /* Character index of left-most character  
                                  * visible in window. */  
     Tcl_TimerToken insertBlinkHandler;  
                                 /* Timer handler used to blink cursor on and  
                                  * off. */  
     GC textGC;                  /* For drawing normal text. */  
     GC selTextGC;               /* For drawing selected text. */  
     GC highlightGC;             /* For drawing traversal highlight. */  
     int avgWidth;               /* Width of average character. */  
     int flags;                  /* Miscellaneous flags;  see below for  
                                  * definitions. */  
     Tk_TSOffset tsoffset;  
   
     char *validateCmd;          /* Command prefix to use when invoking  
                                  * validate command.  NULL means don't  
                                  * invoke commands.  Malloc'ed. */  
     int validate;               /* Non-zero means try to validate */  
     char *invalidCmd;           /* Command called when a validation returns 0  
                                  * (successfully fails), defaults to {}. */  
 } Entry;  
   
 /*  
  * Assigned bits of "flags" fields of Entry structures, and what those  
  * bits mean:  
  *  
  * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler has  
  *                              already been queued to redisplay the entry.  
  * BORDER_NEEDED:               Non-zero means 3-D border must be redrawn  
  *                              around window during redisplay.  Normally  
  *                              only text portion needs to be redrawn.  
  * CURSOR_ON:                   Non-zero means insert cursor is displayed at  
  *                              present.  0 means it isn't displayed.  
  * GOT_FOCUS:                   Non-zero means this window has the input  
  *                              focus.  
  * UPDATE_SCROLLBAR:            Non-zero means scrollbar should be updated  
  *                              during next redisplay operation.  
  * GOT_SELECTION:               Non-zero means we've claimed the selection.  
  * ENTRY_DELETED:               This entry has been effectively destroyed.  
  * VALIDATING:                  Non-zero means we are in a validateCmd  
  * VALIDATE_VAR:                Non-zero means we are attempting to validate  
  *                              the entry's textvariable with validateCmd  
  * VALIDATE_ABORT:              Non-zero if validatecommand signals an abort  
  *                              for current procedure and make no changes  
  */  
   
 #define REDRAW_PENDING          1  
 #define BORDER_NEEDED           2  
 #define CURSOR_ON               4  
 #define GOT_FOCUS               8  
 #define UPDATE_SCROLLBAR        0x10  
 #define GOT_SELECTION           0x20  
 #define ENTRY_DELETED           0x40  
 #define VALIDATING              0x80  
 #define VALIDATE_VAR            0x100  
 #define VALIDATE_ABORT          0x200  
   
 /*  
  * The following macro defines how many extra pixels to leave on each  
  * side of the text in the entry.  
  */  
   
 #define XPAD 1  
 #define YPAD 1  
   
 /*  
  * The following enum is used to define a type for the -state option  
  * of the Entry widget.  These values are used as indices into the  
  * string table below.  
  */  
   
 enum state {  
     STATE_DISABLED, STATE_NORMAL  
 };  
   
 static char *stateStrings[] = {  
     "disabled", "normal", (char *) NULL  
 };  
   
 /*  
  * Definitions for -validate option values:  
  */  
   
 static char *validateStrings[] = {  
     "all", "key", "focus", "focusin", "focusout", "none", (char *) NULL  
 };  
 enum validateType {  
     VALIDATE_ALL, VALIDATE_KEY, VALIDATE_FOCUS,  
     VALIDATE_FOCUSIN, VALIDATE_FOCUSOUT, VALIDATE_NONE,  
     /*  
      * These extra enums are for use with EntryValidateChange  
      */  
     VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT  
 };  
 #define DEF_ENTRY_VALIDATE      "none"  
 #define DEF_ENTRY_INVALIDCMD    ""  
   
 /*  
  * Information used for argv parsing.  
  */  
   
 static Tk_OptionSpec optionSpecs[] = {  
     {TK_OPTION_BORDER, "-background", "background", "Background",  
         DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder),  
         0, (ClientData) DEF_ENTRY_BG_MONO, 0},  
     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},  
     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},  
     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",  
         DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth),  
         0, 0, 0},  
     {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",  
         DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",  
         "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1,  
         Tk_Offset(Entry, exportSelection), 0, 0, 0},  
     {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},  
     {TK_OPTION_FONT, "-font", "font", "Font",  
         DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0},  
     {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",  
         DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0,  
         0, 0},  
     {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",  
         "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,  
         -1, Tk_Offset(Entry, highlightBgColorPtr),  
         0, 0, 0},  
     {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",  
         DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr),  
         0, 0, 0},  
     {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",  
         "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1,  
         Tk_Offset(Entry, highlightWidth), 0, 0, 0},  
     {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground",  
         DEF_ENTRY_INSERT_BG,  
         -1, Tk_Offset(Entry, insertBorder),  
         0, 0, 0},  
     {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth",  
         "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1,  
         Tk_Offset(Entry, insertBorderWidth), 0,  
         (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0},  
     {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime",  
         DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime),  
         0, 0, 0},  
     {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime",  
         DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime),  
         0, 0, 0},  
     {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",  
         DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth),  
         0, 0, 0},  
     {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand",  
         DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_SYNONYM, "-invcmd", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0},  
     {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",  
         DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0},  
     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",  
         DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief),  
         0, 0, 0},  
     {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",  
         DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder),  
         0, (ClientData) DEF_ENTRY_SELECT_MONO, 0},  
     {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth",  
         "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1,  
         Tk_Offset(Entry, selBorderWidth),  
         0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0},  
     {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",  
         DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr),  
         0, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0},  
     {TK_OPTION_STRING, "-show", "show", "Show",  
         DEF_ENTRY_SHOW, -1, Tk_Offset(Entry, showChar),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_STRING_TABLE, "-state", "state", "State",  
         DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state),  
         0, (ClientData) stateStrings, 0},  
     {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",  
         DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",  
         DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate",  
        DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate),  
        0, (ClientData) validateStrings, 0},  
     {TK_OPTION_STRING, "-validatecommand", "validateCommand", "ValidateCommand",  
        (char *) NULL, -1, Tk_Offset(Entry, validateCmd),  
        TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_SYNONYM, "-vcmd", (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, (ClientData) "-validatecommand", 0},  
     {TK_OPTION_INT, "-width", "width", "Width",  
         DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0},  
     {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",  
         DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd),  
         TK_OPTION_NULL_OK, 0, 0},  
     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,  
         (char *) NULL, 0, -1, 0, 0, 0}  
 };  
   
 /*  
  * Flags for GetEntryIndex procedure:  
  */  
   
 #define ZERO_OK                 1  
 #define LAST_PLUS_ONE_OK        2  
   
 /*  
  * The following tables define the entry widget commands (and sub-  
  * commands) and map the indexes into the string tables into  
  * enumerated types used to dispatch the entry widget command.  
  */  
   
 static char *commandNames[] = {  
     "bbox", "cget", "configure", "delete", "get", "icursor", "index",  
     "insert", "scan", "selection", "validate", "xview", (char *) NULL  
 };  
   
 enum command {  
     COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE,  
     COMMAND_GET, COMMAND_ICURSOR, COMMAND_INDEX, COMMAND_INSERT,  
     COMMAND_SCAN, COMMAND_SELECTION, COMMAND_VALIDATE, COMMAND_XVIEW  
 };  
   
 static char *selCommandNames[] = {  
     "adjust", "clear", "from", "present", "range", "to", (char *) NULL  
 };  
   
 enum selcommand {  
     SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM,  
     SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO  
 };  
   
 /*  
  * Forward declarations for procedures defined later in this file:  
  */  
   
 static int              ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,  
                             Entry *entryPtr, int objc,  
                             Tcl_Obj *CONST objv[], int flags));  
 static void             DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,  
                             int count));  
 static void             DestroyEntry _ANSI_ARGS_((char *memPtr));  
 static void             DisplayEntry _ANSI_ARGS_((ClientData clientData));  
 static void             EntryBlinkProc _ANSI_ARGS_((ClientData clientData));  
 static void             EntryCmdDeletedProc _ANSI_ARGS_((  
                             ClientData clientData));  
 static void             EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));  
 static void             EntryEventProc _ANSI_ARGS_((ClientData clientData,  
                             XEvent *eventPtr));  
 static void             EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,  
                             int gotFocus));  
 static int              EntryFetchSelection _ANSI_ARGS_((ClientData clientData,  
                             int offset, char *buffer, int maxBytes));  
 static void             EntryLostSelection _ANSI_ARGS_((  
                             ClientData clientData));  
 static void             EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));  
 static void             EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));  
 static void             EntrySetValue _ANSI_ARGS_((Entry *entryPtr,  
                             char *value));  
 static void             EntrySelectTo _ANSI_ARGS_((  
                             Entry *entryPtr, int index));  
 static char *           EntryTextVarProc _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, char *name1, char *name2,  
                             int flags));  
 static void             EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));  
 static int              EntryValidate _ANSI_ARGS_((Entry *entryPtr,  
                             char *cmd));  
 static int              EntryValidateChange _ANSI_ARGS_((Entry *entryPtr,  
                             char *change, char *new, int index, int type));  
 static void             ExpandPercents _ANSI_ARGS_((Entry *entryPtr,  
                             char *before, char *change, char *new,  
                             int index, int type, Tcl_DString *dsPtr));  
 static void             EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));  
 static void             EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,  
                             double *firstPtr, double *lastPtr));  
 static int              EntryWidgetObjCmd _ANSI_ARGS_((ClientData clientData,  
                             Tcl_Interp *interp, int objc,  
                             Tcl_Obj *CONST objv[]));  
 static void             EntryWorldChanged _ANSI_ARGS_((  
                             ClientData instanceData));  
 static int              GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,  
                             Entry *entryPtr, char *string, int *indexPtr));  
 static void             InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,  
                             char *string));  
   
 /*  
  * The structure below defines entry class behavior by means of procedures  
  * that can be invoked from generic window code.  
  */  
   
 static TkClassProcs entryClass = {  
     NULL,                       /* createProc. */  
     EntryWorldChanged,          /* geometryProc. */  
     NULL                        /* modalProc. */  
 };  
   
   
 /*  
  *--------------------------------------------------------------  
  *  
  * Tk_EntryObjCmd --  
  *  
  *      This procedure is invoked to process the "entry" Tcl  
  *      command.  See the user documentation for details on what  
  *      it does.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      See the user documentation.  
  *  
  *--------------------------------------------------------------  
  */  
   
 int  
 Tk_EntryObjCmd(clientData, interp, objc, objv)  
     ClientData clientData;      /* Either NULL or pointer to option table. */  
     Tcl_Interp *interp;         /* Current interpreter. */  
     int objc;                   /* Number of arguments. */  
     Tcl_Obj *CONST objv[];      /* Argument objects. */  
 {  
     register Entry *entryPtr;  
     Tk_OptionTable optionTable;  
     Tk_Window tkwin;  
   
     optionTable = (Tk_OptionTable) clientData;  
     if (optionTable == NULL) {  
         Tcl_CmdInfo info;  
         char *name;  
   
         /*  
          * We haven't created the option table for this widget class  
          * yet.  Do it now and save the table as the clientData for  
          * the command, so we'll have access to it in future  
          * invocations of the command.  
          */  
   
         optionTable = Tk_CreateOptionTable(interp, optionSpecs);  
         name = Tcl_GetString(objv[0]);  
         Tcl_GetCommandInfo(interp, name, &info);  
         info.objClientData = (ClientData) optionTable;  
         Tcl_SetCommandInfo(interp, name, &info);  
     }  
   
     if (objc < 2) {  
         Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");  
         return TCL_ERROR;  
     }  
   
     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),  
             Tcl_GetString(objv[1]), (char *) NULL);  
     if (tkwin == NULL) {  
         return TCL_ERROR;  
     }  
   
     /*  
      * Initialize the fields of the structure that won't be initialized  
      * by ConfigureEntry, or that ConfigureEntry requires to be  
      * initialized already (e.g. resource pointers).  
      */  
   
     entryPtr                    = (Entry *) ckalloc(sizeof(Entry));  
     entryPtr->tkwin             = tkwin;  
     entryPtr->display           = Tk_Display(tkwin);  
     entryPtr->interp            = interp;  
     entryPtr->widgetCmd         = Tcl_CreateObjCommand(interp,  
             Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd,  
             (ClientData) entryPtr, EntryCmdDeletedProc);  
     entryPtr->optionTable       = optionTable;  
     entryPtr->string            = (char *) ckalloc(1);  
     entryPtr->string[0]         = '\0';  
     entryPtr->insertPos         = 0;  
     entryPtr->selectFirst       = -1;  
     entryPtr->selectLast        = -1;  
     entryPtr->selectAnchor      = 0;  
     entryPtr->scanMarkX         = 0;  
     entryPtr->scanMarkIndex     = 0;  
   
     entryPtr->normalBorder      = NULL;  
     entryPtr->borderWidth       = 0;  
     entryPtr->cursor            = None;  
     entryPtr->exportSelection   = 1;  
     entryPtr->tkfont            = NULL;  
     entryPtr->fgColorPtr        = NULL;  
     entryPtr->highlightBgColorPtr       = NULL;  
     entryPtr->highlightColorPtr = NULL;  
     entryPtr->highlightWidth    = 0;  
     entryPtr->insertBorder      = NULL;  
     entryPtr->insertBorderWidth = 0;  
     entryPtr->insertOffTime     = 0;  
     entryPtr->insertOnTime      = 0;  
     entryPtr->insertWidth       = 0;  
     entryPtr->justify           = TK_JUSTIFY_LEFT;  
     entryPtr->relief            = TK_RELIEF_FLAT;  
     entryPtr->selBorder         = NULL;  
     entryPtr->selBorderWidth    = 0;  
     entryPtr->selFgColorPtr     = NULL;  
     entryPtr->showChar          = NULL;  
     entryPtr->state             = STATE_NORMAL;  
     entryPtr->textVarName       = NULL;  
     entryPtr->takeFocus         = NULL;  
     entryPtr->prefWidth         = 0;  
     entryPtr->scrollCmd         = NULL;  
     entryPtr->numBytes          = 0;  
     entryPtr->numChars          = 0;  
     entryPtr->displayString     = entryPtr->string;  
     entryPtr->numDisplayBytes   = 0;  
     entryPtr->inset             = XPAD;  
     entryPtr->textLayout        = NULL;  
     entryPtr->layoutX           = 0;  
     entryPtr->layoutY           = 0;  
     entryPtr->leftX             = 0;  
     entryPtr->leftIndex         = 0;  
     entryPtr->insertBlinkHandler        = (Tcl_TimerToken) NULL;  
     entryPtr->textGC            = None;  
     entryPtr->selTextGC         = None;  
     entryPtr->highlightGC       = None;  
     entryPtr->avgWidth          = 1;  
     entryPtr->flags             = 0;  
     entryPtr->validateCmd       = NULL;  
     entryPtr->validate          = VALIDATE_NONE;  
     entryPtr->invalidCmd        = NULL;  
   
     Tk_SetClass(entryPtr->tkwin, "Entry");  
     TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);  
     Tk_CreateEventHandler(entryPtr->tkwin,  
             ExposureMask|StructureNotifyMask|FocusChangeMask,  
             EntryEventProc, (ClientData) entryPtr);  
     Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,  
             EntryFetchSelection, (ClientData) entryPtr, XA_STRING);  
   
     if ((Tk_InitOptions(interp, (char *) entryPtr, optionTable, tkwin)  
             != TCL_OK) ||  
             (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK)) {  
         Tk_DestroyWindow(entryPtr->tkwin);  
         return TCL_ERROR;  
     }  
       
     Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC);  
     return TCL_OK;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * EntryWidgetObjCmd --  
  *  
  *      This procedure is invoked to process the Tcl command  
  *      that corresponds to a widget managed by this module.  
  *      See the user documentation for details on what it does.  
  *  
  * Results:  
  *      A standard Tcl result.  
  *  
  * Side effects:  
  *      See the user documentation.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 EntryWidgetObjCmd(clientData, interp, objc, objv)  
     ClientData clientData;      /* Information about entry widget. */  
     Tcl_Interp *interp;         /* Current interpreter. */  
     int objc;                   /* Number of arguments. */  
     Tcl_Obj *CONST objv[];      /* Argument objects. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
     int cmdIndex, selIndex, result;  
     Tcl_Obj *objPtr;  
   
     if (objc < 2) {  
         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");  
         return TCL_ERROR;  
     }  
     Tcl_Preserve((ClientData) entryPtr);  
   
     /*  
      * Parse the widget command by looking up the second token in  
      * the list of valid command names.  
      */  
   
     result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,  
             "option", 0, &cmdIndex);  
     if (result != TCL_OK) {  
         return result;  
     }  
   
     switch (cmdIndex) {  
         case COMMAND_BBOX: {  
             int index, x, y, width, height;  
             char buf[TCL_INTEGER_SPACE * 4];  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "index");  
                 goto error;  
             }  
             if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                     &index) != TCL_OK) {  
                 goto error;  
             }  
             if ((index == entryPtr->numChars) && (index > 0)) {  
                 index--;  
             }  
             Tk_CharBbox(entryPtr->textLayout, index, &x, &y,  
                     &width, &height);  
             sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX,  
                     y + entryPtr->layoutY, width, height);  
             Tcl_SetResult(interp, buf, TCL_VOLATILE);  
             break;  
         }  
           
         case COMMAND_CGET: {  
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "option");  
                 goto error;  
             }  
               
             objPtr = Tk_GetOptionValue(interp, (char *) entryPtr,  
                     entryPtr->optionTable, objv[2], entryPtr->tkwin);  
             if (objPtr == NULL) {  
                  goto error;  
             } else {  
                 Tcl_SetObjResult(interp, objPtr);  
             }  
             break;  
         }  
   
         case COMMAND_CONFIGURE: {  
             if (objc <= 3) {  
                 objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr,  
                         entryPtr->optionTable,  
                         (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,  
                         entryPtr->tkwin);  
                 if (objPtr == NULL) {  
                     goto error;  
                 } else {  
                     Tcl_SetObjResult(interp, objPtr);  
                 }  
             } else {  
                 result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0);  
             }  
             break;  
         }  
   
         case COMMAND_DELETE: {  
             int first, last;  
   
             if ((objc < 3) || (objc > 4)) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");  
                 goto error;  
             }  
             if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                     &first) != TCL_OK) {  
                 goto error;  
             }  
             if (objc == 3) {  
                 last = first + 1;  
             } else {  
                 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]),  
                         &last) != TCL_OK) {  
                     goto error;  
                 }  
             }  
             if ((last >= first) && (entryPtr->state == STATE_NORMAL)) {  
                 DeleteChars(entryPtr, first, last - first);  
             }  
             break;  
         }  
   
         case COMMAND_GET: {  
             if (objc != 2) {  
                 Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);  
                 goto error;  
             }  
             Tcl_SetResult(interp, entryPtr->string, TCL_STATIC);  
             break;  
         }  
   
         case COMMAND_ICURSOR: {  
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "pos");  
                 goto error;  
             }  
             if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                     &entryPtr->insertPos) != TCL_OK) {  
                 goto error;  
             }  
             EventuallyRedraw(entryPtr);  
             break;  
         }  
           
         case COMMAND_INDEX: {  
             int index;  
   
             if (objc != 3) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "string");  
                 goto error;  
             }  
             if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                     &index) != TCL_OK) {  
                 goto error;  
             }  
             Tcl_SetObjResult(interp, Tcl_NewIntObj(index));  
             break;  
         }  
   
         case COMMAND_INSERT: {  
             int index;  
   
             if (objc != 4) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "index text");  
                 goto error;  
             }  
             if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                     &index) != TCL_OK) {  
                 goto error;  
             }  
             if (entryPtr->state == STATE_NORMAL) {  
                 InsertChars(entryPtr, index, Tcl_GetString(objv[3]));  
             }  
             break;  
         }  
   
         case COMMAND_SCAN: {  
             int x;  
             char *minorCmd;  
   
             if (objc != 4) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x");  
                 goto error;  
             }  
             if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {  
                 goto error;  
             }  
   
             minorCmd = Tcl_GetString(objv[2]);  
             if (minorCmd[0] == 'm'  
                     && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) {  
                 entryPtr->scanMarkX = x;  
                 entryPtr->scanMarkIndex = entryPtr->leftIndex;  
             } else if ((minorCmd[0] == 'd')  
                 && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) {  
                 EntryScanTo(entryPtr, x);  
             } else {  
                 Tcl_AppendResult(interp, "bad scan option \"",  
                         Tcl_GetString(objv[2]), "\": must be mark or dragto",  
                         (char *) NULL);  
                 goto error;  
             }  
             break;  
         }  
               
         case COMMAND_SELECTION: {  
             int index, index2;  
   
             if (objc < 3) {  
                 Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");  
                 goto error;  
             }  
   
             /*  
              * Parse the selection sub-command, using the command  
              * table "selCommandNames" defined above.  
              */  
               
             result = Tcl_GetIndexFromObj(interp, objv[2], selCommandNames,  
                     "selection option", 0, &selIndex);  
             if (result != TCL_OK) {  
                 goto error;  
             }  
   
             switch(selIndex) {  
                 case SELECTION_ADJUST: {  
                     if (objc != 4) {  
                         Tcl_WrongNumArgs(interp, 3, objv, "index");  
                         goto error;  
                     }  
                     if (GetEntryIndex(interp, entryPtr,  
                             Tcl_GetString(objv[3]), &index) != TCL_OK) {  
                         goto error;  
                     }  
                     if (entryPtr->selectFirst >= 0) {  
                         int half1, half2;  
                   
                         half1 = (entryPtr->selectFirst  
                                 + entryPtr->selectLast)/2;  
                         half2 = (entryPtr->selectFirst  
                                 + entryPtr->selectLast + 1)/2;  
                         if (index < half1) {  
                             entryPtr->selectAnchor = entryPtr->selectLast;  
                         } else if (index > half2) {  
                             entryPtr->selectAnchor = entryPtr->selectFirst;  
                         } else {  
                           /*  
                            * We're at about the halfway point in the  
                            * selection; just keep the existing anchor.  
                            */  
                         }  
                     }  
                     EntrySelectTo(entryPtr, index);  
                     break;  
                 }  
   
                 case SELECTION_CLEAR: {  
                     if (objc != 3) {  
                         Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);  
                         goto error;  
                     }  
                     if (entryPtr->selectFirst >= 0) {  
                         entryPtr->selectFirst = -1;  
                         entryPtr->selectLast = -1;  
                         EventuallyRedraw(entryPtr);  
                     }  
                     goto done;  
                 }  
   
                 case SELECTION_FROM: {  
                     if (objc != 4) {  
                         Tcl_WrongNumArgs(interp, 3, objv, "index");  
                         goto error;  
                     }  
                     if (GetEntryIndex(interp, entryPtr,  
                             Tcl_GetString(objv[3]), &index) != TCL_OK) {  
                         goto error;  
                     }  
                     entryPtr->selectAnchor = index;  
                     break;  
                 }  
   
                 case SELECTION_PRESENT: {  
                     if (objc != 3) {  
                         Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);  
                         goto error;  
                     }  
                     if (entryPtr->selectFirst < 0) {  
                         Tcl_SetResult(interp, "0", TCL_STATIC);  
                     } else {  
                         Tcl_SetResult(interp, "1", TCL_STATIC);  
                     }  
                     goto done;  
                 }  
   
                 case SELECTION_RANGE: {  
                     if (objc != 5) {  
                         Tcl_WrongNumArgs(interp, 3, objv, "start end");  
                         goto error;  
                     }  
                     if (GetEntryIndex(interp, entryPtr,  
                             Tcl_GetString(objv[3]), &index) != TCL_OK) {  
                         goto error;  
                     }  
                     if (GetEntryIndex(interp, entryPtr,  
                             Tcl_GetString(objv[4]),& index2) != TCL_OK) {  
                         goto error;  
                     }  
                     if (index >= index2) {  
                         entryPtr->selectFirst = -1;  
                         entryPtr->selectLast = -1;  
                     } else {  
                         entryPtr->selectFirst = index;  
                         entryPtr->selectLast = index2;  
                     }  
                     if (!(entryPtr->flags & GOT_SELECTION)  
                             && (entryPtr->exportSelection)) {  
                         Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,  
                                 EntryLostSelection, (ClientData) entryPtr);  
                         entryPtr->flags |= GOT_SELECTION;  
                     }  
                     EventuallyRedraw(entryPtr);  
                     break;  
                 }  
                   
                 case SELECTION_TO: {  
                     if (objc != 4) {  
                         Tcl_WrongNumArgs(interp, 3, objv, "index");  
                         goto error;  
                     }  
                     if (GetEntryIndex(interp, entryPtr,  
                             Tcl_GetString(objv[3]), &index) != TCL_OK) {  
                         goto error;  
                     }  
                     EntrySelectTo(entryPtr, index);  
                     break;  
                 }  
             }  
             break;  
         }  
   
         case COMMAND_VALIDATE: {  
             int code;  
   
             if (objc != 2) {  
                 Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);  
                 goto error;  
             }  
             selIndex = entryPtr->validate;  
             entryPtr->validate = VALIDATE_ALL;  
             code = EntryValidateChange(entryPtr, (char *) NULL,  
                                        entryPtr->string, -1, VALIDATE_FORCED);  
             if (entryPtr->validate != VALIDATE_NONE) {  
                 entryPtr->validate = selIndex;  
             }  
             Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK)));  
             break;  
         }  
   
         case COMMAND_XVIEW: {  
             int index;  
   
             if (objc == 2) {  
                 double first, last;  
                 char buf[TCL_DOUBLE_SPACE * 2];  
               
                 EntryVisibleRange(entryPtr, &first, &last);  
                 sprintf(buf, "%g %g", first, last);  
                 Tcl_SetResult(interp, buf, TCL_VOLATILE);  
                 goto done;  
             } else if (objc == 3) {  
                 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),  
                         &index) != TCL_OK) {  
                     goto error;  
                 }  
             } else {  
                 double fraction;  
                 int count;  
   
                 index = entryPtr->leftIndex;  
                 switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction,  
                         &count)) {  
                     case TK_SCROLL_ERROR: {  
                         goto error;  
                     }  
                     case TK_SCROLL_MOVETO: {  
                         index = (int) ((fraction * entryPtr->numChars) + 0.5);  
                         break;  
                     }  
                     case TK_SCROLL_PAGES: {  
                         int charsPerPage;  
                       
                         charsPerPage = ((Tk_Width(entryPtr->tkwin)  
                                 - 2 * entryPtr->inset)  
                                 / entryPtr->avgWidth) - 2;  
                         if (charsPerPage < 1) {  
                             charsPerPage = 1;  
                         }  
                         index += count * charsPerPage;  
                         break;  
                     }  
                     case TK_SCROLL_UNITS: {  
                         index += count;  
                         break;  
                     }  
                 }  
             }  
             if (index >= entryPtr->numChars) {  
                 index = entryPtr->numChars - 1;  
             }  
             if (index < 0) {  
                 index = 0;  
             }  
             entryPtr->leftIndex = index;  
             entryPtr->flags |= UPDATE_SCROLLBAR;  
             EntryComputeGeometry(entryPtr);  
             EventuallyRedraw(entryPtr);  
             break;  
         }  
     }  
   
     done:  
     Tcl_Release((ClientData) entryPtr);  
     return result;  
   
     error:  
     Tcl_Release((ClientData) entryPtr);  
     return TCL_ERROR;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DestroyEntry --  
  *  
  *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release  
  *      to clean up the internal structure of an entry at a safe time  
  *      (when no-one is using it anymore).  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Everything associated with the entry is freed up.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DestroyEntry(memPtr)  
     char *memPtr;               /* Info about entry widget. */  
 {  
     Entry *entryPtr = (Entry *) memPtr;  
     entryPtr->flags |= ENTRY_DELETED;  
   
     Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);  
     if (entryPtr->flags & REDRAW_PENDING) {  
         Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);  
     }  
   
     /*  
      * Free up all the stuff that requires special handling, then  
      * let Tk_FreeOptions handle all the standard option-related  
      * stuff.  
      */  
   
     ckfree(entryPtr->string);  
     if (entryPtr->textVarName != NULL) {  
         Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,  
                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                 EntryTextVarProc, (ClientData) entryPtr);  
     }  
     if (entryPtr->textGC != None) {  
         Tk_FreeGC(entryPtr->display, entryPtr->textGC);  
     }  
     if (entryPtr->selTextGC != None) {  
         Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);  
     }  
     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);  
     if (entryPtr->displayString != entryPtr->string) {  
         ckfree(entryPtr->displayString);  
     }  
     Tk_FreeTextLayout(entryPtr->textLayout);  
     Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable,  
             entryPtr->tkwin);  
     entryPtr->tkwin = NULL;  
     ckfree((char *) entryPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * ConfigureEntry --  
  *  
  *      This procedure is called to process an argv/argc list, plus  
  *      the Tk option database, in order to configure (or reconfigure)  
  *      an entry widget.  
  *  
  * Results:  
  *      The return value is a standard Tcl result.  If TCL_ERROR is  
  *      returned, then the interp's result contains an error message.  
  *  
  * Side effects:  
  *      Configuration information, such as colors, border width,  
  *      etc. get set for entryPtr;  old resources get freed,  
  *      if there were any.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 ConfigureEntry(interp, entryPtr, objc, objv, flags)  
     Tcl_Interp *interp;         /* Used for error reporting. */  
     Entry *entryPtr;            /* Information about widget; may or may not  
                                  * already have values for some fields. */  
     int objc;                   /* Number of valid entries in argv. */  
     Tcl_Obj *CONST objv[];      /* Argument objects. */  
     int flags;                  /* Flags to pass to Tk_ConfigureWidget. */  
 {  
     Tk_SavedOptions savedOptions;  
     Tcl_Obj *errorResult = NULL;  
     int error;  
     int oldExport;  
   
     /*  
      * Eliminate any existing trace on a variable monitored by the entry.  
      */  
   
     if (entryPtr->textVarName != NULL) {  
         Tcl_UntraceVar(interp, entryPtr->textVarName,  
                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                 EntryTextVarProc, (ClientData) entryPtr);  
     }  
   
     oldExport = entryPtr->exportSelection;  
   
     for (error = 0; error <= 1; error++) {  
         if (!error) {  
             /*  
              * First pass: set options to new values.  
              */  
   
             if (Tk_SetOptions(interp, (char *) entryPtr,  
                     entryPtr->optionTable, objc, objv,  
                     entryPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {  
                 continue;  
             }  
         } else {  
             /*  
              * Second pass: restore options to old values.  
              */  
   
             errorResult = Tcl_GetObjResult(interp);  
             Tcl_IncrRefCount(errorResult);  
             Tk_RestoreSavedOptions(&savedOptions);  
         }  
   
         /*  
          * A few other options also need special processing, such as parsing  
          * the geometry and setting the background from a 3-D border.  
          */  
   
         Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);  
   
         if (entryPtr->insertWidth <= 0) {  
             entryPtr->insertWidth = 2;  
         }  
         if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {  
             entryPtr->insertBorderWidth = entryPtr->insertWidth/2;  
         }  
   
         /*  
          * Restart the cursor timing sequence in case the on-time or  
          * off-time just changed.  Set validate temporarily to none,  
          * so the configure doesn't cause it to be triggered.  
          */  
   
         if (entryPtr->flags & GOT_FOCUS) {  
             int validate = entryPtr->validate;  
             entryPtr->validate = VALIDATE_NONE;  
             EntryFocusProc(entryPtr, 1);  
             entryPtr->validate = validate;  
         }  
   
         /*  
          * Claim the selection if we've suddenly started exporting it.  
          */  
   
         if (entryPtr->exportSelection && (!oldExport)  
                 && (entryPtr->selectFirst != -1)  
                 && !(entryPtr->flags & GOT_SELECTION)) {  
             Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,  
                     (ClientData) entryPtr);  
             entryPtr->flags |= GOT_SELECTION;  
         }  
   
         /*  
          * Recompute the window's geometry and arrange for it to be  
          * redisplayed.  
          */  
   
         Tk_SetInternalBorder(entryPtr->tkwin,  
                 entryPtr->borderWidth + entryPtr->highlightWidth);  
         if (entryPtr->highlightWidth <= 0) {  
             entryPtr->highlightWidth = 0;  
         }  
         entryPtr->inset = entryPtr->highlightWidth  
                 + entryPtr->borderWidth + XPAD;  
         break;  
     }  
     if (!error) {  
         Tk_FreeSavedOptions(&savedOptions);  
     }  
   
     /*  
      * If the entry is tied to the value of a variable, then set up  
      * a trace on the variable's value, create the variable if it doesn't  
      * exist, and set the entry's value from the variable's value.  
      */  
   
     if (entryPtr->textVarName != NULL) {  
         char *value;  
   
         value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);  
         if (value == NULL) {  
             EntryValueChanged(entryPtr);  
         } else {  
             EntrySetValue(entryPtr, value);  
         }  
         Tcl_TraceVar(interp, entryPtr->textVarName,  
                 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                 EntryTextVarProc, (ClientData) entryPtr);  
     }  
   
     EntryWorldChanged((ClientData) entryPtr);  
     if (error) {  
         Tcl_SetObjResult(interp, errorResult);  
         Tcl_DecrRefCount(errorResult);  
         return TCL_ERROR;  
     } else {  
         return TCL_OK;  
     }  
 }  
   
 /*  
  *---------------------------------------------------------------------------  
  *  
  * EntryWorldChanged --  
  *  
  *      This procedure is called when the world has changed in some  
  *      way and the widget needs to recompute all its graphics contexts  
  *      and determine its new geometry.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Entry will be relayed out and redisplayed.  
  *  
  *---------------------------------------------------------------------------  
  */  
   
 static void  
 EntryWorldChanged(instanceData)  
     ClientData instanceData;    /* Information about widget. */  
 {  
     XGCValues gcValues;  
     GC gc = None;  
     unsigned long mask;  
     Entry *entryPtr;  
   
     entryPtr = (Entry *) instanceData;  
   
     entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);  
     if (entryPtr->avgWidth == 0) {  
         entryPtr->avgWidth = 1;  
     }  
   
     if (entryPtr->normalBorder != NULL) {  
         Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);  
     }  
   
     gcValues.foreground = entryPtr->fgColorPtr->pixel;  
     gcValues.font = Tk_FontId(entryPtr->tkfont);  
     gcValues.graphics_exposures = False;  
     mask = GCForeground | GCFont | GCGraphicsExposures;  
     gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);  
     if (entryPtr->textGC != None) {  
         Tk_FreeGC(entryPtr->display, entryPtr->textGC);  
     }  
     entryPtr->textGC = gc;  
   
     gcValues.foreground = entryPtr->selFgColorPtr->pixel;  
     gcValues.font = Tk_FontId(entryPtr->tkfont);  
     mask = GCForeground | GCFont;  
     gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);  
     if (entryPtr->selTextGC != None) {  
         Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);  
     }  
     entryPtr->selTextGC = gc;  
   
     /*  
      * Recompute the window's geometry and arrange for it to be  
      * redisplayed.  
      */  
   
     EntryComputeGeometry(entryPtr);  
     entryPtr->flags |= UPDATE_SCROLLBAR;  
     EventuallyRedraw(entryPtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * DisplayEntry --  
  *  
  *      This procedure redraws the contents of an entry window.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Information appears on the screen.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 DisplayEntry(clientData)  
     ClientData clientData;      /* Information about window. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
     Tk_Window tkwin = entryPtr->tkwin;  
     int baseY, selStartX, selEndX, cursorX;  
     int xBound;  
     Tk_FontMetrics fm;  
     Pixmap pixmap;  
     int showSelection;  
   
     entryPtr->flags &= ~REDRAW_PENDING;  
     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {  
         return;  
     }  
   
     Tk_GetFontMetrics(entryPtr->tkfont, &fm);  
   
     /*  
      * Update the scrollbar if that's needed.  
      */  
   
     if (entryPtr->flags & UPDATE_SCROLLBAR) {  
         entryPtr->flags &= ~UPDATE_SCROLLBAR;  
         EntryUpdateScrollbar(entryPtr);  
     }  
   
     /*  
      * In order to avoid screen flashes, this procedure redraws the  
      * textual area of the entry into off-screen memory, then copies  
      * it back on-screen in a single operation.  This means there's  
      * no point in time where the on-screen image has been cleared.  
      */  
   
     pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),  
             Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));  
   
     /*  
      * Compute x-coordinate of the pixel just after last visible  
      * one, plus vertical position of baseline of text.  
      */  
   
     xBound = Tk_Width(tkwin) - entryPtr->inset;  
     baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;  
   
     /*  
      * On Windows and Mac, we need to hide the selection whenever we  
      * don't have the focus.  
      */  
   
 #ifdef ALWAYS_SHOW_SELECTION  
     showSelection = 1;  
 #else  
     showSelection = (entryPtr->flags & GOT_FOCUS);  
 #endif  
   
     /*  
      * Draw the background in three layers.  From bottom to top the  
      * layers are:  normal background, selection background, and  
      * insertion cursor background.  
      */  
   
     Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,  
                 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);  
   
     if (showSelection  
             && (entryPtr->selectLast > entryPtr->leftIndex)) {  
         if (entryPtr->selectFirst <= entryPtr->leftIndex) {  
             selStartX = entryPtr->leftX;  
         } else {  
             Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,  
                     &selStartX, NULL, NULL, NULL);  
             selStartX += entryPtr->layoutX;  
         }  
         if ((selStartX - entryPtr->selBorderWidth) < xBound) {  
             Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast,  
                     &selEndX, NULL, NULL, NULL);  
             selEndX += entryPtr->layoutX;  
             Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,  
                     selStartX - entryPtr->selBorderWidth,  
                     baseY - fm.ascent - entryPtr->selBorderWidth,  
                     (selEndX - selStartX) + 2*entryPtr->selBorderWidth,  
                     (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,  
                     entryPtr->selBorderWidth, TK_RELIEF_RAISED);  
         }  
     }  
   
     /*  
      * Draw a special background for the insertion cursor, overriding  
      * even the selection background.  As a special hack to keep the  
      * cursor visible when the insertion cursor color is the same as  
      * the color for selected text (e.g., on mono displays), write  
      * background in the cursor area (instead of nothing) when the  
      * cursor isn't on.  Otherwise the selection would hide the cursor.  
      */  
   
     if ((entryPtr->insertPos >= entryPtr->leftIndex)  
             && (entryPtr->state == STATE_NORMAL)  
             && (entryPtr->flags & GOT_FOCUS)) {  
         Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos, &cursorX, NULL,  
                 NULL, NULL);  
         cursorX += entryPtr->layoutX;  
         cursorX -= (entryPtr->insertWidth)/2;  
         if (cursorX < xBound) {  
             if (entryPtr->flags & CURSOR_ON) {  
                 Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,  
                         cursorX, baseY - fm.ascent, entryPtr->insertWidth,  
                         fm.ascent + fm.descent, entryPtr->insertBorderWidth,  
                         TK_RELIEF_RAISED);  
             } else if (entryPtr->insertBorder == entryPtr->selBorder) {  
                 Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,  
                         cursorX, baseY - fm.ascent, entryPtr->insertWidth,  
                         fm.ascent + fm.descent, 0, TK_RELIEF_FLAT);  
             }  
         }  
     }  
   
     /*  
      * Draw the text in two pieces:  first the unselected portion, then the  
      * selected portion on top of it.  
      */  
   
     Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,  
             entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,  
             entryPtr->leftIndex, entryPtr->numChars);  
   
     if (showSelection  
             && (entryPtr->selTextGC != entryPtr->textGC)  
             && (entryPtr->selectFirst < entryPtr->selectLast)) {  
         int selFirst;  
   
         if (entryPtr->selectFirst < entryPtr->leftIndex) {  
             selFirst = entryPtr->leftIndex;  
         } else {  
             selFirst = entryPtr->selectFirst;  
         }  
         Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,  
                 entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,  
                 selFirst, entryPtr->selectLast);  
     }  
   
     /*  
      * Draw the border and focus highlight last, so they will overwrite  
      * any text that extends past the viewable part of the window.  
      */  
   
     if (entryPtr->relief != TK_RELIEF_FLAT) {  
         Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,  
                 entryPtr->highlightWidth, entryPtr->highlightWidth,  
                 Tk_Width(tkwin) - 2 * entryPtr->highlightWidth,  
                 Tk_Height(tkwin) - 2 * entryPtr->highlightWidth,  
                 entryPtr->borderWidth, entryPtr->relief);  
     }  
     if (entryPtr->highlightWidth != 0) {  
         GC fgGC, bgGC;  
   
         bgGC = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);  
         if (entryPtr->flags & GOT_FOCUS) {  
             fgGC = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);  
             TkpDrawHighlightBorder(tkwin, fgGC, bgGC,  
                     entryPtr->highlightWidth, pixmap);  
         } else {  
             TkpDrawHighlightBorder(tkwin, bgGC, bgGC,  
                     entryPtr->highlightWidth, pixmap);  
         }  
     }  
   
     /*  
      * Everything's been redisplayed;  now copy the pixmap onto the screen  
      * and free up the pixmap.  
      */  
   
     XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,  
             0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),  
             0, 0);  
     Tk_FreePixmap(entryPtr->display, pixmap);  
     entryPtr->flags &= ~BORDER_NEEDED;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryComputeGeometry --  
  *  
  *      This procedure is invoked to recompute information about where  
  *      in its window an entry's string will be displayed.  It also  
  *      computes the requested size for the window.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The leftX and tabOrigin fields are recomputed for entryPtr,  
  *      and leftIndex may be adjusted.  Tk_GeometryRequest is called  
  *      to register the desired dimensions for the window.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryComputeGeometry(entryPtr)  
     Entry *entryPtr;            /* Widget record for entry. */  
 {  
     int totalLength, overflow, maxOffScreen, rightX;  
     int height, width, i;  
     Tk_FontMetrics fm;  
     char *p;  
   
     if (entryPtr->displayString != entryPtr->string) {  
         ckfree(entryPtr->displayString);  
         entryPtr->displayString = entryPtr->string;  
         entryPtr->numDisplayBytes = entryPtr->numBytes;  
     }  
   
     /*  
      * If we're displaying a special character instead of the value of  
      * the entry, recompute the displayString.  
      */  
   
     if (entryPtr->showChar != NULL) {  
         Tcl_UniChar ch;  
         char buf[TCL_UTF_MAX];  
         int size;  
   
         /*  
          * Normalize the special character so we can safely duplicate it  
          * in the display string.  If we didn't do this, then two malformed  
          * characters might end up looking like one valid UTF character in  
          * the resulting string.  
          */  
   
         Tcl_UtfToUniChar(entryPtr->showChar, &ch);  
         size = Tcl_UniCharToUtf(ch, buf);  
   
         entryPtr->numDisplayBytes = entryPtr->numChars * size;  
         entryPtr->displayString =  
                 (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1));  
   
         p = entryPtr->displayString;  
         for (i = entryPtr->numChars; --i >= 0; ) {  
             p += Tcl_UniCharToUtf(ch, p);  
         }  
         *p = '\0';  
     }  
     Tk_FreeTextLayout(entryPtr->textLayout);  
     entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,  
             entryPtr->displayString, entryPtr->numChars, 0,  
             entryPtr->justify, TK_IGNORE_NEWLINES, &totalLength, &height);  
   
     entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;  
   
     /*  
      * Recompute where the leftmost character on the display will  
      * be drawn (entryPtr->leftX) and adjust leftIndex if necessary  
      * so that we don't let characters hang off the edge of the  
      * window unless the entire window is full.  
      */  
   
     overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);  
     if (overflow <= 0) {  
         entryPtr->leftIndex = 0;  
         if (entryPtr->justify == TK_JUSTIFY_LEFT) {  
             entryPtr->leftX = entryPtr->inset;  
         } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {  
             entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset  
                     - totalLength;  
         } else {  
             entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;  
         }  
         entryPtr->layoutX = entryPtr->leftX;  
     } else {  
         /*  
          * The whole string can't fit in the window.  Compute the  
          * maximum number of characters that may be off-screen to  
          * the left without leaving empty space on the right of the  
          * window, then don't let leftIndex be any greater than that.  
          */  
   
         maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);  
         Tk_CharBbox(entryPtr->textLayout, maxOffScreen,  
                 &rightX, NULL, NULL, NULL);  
         if (rightX < overflow) {  
             maxOffScreen++;  
         }  
         if (entryPtr->leftIndex > maxOffScreen) {  
             entryPtr->leftIndex = maxOffScreen;  
         }  
         Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex, &rightX,  
                 NULL, NULL, NULL);  
         entryPtr->leftX = entryPtr->inset;  
         entryPtr->layoutX = entryPtr->leftX - rightX;  
     }  
   
     Tk_GetFontMetrics(entryPtr->tkfont, &fm);  
     height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);  
     if (entryPtr->prefWidth > 0) {  
         width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;  
     } else {  
         if (totalLength == 0) {  
             width = entryPtr->avgWidth + 2*entryPtr->inset;  
         } else {  
             width = totalLength + 2*entryPtr->inset;  
         }  
     }  
     Tk_GeometryRequest(entryPtr->tkwin, width, height);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * InsertChars --  
  *  
  *      Add new characters to an entry widget.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      New information gets added to entryPtr;  it will be redisplayed  
  *      soon, but not necessarily immediately.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 InsertChars(entryPtr, index, value)  
     Entry *entryPtr;            /* Entry that is to get the new elements. */  
     int index;                  /* Add the new elements before this  
                                  * character index. */  
     char *value;                /* New characters to add (NULL-terminated  
                                  * string). */  
 {  
     int byteIndex, byteCount, oldChars, charsAdded, newByteCount;  
     char *new, *string;  
   
     string = entryPtr->string;  
     byteIndex = Tcl_UtfAtIndex(string, index) - string;  
     byteCount = strlen(value);  
     if (byteCount == 0) {  
         return;  
     }  
   
     newByteCount = entryPtr->numBytes + byteCount + 1;  
     new = (char *) ckalloc((unsigned) newByteCount);  
     memcpy(new, string, (size_t) byteIndex);  
     strcpy(new + byteIndex, value);  
     strcpy(new + byteIndex + byteCount, string + byteIndex);  
   
     if ((entryPtr->validate == VALIDATE_KEY ||  
          entryPtr->validate == VALIDATE_ALL) &&  
         EntryValidateChange(entryPtr, value, new, index,  
                             VALIDATE_INSERT) != TCL_OK) {  
         ckfree(new);  
         return;  
     }  
   
     ckfree(string);  
     entryPtr->string = new;  
   
     /*  
      * The following construction is used because inserting improperly  
      * formed UTF-8 sequences between other improperly formed UTF-8  
      * sequences could result in actually forming valid UTF-8 sequences;  
      * the number of characters added may not be Tcl_NumUtfChars(string, -1),  
      * because of context.  The actual number of characters added is how  
      * many characters are in the string now minus the number that  
      * used to be there.  
      */  
   
     oldChars = entryPtr->numChars;  
     entryPtr->numChars = Tcl_NumUtfChars(new, -1);  
     charsAdded = entryPtr->numChars - oldChars;  
     entryPtr->numBytes += byteCount;  
   
     if (entryPtr->displayString == string) {  
         entryPtr->displayString = new;  
         entryPtr->numDisplayBytes = entryPtr->numBytes;  
     }  
   
     /*  
      * Inserting characters invalidates all indexes into the string.  
      * Touch up the indexes so that they still refer to the same  
      * characters (at new positions).  When updating the selection  
      * end-points, don't include the new text in the selection unless  
      * it was completely surrounded by the selection.  
      */  
   
     if (entryPtr->selectFirst >= index) {  
         entryPtr->selectFirst += charsAdded;  
     }  
     if (entryPtr->selectLast > index) {  
         entryPtr->selectLast += charsAdded;  
     }  
     if ((entryPtr->selectAnchor > index)  
             || (entryPtr->selectFirst >= index)) {  
         entryPtr->selectAnchor += charsAdded;  
     }  
     if (entryPtr->leftIndex > index) {  
         entryPtr->leftIndex += charsAdded;  
     }  
     if (entryPtr->insertPos >= index) {  
         entryPtr->insertPos += charsAdded;  
     }  
     EntryValueChanged(entryPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * DeleteChars --  
  *  
  *      Remove one or more characters from an entry widget.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Memory gets freed, the entry gets modified and (eventually)  
  *      redisplayed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 DeleteChars(entryPtr, index, count)  
     Entry *entryPtr;            /* Entry widget to modify. */  
     int index;                  /* Index of first character to delete. */  
     int count;                  /* How many characters to delete. */  
 {  
     int byteIndex, byteCount, newByteCount;  
     char *new, *string, *todelete;  
   
     if ((index + count) > entryPtr->numChars) {  
         count = entryPtr->numChars - index;  
     }  
     if (count <= 0) {  
         return;  
     }  
   
     string = entryPtr->string;  
     byteIndex = Tcl_UtfAtIndex(string, index) - string;  
     byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string + byteIndex);  
   
     newByteCount = entryPtr->numBytes + 1 - byteCount;  
     new = (char *) ckalloc((unsigned) newByteCount);  
     memcpy(new, string, (size_t) byteIndex);  
     strcpy(new + byteIndex, string + byteIndex + byteCount);  
   
     todelete = (char *) ckalloc((unsigned) (byteCount + 1));  
     memcpy(todelete, string + byteIndex, (size_t) byteCount);  
     todelete[byteCount] = '\0';  
   
     if ((entryPtr->validate == VALIDATE_KEY ||  
          entryPtr->validate == VALIDATE_ALL) &&  
         EntryValidateChange(entryPtr, todelete, new, index,  
                             VALIDATE_DELETE) != TCL_OK) {  
         ckfree(new);  
         ckfree(todelete);  
         return;  
     }  
   
     ckfree(todelete);  
     ckfree(entryPtr->string);  
     entryPtr->string = new;  
     entryPtr->numChars -= count;  
     entryPtr->numBytes -= byteCount;  
   
     if (entryPtr->displayString == string) {  
         entryPtr->displayString = new;  
         entryPtr->numDisplayBytes = entryPtr->numBytes;  
     }  
   
     /*  
      * Deleting characters results in the remaining characters being  
      * renumbered.  Update the various indexes into the string to reflect  
      * this change.  
      */  
   
     if (entryPtr->selectFirst >= index) {  
         if (entryPtr->selectFirst >= (index + count)) {  
             entryPtr->selectFirst -= count;  
         } else {  
             entryPtr->selectFirst = index;  
         }  
     }  
     if (entryPtr->selectLast >= index) {  
         if (entryPtr->selectLast >= (index + count)) {  
             entryPtr->selectLast -= count;  
         } else {  
             entryPtr->selectLast = index;  
         }  
     }  
     if (entryPtr->selectLast <= entryPtr->selectFirst) {  
         entryPtr->selectFirst = -1;  
         entryPtr->selectLast = -1;  
     }  
     if (entryPtr->selectAnchor >= index) {  
         if (entryPtr->selectAnchor >= (index+count)) {  
             entryPtr->selectAnchor -= count;  
         } else {  
             entryPtr->selectAnchor = index;  
         }  
     }  
     if (entryPtr->leftIndex > index) {  
         if (entryPtr->leftIndex >= (index + count)) {  
             entryPtr->leftIndex -= count;  
         } else {  
             entryPtr->leftIndex = index;  
         }  
     }  
     if (entryPtr->insertPos >= index) {  
         if (entryPtr->insertPos >= (index + count)) {  
             entryPtr->insertPos -= count;  
         } else {  
             entryPtr->insertPos = index;  
         }  
     }  
     EntryValueChanged(entryPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryValueChanged --  
  *  
  *      This procedure is invoked when characters are inserted into  
  *      an entry or deleted from it.  It updates the entry's associated  
  *      variable, if there is one, and does other bookkeeping such  
  *      as arranging for redisplay.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryValueChanged(entryPtr)  
     Entry *entryPtr;            /* Entry whose value just changed. */  
 {  
     char *newValue;  
   
     if (entryPtr->textVarName == NULL) {  
         newValue = NULL;  
     } else {  
         newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,  
                 entryPtr->string, TCL_GLOBAL_ONLY);  
     }  
   
     if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {  
         /*  
          * The value of the variable is different than what we asked for.  
          * This means that a trace on the variable modified it.  In this  
          * case our trace procedure wasn't invoked since the modification  
          * came while a trace was already active on the variable.  So,  
          * update our value to reflect the variable's latest value.  
          */  
   
         EntrySetValue(entryPtr, newValue);  
     } else {  
         /*  
          * Arrange for redisplay.  
          */  
   
         entryPtr->flags |= UPDATE_SCROLLBAR;  
         EntryComputeGeometry(entryPtr);  
         EventuallyRedraw(entryPtr);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntrySetValue --  
  *  
  *      Replace the contents of a text entry with a given value.  This  
  *      procedure is invoked when updating the entry from the entry's  
  *      associated variable.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The string displayed in the entry will change.  The selection,  
  *      insertion point, and view may have to be adjusted to keep them  
  *      within the bounds of the new string.  Note: this procedure does  
  *      *not* update the entry's associated variable, since that could  
  *      result in an infinite loop.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntrySetValue(entryPtr, value)  
     Entry *entryPtr;            /* Entry whose value is to be changed. */  
     char *value;                /* New text to display in entry. */  
 {  
     char *oldSource;  
     int code, valueLen, malloced = 0;  
   
     if (strcmp(value, entryPtr->string) == 0) {  
         return;  
     }  
     valueLen = strlen(value);  
   
     if (entryPtr->flags & VALIDATE_VAR) {  
         entryPtr->flags |= VALIDATE_ABORT;  
     } else {  
         /*  
          * If we validate, we create a copy of the value, as it may  
          * point to volatile memory, like the value of the -textvar  
          * which may get freed during validation  
          */  
         oldSource = (char *) ckalloc((unsigned) (valueLen + 1));  
         strcpy(oldSource, value);  
         value = oldSource;  
         malloced = 1;  
   
         entryPtr->flags |= VALIDATE_VAR;  
         code = EntryValidateChange(entryPtr, (char *) NULL, value, -1,  
                 VALIDATE_FORCED);  
         entryPtr->flags &= ~VALIDATE_VAR;  
         /*  
          * If VALIDATE_ABORT has been set, then this operation should be  
          * aborted because the validatecommand did something else instead  
          */  
         if (entryPtr->flags & VALIDATE_ABORT) {  
             entryPtr->flags &= ~VALIDATE_ABORT;  
             ckfree(value);  
             return;  
         }  
     }  
   
     oldSource = entryPtr->string;  
     ckfree(entryPtr->string);  
   
     if (malloced) {  
         entryPtr->string = value;  
     } else {  
         entryPtr->string   = (char *) ckalloc((unsigned) (valueLen + 1));  
         strcpy(entryPtr->string, value);  
     }  
     entryPtr->numBytes = valueLen;  
     entryPtr->numChars = Tcl_NumUtfChars(value, valueLen);  
   
     if (entryPtr->displayString == oldSource) {  
         entryPtr->displayString = entryPtr->string;  
         entryPtr->numDisplayBytes = entryPtr->numBytes;  
     }  
   
     if (entryPtr->selectFirst >= 0) {  
         if (entryPtr->selectFirst >= entryPtr->numChars) {  
             entryPtr->selectFirst = -1;  
             entryPtr->selectLast = -1;  
         } else if (entryPtr->selectLast > entryPtr->numChars) {  
             entryPtr->selectLast = entryPtr->numChars;  
         }  
     }  
     if (entryPtr->leftIndex >= entryPtr->numChars) {  
         if (entryPtr->numChars > 0) {  
             entryPtr->leftIndex = entryPtr->numChars - 1;  
         } else {  
             entryPtr->leftIndex = 0;  
         }  
     }  
     if (entryPtr->insertPos > entryPtr->numChars) {  
         entryPtr->insertPos = entryPtr->numChars;  
     }  
   
     entryPtr->flags |= UPDATE_SCROLLBAR;  
     EntryComputeGeometry(entryPtr);  
     EventuallyRedraw(entryPtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * EntryEventProc --  
  *  
  *      This procedure is invoked by the Tk dispatcher for various  
  *      events on entryes.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      When the window gets deleted, internal structures get  
  *      cleaned up.  When it gets exposed, it is redisplayed.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 EntryEventProc(clientData, eventPtr)  
     ClientData clientData;      /* Information about window. */  
     XEvent *eventPtr;           /* Information about event. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
     if (eventPtr->type == Expose) {  
         EventuallyRedraw(entryPtr);  
         entryPtr->flags |= BORDER_NEEDED;  
     } else if (eventPtr->type == DestroyNotify) {  
         DestroyEntry((char *) clientData);  
     } else if (eventPtr->type == ConfigureNotify) {  
         Tcl_Preserve((ClientData) entryPtr);  
         entryPtr->flags |= UPDATE_SCROLLBAR;  
         EntryComputeGeometry(entryPtr);  
         EventuallyRedraw(entryPtr);  
         Tcl_Release((ClientData) entryPtr);  
     } else if (eventPtr->type == FocusIn) {  
         if (eventPtr->xfocus.detail != NotifyInferior) {  
             EntryFocusProc(entryPtr, 1);  
         }  
     } else if (eventPtr->type == FocusOut) {  
         if (eventPtr->xfocus.detail != NotifyInferior) {  
             EntryFocusProc(entryPtr, 0);  
         }  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryCmdDeletedProc --  
  *  
  *      This procedure is invoked when a widget command is deleted.  If  
  *      the widget isn't already in the process of being destroyed,  
  *      this command destroys it.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The widget is destroyed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryCmdDeletedProc(clientData)  
     ClientData clientData;      /* Pointer to widget record for widget. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
   
     /*  
      * This procedure could be invoked either because the window was  
      * destroyed and the command was then deleted (in which case tkwin  
      * is NULL) or because the command was deleted, and then this procedure  
      * destroys the widget.  
      */  
   
     if (!(entryPtr->flags & ENTRY_DELETED)) {  
         Tk_DestroyWindow(entryPtr->tkwin);  
     }  
 }  
   
 /*  
  *---------------------------------------------------------------------------  
  *  
  * GetEntryIndex --  
  *  
  *      Parse an index into an entry and return either its value  
  *      or an error.  
  *  
  * Results:  
  *      A standard Tcl result.  If all went well, then *indexPtr is  
  *      filled in with the character index (into entryPtr) corresponding to  
  *      string.  The index value is guaranteed to lie between 0 and  
  *      the number of characters in the string, inclusive.  If an  
  *      error occurs then an error message is left in the interp's result.  
  *  
  * Side effects:  
  *      None.  
  *  
  *---------------------------------------------------------------------------  
  */  
   
 static int  
 GetEntryIndex(interp, entryPtr, string, indexPtr)  
     Tcl_Interp *interp;         /* For error messages. */  
     Entry *entryPtr;            /* Entry for which the index is being  
                                  * specified. */  
     char *string;               /* Specifies character in entryPtr. */  
     int *indexPtr;              /* Where to store converted character  
                                  * index. */  
 {  
     size_t length;  
   
     length = strlen(string);  
   
     if (string[0] == 'a') {  
         if (strncmp(string, "anchor", length) == 0) {  
             *indexPtr = entryPtr->selectAnchor;  
         } else {  
             badIndex:  
   
             /*  
              * Some of the paths here leave messages in the interp's result,  
              * so we have to clear it out before storing our own message.  
              */  
   
             Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);  
             Tcl_AppendResult(interp, "bad entry index \"", string,  
                     "\"", (char *) NULL);  
             return TCL_ERROR;  
         }  
     } else if (string[0] == 'e') {  
         if (strncmp(string, "end", length) == 0) {  
             *indexPtr = entryPtr->numChars;  
         } else {  
             goto badIndex;  
         }  
     } else if (string[0] == 'i') {  
         if (strncmp(string, "insert", length) == 0) {  
             *indexPtr = entryPtr->insertPos;  
         } else {  
             goto badIndex;  
         }  
     } else if (string[0] == 's') {  
         if (entryPtr->selectFirst < 0) {  
             Tcl_SetResult(interp, "selection isn't in entry", TCL_STATIC);  
             return TCL_ERROR;  
         }  
         if (length < 5) {  
             goto badIndex;  
         }  
         if (strncmp(string, "sel.first", length) == 0) {  
             *indexPtr = entryPtr->selectFirst;  
         } else if (strncmp(string, "sel.last", length) == 0) {  
             *indexPtr = entryPtr->selectLast;  
         } else {  
             goto badIndex;  
         }  
     } else if (string[0] == '@') {  
         int x, roundUp;  
   
         if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) {  
             goto badIndex;  
         }  
         if (x < entryPtr->inset) {  
             x = entryPtr->inset;  
         }  
         roundUp = 0;  
         if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {  
             x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;  
             roundUp = 1;  
         }  
         *indexPtr = Tk_PointToChar(entryPtr->textLayout,  
                 x - entryPtr->layoutX, 0);  
   
         /*  
          * Special trick:  if the x-position was off-screen to the right,  
          * round the index up to refer to the character just after the  
          * last visible one on the screen.  This is needed to enable the  
          * last character to be selected, for example.  
          */  
   
         if (roundUp && (*indexPtr < entryPtr->numChars)) {  
             *indexPtr += 1;  
         }  
     } else {  
         if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {  
             goto badIndex;  
         }  
         if (*indexPtr < 0){  
             *indexPtr = 0;  
         } else if (*indexPtr > entryPtr->numChars) {  
             *indexPtr = entryPtr->numChars;  
         }  
     }  
     return TCL_OK;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryScanTo --  
  *  
  *      Given a y-coordinate (presumably of the curent mouse location)  
  *      drag the view in the window to implement the scan operation.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The view in the window may change.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryScanTo(entryPtr, x)  
     Entry *entryPtr;            /* Information about widget. */  
     int x;                      /* X-coordinate to use for scan operation. */  
 {  
     int newLeftIndex;  
   
     /*  
      * Compute new leftIndex for entry by amplifying the difference  
      * between the current position and the place where the scan  
      * started (the "mark" position).  If we run off the left or right  
      * side of the entry, then reset the mark point so that the current  
      * position continues to correspond to the edge of the window.  
      * This means that the picture will start dragging as soon as the  
      * mouse reverses direction (without this reset, might have to slide  
      * mouse a long ways back before the picture starts moving again).  
      */  
   
     newLeftIndex = entryPtr->scanMarkIndex  
             - (10 * (x - entryPtr->scanMarkX)) / entryPtr->avgWidth;  
     if (newLeftIndex >= entryPtr->numChars) {  
         newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars - 1;  
         entryPtr->scanMarkX = x;  
     }  
     if (newLeftIndex < 0) {  
         newLeftIndex = entryPtr->scanMarkIndex = 0;  
         entryPtr->scanMarkX = x;  
     }  
   
     if (newLeftIndex != entryPtr->leftIndex) {  
         entryPtr->leftIndex = newLeftIndex;  
         entryPtr->flags |= UPDATE_SCROLLBAR;  
         EntryComputeGeometry(entryPtr);  
         if (newLeftIndex != entryPtr->leftIndex) {  
             entryPtr->scanMarkIndex = entryPtr->leftIndex;  
             entryPtr->scanMarkX = x;  
         }  
         EventuallyRedraw(entryPtr);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntrySelectTo --  
  *  
  *      Modify the selection by moving its un-anchored end.  This could  
  *      make the selection either larger or smaller.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The selection changes.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntrySelectTo(entryPtr, index)  
     Entry *entryPtr;            /* Information about widget. */  
     int index;                  /* Character index of element that is to  
                                  * become the "other" end of the selection. */  
 {  
     int newFirst, newLast;  
   
     /*  
      * Grab the selection if we don't own it already.  
      */  
   
     if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {  
         Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,  
                 (ClientData) entryPtr);  
         entryPtr->flags |= GOT_SELECTION;  
     }  
   
     /*  
      * Pick new starting and ending points for the selection.  
      */  
   
     if (entryPtr->selectAnchor > entryPtr->numChars) {  
         entryPtr->selectAnchor = entryPtr->numChars;  
     }  
     if (entryPtr->selectAnchor <= index) {  
         newFirst = entryPtr->selectAnchor;  
         newLast = index;  
     } else {  
         newFirst = index;  
         newLast = entryPtr->selectAnchor;  
         if (newLast < 0) {  
             newFirst = newLast = -1;  
         }  
     }  
     if ((entryPtr->selectFirst == newFirst)  
             && (entryPtr->selectLast == newLast)) {  
         return;  
     }  
     entryPtr->selectFirst = newFirst;  
     entryPtr->selectLast = newLast;  
     EventuallyRedraw(entryPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryFetchSelection --  
  *  
  *      This procedure is called back by Tk when the selection is  
  *      requested by someone.  It returns part or all of the selection  
  *      in a buffer provided by the caller.  
  *  
  * Results:  
  *      The return value is the number of non-NULL bytes stored  
  *      at buffer.  Buffer is filled (or partially filled) with a  
  *      NULL-terminated string containing part or all of the selection,  
  *      as given by offset and maxBytes.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static int  
 EntryFetchSelection(clientData, offset, buffer, maxBytes)  
     ClientData clientData;      /* Information about entry widget. */  
     int offset;                 /* Byte offset within selection of first  
                                  * character to be returned. */  
     char *buffer;               /* Location in which to place selection. */  
     int maxBytes;               /* Maximum number of bytes to place at  
                                  * buffer, not including terminating NULL  
                                  * character. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
     int byteCount;  
     char *string, *selStart, *selEnd;  
   
     if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {  
         return -1;  
     }  
     string = entryPtr->displayString;  
     selStart = Tcl_UtfAtIndex(string, entryPtr->selectFirst);  
     selEnd = Tcl_UtfAtIndex(selStart,  
             entryPtr->selectLast - entryPtr->selectFirst);  
     byteCount = selEnd - selStart - offset;  
     if (byteCount > maxBytes) {  
         byteCount = maxBytes;  
     }  
     if (byteCount <= 0) {  
         return 0;  
     }  
     memcpy(buffer, selStart + offset, (size_t) byteCount);  
     buffer[byteCount] = '\0';  
     return byteCount;  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryLostSelection --  
  *  
  *      This procedure is called back by Tk when the selection is  
  *      grabbed away from an entry widget.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The existing selection is unhighlighted, and the window is  
  *      marked as not containing a selection.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryLostSelection(clientData)  
     ClientData clientData;      /* Information about entry widget. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
   
     entryPtr->flags &= ~GOT_SELECTION;  
   
     /*  
      * On Windows and Mac systems, we want to remember the selection  
      * for the next time the focus enters the window.  On Unix, we need  
      * to clear the selection since it is always visible.  
      */  
   
 #ifdef ALWAYS_SHOW_SELECTION  
     if ((entryPtr->selectFirst >= 0) && entryPtr->exportSelection) {  
         entryPtr->selectFirst = -1;  
         entryPtr->selectLast = -1;  
         EventuallyRedraw(entryPtr);  
     }  
 #endif  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EventuallyRedraw --  
  *  
  *      Ensure that an entry is eventually redrawn on the display.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      Information gets redisplayed.  Right now we don't do selective  
  *      redisplays:  the whole window will be redrawn.  This doesn't  
  *      seem to hurt performance noticeably, but if it does then this  
  *      could be changed.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EventuallyRedraw(entryPtr)  
     Entry *entryPtr;            /* Information about widget. */  
 {  
     if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {  
         return;  
     }  
   
     /*  
      * Right now we don't do selective redisplays:  the whole window  
      * will be redrawn.  This doesn't seem to hurt performance noticeably,  
      * but if it does then this could be changed.  
      */  
   
     if (!(entryPtr->flags & REDRAW_PENDING)) {  
         entryPtr->flags |= REDRAW_PENDING;  
         Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryVisibleRange --  
  *  
  *      Return information about the range of the entry that is  
  *      currently visible.  
  *  
  * Results:  
  *      *firstPtr and *lastPtr are modified to hold fractions between  
  *      0 and 1 identifying the range of characters visible in the  
  *      entry.  
  *  
  * Side effects:  
  *      None.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryVisibleRange(entryPtr, firstPtr, lastPtr)  
     Entry *entryPtr;            /* Information about widget. */  
     double *firstPtr;           /* Return position of first visible  
                                  * character in widget. */  
     double *lastPtr;            /* Return position of char just after last  
                                  * visible one. */  
 {  
     int charsInWindow;  
   
     if (entryPtr->numChars == 0) {  
         *firstPtr = 0.0;  
         *lastPtr = 1.0;  
     } else {  
         charsInWindow = Tk_PointToChar(entryPtr->textLayout,  
                 Tk_Width(entryPtr->tkwin) - entryPtr->inset  
                         - entryPtr->layoutX - 1, 0);  
         if (charsInWindow < entryPtr->numChars) {  
             charsInWindow++;  
         }  
         charsInWindow -= entryPtr->leftIndex;  
         if (charsInWindow == 0) {  
             charsInWindow = 1;  
         }  
   
         *firstPtr = (double) entryPtr->leftIndex / entryPtr->numChars;  
         *lastPtr = (double) (entryPtr->leftIndex + charsInWindow)  
                 / entryPtr->numChars;  
     }  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryUpdateScrollbar --  
  *  
  *      This procedure is invoked whenever information has changed in  
  *      an entry in a way that would invalidate a scrollbar display.  
  *      If there is an associated scrollbar, then this procedure updates  
  *      it by invoking a Tcl command.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      A Tcl command is invoked, and an additional command may be  
  *      invoked to process errors in the command.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryUpdateScrollbar(entryPtr)  
     Entry *entryPtr;                    /* Information about widget. */  
 {  
     char args[TCL_DOUBLE_SPACE * 2];  
     int code;  
     double first, last;  
     Tcl_Interp *interp;  
   
     if (entryPtr->scrollCmd == NULL) {  
         return;  
     }  
   
     interp = entryPtr->interp;  
     Tcl_Preserve((ClientData) interp);  
     EntryVisibleRange(entryPtr, &first, &last);  
     sprintf(args, " %g %g", first, last);  
     code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);  
     if (code != TCL_OK) {  
         Tcl_AddErrorInfo(interp,  
                 "\n    (horizontal scrolling command executed by entry)");  
         Tcl_BackgroundError(interp);  
     }  
     Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);  
     Tcl_Release((ClientData) interp);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryBlinkProc --  
  *  
  *      This procedure is called as a timer handler to blink the  
  *      insertion cursor off and on.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The cursor gets turned on or off, redisplay gets invoked,  
  *      and this procedure reschedules itself.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryBlinkProc(clientData)  
     ClientData clientData;      /* Pointer to record describing entry. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
   
     if ((entryPtr->state == STATE_DISABLED) ||  
             !(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {  
         return;  
     }  
     if (entryPtr->flags & CURSOR_ON) {  
         entryPtr->flags &= ~CURSOR_ON;  
         entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(  
                 entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);  
     } else {  
         entryPtr->flags |= CURSOR_ON;  
         entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(  
                 entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);  
     }  
     EventuallyRedraw(entryPtr);  
 }  
   
 /*  
  *----------------------------------------------------------------------  
  *  
  * EntryFocusProc --  
  *  
  *      This procedure is called whenever the entry gets or loses the  
  *      input focus.  It's also called whenever the window is reconfigured  
  *      while it has the focus.  
  *  
  * Results:  
  *      None.  
  *  
  * Side effects:  
  *      The cursor gets turned on or off.  
  *  
  *----------------------------------------------------------------------  
  */  
   
 static void  
 EntryFocusProc(entryPtr, gotFocus)  
     Entry *entryPtr;            /* Entry that got or lost focus. */  
     int gotFocus;               /* 1 means window is getting focus, 0 means  
                                  * it's losing it. */  
 {  
     Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);  
     if (gotFocus) {  
         entryPtr->flags |= GOT_FOCUS | CURSOR_ON;  
         if (entryPtr->insertOffTime != 0) {  
             entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(  
                     entryPtr->insertOnTime, EntryBlinkProc,  
                     (ClientData) entryPtr);  
         }  
         if (entryPtr->validate == VALIDATE_ALL ||  
             entryPtr->validate == VALIDATE_FOCUS ||  
             entryPtr->validate == VALIDATE_FOCUSIN) {  
             EntryValidateChange(entryPtr, (char *) NULL,  
                                 entryPtr->string, -1, VALIDATE_FOCUSIN);  
         }  
     } else {  
         entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);  
         entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;  
         if (entryPtr->validate == VALIDATE_ALL ||  
             entryPtr->validate == VALIDATE_FOCUS ||  
             entryPtr->validate == VALIDATE_FOCUSOUT) {  
             EntryValidateChange(entryPtr, (char *) NULL,  
                                 entryPtr->string, -1, VALIDATE_FOCUSOUT);  
         }  
     }  
     EventuallyRedraw(entryPtr);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * EntryTextVarProc --  
  *  
  *      This procedure is invoked when someone changes the variable  
  *      whose contents are to be displayed in an entry.  
  *  
  * Results:  
  *      NULL is always returned.  
  *  
  * Side effects:  
  *      The text displayed in the entry will change to match the  
  *      variable.  
  *  
  *--------------------------------------------------------------  
  */  
   
         /* ARGSUSED */  
 static char *  
 EntryTextVarProc(clientData, interp, name1, name2, flags)  
     ClientData clientData;      /* Information about button. */  
     Tcl_Interp *interp;         /* Interpreter containing variable. */  
     char *name1;                /* Not used. */  
     char *name2;                /* Not used. */  
     int flags;                  /* Information about what happened. */  
 {  
     Entry *entryPtr = (Entry *) clientData;  
     char *value;  
   
     /*  
      * If the variable is unset, then immediately recreate it unless  
      * the whole interpreter is going away.  
      */  
   
     if (flags & TCL_TRACE_UNSETS) {  
         if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {  
             Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,  
                     TCL_GLOBAL_ONLY);  
             Tcl_TraceVar(interp, entryPtr->textVarName,  
                     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,  
                     EntryTextVarProc, clientData);  
         }  
         return (char *) NULL;  
     }  
   
     /*  
      * Update the entry's text with the value of the variable, unless  
      * the entry already has that value (this happens when the variable  
      * changes value because we changed it because someone typed in  
      * the entry).  
      */  
   
     value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);  
     if (value == NULL) {  
         value = "";  
     }  
     EntrySetValue(entryPtr, value);  
     return (char *) NULL;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * EntryValidate --  
  *  
  *      This procedure is invoked when any character is added or  
  *      removed from the entry widget, or a focus has trigerred validation.  
  *  
  * Results:  
  *      TCL_OK if the validatecommand passes the new string.  
  *      TCL_BREAK if the vcmd executed OK, but rejects the string.  
  *      TCL_ERROR if an error occurred while executing the vcmd  
  *      or a valid Tcl_Bool is not returned.  
  *  
  * Side effects:  
  *      An error condition may arise  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 EntryValidate(entryPtr, cmd)  
      register Entry *entryPtr;  /* Entry that needs validation. */  
      register char *cmd;        /* Validation command (NULL-terminated  
                                  * string). */  
 {  
     register Tcl_Interp *interp = entryPtr->interp;  
     int code, bool;  
   
     code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);  
   
     if (code != TCL_OK && code != TCL_RETURN) {  
         Tcl_AddErrorInfo(interp,  
                          "\n\t(in validation command executed by entry)");  
         Tcl_BackgroundError(interp);  
         return TCL_ERROR;  
     }  
   
     if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),  
                               &bool) != TCL_OK) {  
         Tcl_AddErrorInfo(interp,  
                  "\nvalid boolean not returned by validation command");  
         Tcl_BackgroundError(interp);  
         Tcl_SetResult(interp, NULL, 0);  
         return TCL_ERROR;  
     }  
   
     Tcl_SetResult(interp, NULL, 0);  
     return (bool ? TCL_OK : TCL_BREAK);  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * EntryValidateChange --  
  *  
  *      This procedure is invoked when any character is added or  
  *      removed from the entry widget, or a focus has trigerred validation.  
  *  
  * Results:  
  *      TCL_OK if the validatecommand accepts the new string,  
  *      TCL_ERROR if any problems occured with validatecommand.  
  *  
  * Side effects:  
  *      The insertion/deletion may be aborted, and the  
  *      validatecommand might turn itself off (if an error  
  *      or loop condition arises).  
  *  
  *--------------------------------------------------------------  
  */  
   
 static int  
 EntryValidateChange(entryPtr, change, new, index, type)  
      register Entry *entryPtr;  /* Entry that needs validation. */  
      char *change;              /* Characters to be added/deleted  
                                  * (NULL-terminated string). */  
      char *new;                 /* Potential new value of entry string */  
      int index;                 /* index of insert/delete, -1 otherwise */  
      int type;                  /* forced, delete, insert,  
                                  * focusin or focusout */  
 {  
     int code, varValidate = (entryPtr->flags & VALIDATE_VAR);  
     char *p;  
     Tcl_DString script;  
       
     if (entryPtr->validateCmd == NULL ||  
         entryPtr->validate == VALIDATE_NONE) {  
         return (varValidate ? TCL_ERROR : TCL_OK);  
     }  
   
     /*  
      * If we're already validating, then we're hitting a loop condition  
      * Return and set validate to 0 to disallow further validations  
      * and prevent current validation from finishing  
      */  
     if (entryPtr->flags & VALIDATING) {  
         entryPtr->validate = VALIDATE_NONE;  
         return (varValidate ? TCL_ERROR : TCL_OK);  
     }  
   
     entryPtr->flags |= VALIDATING;  
   
     /*  
      * Now form command string and run through the -validatecommand  
      */  
   
     Tcl_DStringInit(&script);  
     ExpandPercents(entryPtr, entryPtr->validateCmd,  
             change, new, index, type, &script);  
     Tcl_DStringAppend(&script, "", 1);  
   
     p = Tcl_DStringValue(&script);  
     code = EntryValidate(entryPtr, p);  
     Tcl_DStringFree(&script);  
   
     /*  
      * If e->validate has become VALIDATE_NONE during the validation, or  
      * we now have VALIDATE_VAR set (from EntrySetValue) and didn't before,  
      * it means that a loop condition almost occured.  Do not allow  
      * this validation result to finish.  
      */  
     if (entryPtr->validate == VALIDATE_NONE  
             || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) {  
         code = TCL_ERROR;  
     }  
     /*  
      * If validate will return ERROR, then disallow further validations  
      * Otherwise, if it didn't accept the new string (returned TCL_BREAK)  
      * then eval the invalidCmd (if it's set)  
      */  
     if (code == TCL_ERROR) {  
         entryPtr->validate = VALIDATE_NONE;  
     } else if (code == TCL_BREAK) {  
         /*  
          * If we were doing forced validation (like via a variable  
          * trace) and the command returned 0, the we turn off validation  
          * because we assume that textvariables have precedence in  
          * managing the value.  We also don't call the invcmd, as it  
          * may want to do entry manipulation which the setting of the  
          * var will later wipe anyway.  
          */  
         if (varValidate) {  
             entryPtr->validate = VALIDATE_NONE;  
         } else if (entryPtr->invalidCmd != NULL) {  
             Tcl_DStringInit(&script);  
             ExpandPercents(entryPtr, entryPtr->invalidCmd,  
                            change, new, index, type, &script);  
             Tcl_DStringAppend(&script, "", 1);  
             p = Tcl_DStringValue(&script);  
             if (Tcl_EvalEx(entryPtr->interp, p, -1,  
                     TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT) != TCL_OK) {  
                 Tcl_AddErrorInfo(entryPtr->interp,  
                                  "\n\t(in invalidcommand executed by entry)");  
                 Tcl_BackgroundError(entryPtr->interp);  
                 code = TCL_ERROR;  
                 entryPtr->validate = VALIDATE_NONE;  
             }  
             Tcl_DStringFree(&script);  
         }  
     }  
   
     entryPtr->flags &= ~VALIDATING;  
   
     return code;  
 }  
   
 /*  
  *--------------------------------------------------------------  
  *  
  * ExpandPercents --  
  *  
  *      Given a command and an event, produce a new command  
  *      by replacing % constructs in the original command  
  *      with information from the X event.  
  *  
  * Results:  
  *      The new expanded command is appended to the dynamic string  
  *      given by dsPtr.  
  *  
  * Side effects:  
  *      None.  
  *  
  *--------------------------------------------------------------  
  */  
   
 static void  
 ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)  
      register Entry *entryPtr;  /* Entry that needs validation. */  
      register char *before;     /* Command containing percent  
                                  * expressions to be replaced. */  
      char *change;              /* Characters to added/deleted  
                                  * (NULL-terminated string). */  
      char *new;                 /* Potential new value of entry string */  
      int index;                 /* index of insert/delete */  
      int type;                  /* INSERT or DELETE */  
      Tcl_DString *dsPtr;        /* Dynamic string in which to append  
                                  * new command. */  
 {  
     int spaceNeeded, cvtFlags;  /* Used to substitute string as proper Tcl  
                                  * list element. */  
     int number, length;  
     register char *string;  
     Tcl_UniChar ch;  
     char numStorage[2*TCL_INTEGER_SPACE];  
   
     while (1) {  
         if (*before == '\0') {  
             break;  
         }  
         /*  
          * Find everything up to the next % character and append it  
          * to the result string.  
          */  
   
         string = before;  
         /* No need to convert '%', as it is in ascii range */  
         string = Tcl_UtfFindFirst(before, '%');  
         if (string == (char *) NULL) {  
             Tcl_DStringAppend(dsPtr, before, -1);  
             break;  
         } else if (string != before) {  
             Tcl_DStringAppend(dsPtr, before, string-before);  
             before = string;  
         }  
   
         /*  
          * There's a percent sequence here.  Process it.  
          */  
   
         before++; /* skip over % */  
         if (*before != '\0') {  
             before += Tcl_UtfToUniChar(before, &ch);  
         } else {  
             ch = '%';  
         }  
         switch (ch) {  
         case 'd': /* Type of call that caused validation */  
             switch (type) {  
             case VALIDATE_INSERT:  
                 number = 1;  
                 break;  
             case VALIDATE_DELETE:  
                 number = 0;  
                 break;  
             default:  
                 number = -1;  
                 break;  
             }  
             sprintf(numStorage, "%d", number);  
             string = numStorage;  
             break;  
         case 'i': /* index of insert/delete */  
             sprintf(numStorage, "%d", index);  
             string = numStorage;  
             break;  
         case 'P': /* 'Peeked' new value of the string */  
             string = new;  
             break;  
         case 's': /* Current string value of entry */  
             string = entryPtr->string;  
             break;  
         case 'S': /* string to be inserted/deleted, if any */  
             string = change;  
             break;  
         case 'v': /* type of validation currently set */  
             string = validateStrings[entryPtr->validate];  
             break;  
         case 'V': /* type of validation in effect */  
             switch (type) {  
             case VALIDATE_INSERT:  
             case VALIDATE_DELETE:  
                 string = validateStrings[VALIDATE_KEY];  
                 break;  
             case VALIDATE_FORCED:  
                 string = "forced";  
                 break;  
             default:  
                 string = validateStrings[type];  
                 break;  
             }  
             break;  
         case 'W': /* widget name */  
             string = Tk_PathName(entryPtr->tkwin);  
             break;  
         default:  
             length = Tcl_UniCharToUtf(ch, numStorage);  
             numStorage[length] = '\0';  
             string = numStorage;  
             break;  
         }  
   
         spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);  
         length = Tcl_DStringLength(dsPtr);  
         Tcl_DStringSetLength(dsPtr, length + spaceNeeded);  
         spaceNeeded = Tcl_ConvertCountedElement(string, -1,  
                 Tcl_DStringValue(dsPtr) + length,  
                 cvtFlags | TCL_DONT_USE_BRACES);  
         Tcl_DStringSetLength(dsPtr, length + spaceNeeded);  
     }  
 }  
   
   
 /* $History: tkEntry.c $  
  *  
  * *****************  Version 1  *****************  
  * User: Dtashley     Date: 1/02/01    Time: 2:39a  
  * Created in $/IjuScripter, IjuConsole/Source/Tk Base  
  * Initial check-in.  
  */  
   
 /* End of TKENTRY.C */  
1    /* $Header$ */
2    
3    /*
4     * tkEntry.c --
5     *
6     *      This module implements entry widgets for the Tk
7     *      toolkit.  An entry displays a string and allows
8     *      the string to be edited.
9     *
10     * Copyright (c) 1990-1994 The Regents of the University of California.
11     * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12     *
13     * See the file "license.terms" for information on usage and redistribution
14     * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15     *
16     * RCS: @(#) $Id: tkentry.c,v 1.1.1.1 2001/06/13 04:59:57 dtashley Exp $
17     */
18    
19    #include "tkInt.h"
20    #include "default.h"
21    
22    /*
23     * A data structure of the following type is kept for each entry
24     * widget managed by this file:
25     */
26    
27    typedef struct {
28        Tk_Window tkwin;            /* Window that embodies the entry. NULL
29                                     * means that the window has been destroyed
30                                     * but the data structures haven't yet been
31                                     * cleaned up.*/
32        Display *display;           /* Display containing widget.  Used, among
33                                     * other things, so that resources can be
34                                     * freed even after tkwin has gone away. */
35        Tcl_Interp *interp;         /* Interpreter associated with entry. */
36        Tcl_Command widgetCmd;      /* Token for entry's widget command. */
37        Tk_OptionTable optionTable; /* Table that defines configuration options
38                                     * available for this widget. */
39    
40    
41        /*
42         * Fields that are set by widget commands other than "configure".
43         */
44        
45        char *string;               /* Pointer to storage for string;
46                                     * NULL-terminated;  malloc-ed. */
47        int insertPos;              /* Character index before which next typed
48                                     * character will be inserted. */
49    
50        /*
51         * Information about what's selected, if any.
52         */
53    
54        int selectFirst;            /* Character index of first selected
55                                     * character (-1 means nothing selected. */
56        int selectLast;             /* Character index just after last selected
57                                     * character (-1 means nothing selected. */
58        int selectAnchor;           /* Fixed end of selection (i.e. "select to"
59                                     * operation will use this as one end of the
60                                     * selection). */
61    
62        /*
63         * Information for scanning:
64         */
65    
66        int scanMarkX;              /* X-position at which scan started (e.g.
67                                     * button was pressed here). */
68        int scanMarkIndex;          /* Character index of character that was at
69                                     * left of window when scan started. */
70    
71        /*
72         * Configuration settings that are updated by Tk_ConfigureWidget.
73         */
74    
75        Tk_3DBorder normalBorder;   /* Used for drawing border around whole
76                                     * window, plus used for background. */
77        int borderWidth;            /* Width of 3-D border around window. */
78        Tk_Cursor cursor;           /* Current cursor for window, or None. */
79        int exportSelection;        /* Non-zero means tie internal entry selection
80                                     * to X selection. */
81        Tk_Font tkfont;             /* Information about text font, or NULL. */
82        XColor *fgColorPtr;         /* Text color in normal mode. */
83        XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
84                                     * area when highlight is off. */
85        XColor *highlightColorPtr;  /* Color for drawing traversal highlight. */
86        int highlightWidth;         /* Width in pixels of highlight to draw
87                                     * around widget when it has the focus.
88                                     * <= 0 means don't draw a highlight. */
89        Tk_3DBorder insertBorder;   /* Used to draw vertical bar for insertion
90                                     * cursor. */
91        int insertBorderWidth;      /* Width of 3-D border around insert cursor. */
92        int insertOffTime;          /* Number of milliseconds cursor should spend
93                                     * in "off" state for each blink. */
94        int insertOnTime;           /* Number of milliseconds cursor should spend
95                                     * in "on" state for each blink. */
96        int insertWidth;            /* Total width of insert cursor. */
97        Tk_Justify justify;         /* Justification to use for text within
98                                     * window. */
99        int relief;                 /* 3-D effect: TK_RELIEF_RAISED, etc. */
100        Tk_3DBorder selBorder;      /* Border and background for selected
101                                     * characters. */
102        int selBorderWidth;         /* Width of border around selection. */
103        XColor *selFgColorPtr;      /* Foreground color for selected text. */
104        char *showChar;             /* Value of -show option.  If non-NULL, first
105                                     * character is used for displaying all
106                                     * characters in entry.  Malloc'ed. */
107        int state;                  /* Normal or disabled.  Entry is read-only
108                                     * when disabled. */
109        char *textVarName;          /* Name of variable (malloc'ed) or NULL.
110                                     * If non-NULL, entry's string tracks the
111                                     * contents of this variable and vice versa. */
112        char *takeFocus;            /* Value of -takefocus option;  not used in
113                                     * the C code, but used by keyboard traversal
114                                     * scripts.  Malloc'ed, but may be NULL. */
115        int prefWidth;              /* Desired width of window, measured in
116                                     * average characters. */
117        char *scrollCmd;            /* Command prefix for communicating with
118                                     * scrollbar(s).  Malloc'ed.  NULL means
119                                     * no command to issue. */
120    
121        /*
122         * Fields whose values are derived from the current values of the
123         * configuration settings above.
124         */
125    
126        int numBytes;               /* Length of string in bytes. */
127        int numChars;               /* Length of string in characters.  Both
128                                     * string and displayString have the same
129                                     * character length, but may have different
130                                     * byte lengths due to being made from
131                                     * different UTF-8 characters. */
132        char *displayString;        /* String to use when displaying.  This may
133                                     * be a pointer to string, or a pointer to
134                                     * malloced memory with the same character
135                                     * length as string but whose characters
136                                     * are all equal to showChar. */
137        int numDisplayBytes;        /* Length of displayString in bytes. */
138        int inset;                  /* Number of pixels on the left and right
139                                     * sides that are taken up by XPAD, borderWidth
140                                     * (if any), and highlightWidth (if any). */
141        Tk_TextLayout textLayout;   /* Cached text layout information. */
142        int layoutX, layoutY;       /* Origin for layout. */
143        int leftX;                  /* X position at which character at leftIndex
144                                     * is drawn (varies depending on justify). */
145        int leftIndex;              /* Character index of left-most character
146                                     * visible in window. */
147        Tcl_TimerToken insertBlinkHandler;
148                                    /* Timer handler used to blink cursor on and
149                                     * off. */
150        GC textGC;                  /* For drawing normal text. */
151        GC selTextGC;               /* For drawing selected text. */
152        GC highlightGC;             /* For drawing traversal highlight. */
153        int avgWidth;               /* Width of average character. */
154        int flags;                  /* Miscellaneous flags;  see below for
155                                     * definitions. */
156        Tk_TSOffset tsoffset;
157    
158        char *validateCmd;          /* Command prefix to use when invoking
159                                     * validate command.  NULL means don't
160                                     * invoke commands.  Malloc'ed. */
161        int validate;               /* Non-zero means try to validate */
162        char *invalidCmd;           /* Command called when a validation returns 0
163                                     * (successfully fails), defaults to {}. */
164    } Entry;
165    
166    /*
167     * Assigned bits of "flags" fields of Entry structures, and what those
168     * bits mean:
169     *
170     * REDRAW_PENDING:              Non-zero means a DoWhenIdle handler has
171     *                              already been queued to redisplay the entry.
172     * BORDER_NEEDED:               Non-zero means 3-D border must be redrawn
173     *                              around window during redisplay.  Normally
174     *                              only text portion needs to be redrawn.
175     * CURSOR_ON:                   Non-zero means insert cursor is displayed at
176     *                              present.  0 means it isn't displayed.
177     * GOT_FOCUS:                   Non-zero means this window has the input
178     *                              focus.
179     * UPDATE_SCROLLBAR:            Non-zero means scrollbar should be updated
180     *                              during next redisplay operation.
181     * GOT_SELECTION:               Non-zero means we've claimed the selection.
182     * ENTRY_DELETED:               This entry has been effectively destroyed.
183     * VALIDATING:                  Non-zero means we are in a validateCmd
184     * VALIDATE_VAR:                Non-zero means we are attempting to validate
185     *                              the entry's textvariable with validateCmd
186     * VALIDATE_ABORT:              Non-zero if validatecommand signals an abort
187     *                              for current procedure and make no changes
188     */
189    
190    #define REDRAW_PENDING          1
191    #define BORDER_NEEDED           2
192    #define CURSOR_ON               4
193    #define GOT_FOCUS               8
194    #define UPDATE_SCROLLBAR        0x10
195    #define GOT_SELECTION           0x20
196    #define ENTRY_DELETED           0x40
197    #define VALIDATING              0x80
198    #define VALIDATE_VAR            0x100
199    #define VALIDATE_ABORT          0x200
200    
201    /*
202     * The following macro defines how many extra pixels to leave on each
203     * side of the text in the entry.
204     */
205    
206    #define XPAD 1
207    #define YPAD 1
208    
209    /*
210     * The following enum is used to define a type for the -state option
211     * of the Entry widget.  These values are used as indices into the
212     * string table below.
213     */
214    
215    enum state {
216        STATE_DISABLED, STATE_NORMAL
217    };
218    
219    static char *stateStrings[] = {
220        "disabled", "normal", (char *) NULL
221    };
222    
223    /*
224     * Definitions for -validate option values:
225     */
226    
227    static char *validateStrings[] = {
228        "all", "key", "focus", "focusin", "focusout", "none", (char *) NULL
229    };
230    enum validateType {
231        VALIDATE_ALL, VALIDATE_KEY, VALIDATE_FOCUS,
232        VALIDATE_FOCUSIN, VALIDATE_FOCUSOUT, VALIDATE_NONE,
233        /*
234         * These extra enums are for use with EntryValidateChange
235         */
236        VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT
237    };
238    #define DEF_ENTRY_VALIDATE      "none"
239    #define DEF_ENTRY_INVALIDCMD    ""
240    
241    /*
242     * Information used for argv parsing.
243     */
244    
245    static Tk_OptionSpec optionSpecs[] = {
246        {TK_OPTION_BORDER, "-background", "background", "Background",
247            DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder),
248            0, (ClientData) DEF_ENTRY_BG_MONO, 0},
249        {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
250            (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
251        {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
252            (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
253        {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
254            DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth),
255            0, 0, 0},
256        {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
257            DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor),
258            TK_OPTION_NULL_OK, 0, 0},
259        {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
260            "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1,
261            Tk_Offset(Entry, exportSelection), 0, 0, 0},
262        {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
263            (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
264        {TK_OPTION_FONT, "-font", "font", "Font",
265            DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0},
266        {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
267            DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0,
268            0, 0},
269        {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
270            "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
271            -1, Tk_Offset(Entry, highlightBgColorPtr),
272            0, 0, 0},
273        {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
274            DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr),
275            0, 0, 0},
276        {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
277            "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1,
278            Tk_Offset(Entry, highlightWidth), 0, 0, 0},
279        {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground",
280            DEF_ENTRY_INSERT_BG,
281            -1, Tk_Offset(Entry, insertBorder),
282            0, 0, 0},
283        {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth",
284            "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1,
285            Tk_Offset(Entry, insertBorderWidth), 0,
286            (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0},
287        {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime",
288            DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime),
289            0, 0, 0},
290        {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime",
291            DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime),
292            0, 0, 0},
293        {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
294            DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth),
295            0, 0, 0},
296        {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand",
297            DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd),
298            TK_OPTION_NULL_OK, 0, 0},
299        {TK_OPTION_SYNONYM, "-invcmd", (char *) NULL, (char *) NULL,
300            (char *) NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0},
301        {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
302            DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0},
303        {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
304            DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief),
305            0, 0, 0},
306        {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
307            DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder),
308            0, (ClientData) DEF_ENTRY_SELECT_MONO, 0},
309        {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth",
310            "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1,
311            Tk_Offset(Entry, selBorderWidth),
312            0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0},
313        {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
314            DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr),
315            0, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0},
316        {TK_OPTION_STRING, "-show", "show", "Show",
317            DEF_ENTRY_SHOW, -1, Tk_Offset(Entry, showChar),
318            TK_OPTION_NULL_OK, 0, 0},
319        {TK_OPTION_STRING_TABLE, "-state", "state", "State",
320            DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state),
321            0, (ClientData) stateStrings, 0},
322        {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
323            DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus),
324            TK_OPTION_NULL_OK, 0, 0},
325        {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
326            DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName),
327            TK_OPTION_NULL_OK, 0, 0},
328        {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate",
329           DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate),
330           0, (ClientData) validateStrings, 0},
331        {TK_OPTION_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
332           (char *) NULL, -1, Tk_Offset(Entry, validateCmd),
333           TK_OPTION_NULL_OK, 0, 0},
334        {TK_OPTION_SYNONYM, "-vcmd", (char *) NULL, (char *) NULL,
335            (char *) NULL, 0, -1, 0, (ClientData) "-validatecommand", 0},
336        {TK_OPTION_INT, "-width", "width", "Width",
337            DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0},
338        {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
339            DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd),
340            TK_OPTION_NULL_OK, 0, 0},
341        {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
342            (char *) NULL, 0, -1, 0, 0, 0}
343    };
344    
345    /*
346     * Flags for GetEntryIndex procedure:
347     */
348    
349    #define ZERO_OK                 1
350    #define LAST_PLUS_ONE_OK        2
351    
352    /*
353     * The following tables define the entry widget commands (and sub-
354     * commands) and map the indexes into the string tables into
355     * enumerated types used to dispatch the entry widget command.
356     */
357    
358    static char *commandNames[] = {
359        "bbox", "cget", "configure", "delete", "get", "icursor", "index",
360        "insert", "scan", "selection", "validate", "xview", (char *) NULL
361    };
362    
363    enum command {
364        COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE,
365        COMMAND_GET, COMMAND_ICURSOR, COMMAND_INDEX, COMMAND_INSERT,
366        COMMAND_SCAN, COMMAND_SELECTION, COMMAND_VALIDATE, COMMAND_XVIEW
367    };
368    
369    static char *selCommandNames[] = {
370        "adjust", "clear", "from", "present", "range", "to", (char *) NULL
371    };
372    
373    enum selcommand {
374        SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM,
375        SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO
376    };
377    
378    /*
379     * Forward declarations for procedures defined later in this file:
380     */
381    
382    static int              ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
383                                Entry *entryPtr, int objc,
384                                Tcl_Obj *CONST objv[], int flags));
385    static void             DeleteChars _ANSI_ARGS_((Entry *entryPtr, int index,
386                                int count));
387    static void             DestroyEntry _ANSI_ARGS_((char *memPtr));
388    static void             DisplayEntry _ANSI_ARGS_((ClientData clientData));
389    static void             EntryBlinkProc _ANSI_ARGS_((ClientData clientData));
390    static void             EntryCmdDeletedProc _ANSI_ARGS_((
391                                ClientData clientData));
392    static void             EntryComputeGeometry _ANSI_ARGS_((Entry *entryPtr));
393    static void             EntryEventProc _ANSI_ARGS_((ClientData clientData,
394                                XEvent *eventPtr));
395    static void             EntryFocusProc _ANSI_ARGS_ ((Entry *entryPtr,
396                                int gotFocus));
397    static int              EntryFetchSelection _ANSI_ARGS_((ClientData clientData,
398                                int offset, char *buffer, int maxBytes));
399    static void             EntryLostSelection _ANSI_ARGS_((
400                                ClientData clientData));
401    static void             EventuallyRedraw _ANSI_ARGS_((Entry *entryPtr));
402    static void             EntryScanTo _ANSI_ARGS_((Entry *entryPtr, int y));
403    static void             EntrySetValue _ANSI_ARGS_((Entry *entryPtr,
404                                char *value));
405    static void             EntrySelectTo _ANSI_ARGS_((
406                                Entry *entryPtr, int index));
407    static char *           EntryTextVarProc _ANSI_ARGS_((ClientData clientData,
408                                Tcl_Interp *interp, char *name1, char *name2,
409                                int flags));
410    static void             EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
411    static int              EntryValidate _ANSI_ARGS_((Entry *entryPtr,
412                                char *cmd));
413    static int              EntryValidateChange _ANSI_ARGS_((Entry *entryPtr,
414                                char *change, char *new, int index, int type));
415    static void             ExpandPercents _ANSI_ARGS_((Entry *entryPtr,
416                                char *before, char *change, char *new,
417                                int index, int type, Tcl_DString *dsPtr));
418    static void             EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
419    static void             EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
420                                double *firstPtr, double *lastPtr));
421    static int              EntryWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
422                                Tcl_Interp *interp, int objc,
423                                Tcl_Obj *CONST objv[]));
424    static void             EntryWorldChanged _ANSI_ARGS_((
425                                ClientData instanceData));
426    static int              GetEntryIndex _ANSI_ARGS_((Tcl_Interp *interp,
427                                Entry *entryPtr, char *string, int *indexPtr));
428    static void             InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
429                                char *string));
430    
431    /*
432     * The structure below defines entry class behavior by means of procedures
433     * that can be invoked from generic window code.
434     */
435    
436    static TkClassProcs entryClass = {
437        NULL,                       /* createProc. */
438        EntryWorldChanged,          /* geometryProc. */
439        NULL                        /* modalProc. */
440    };
441    
442    
443    /*
444     *--------------------------------------------------------------
445     *
446     * Tk_EntryObjCmd --
447     *
448     *      This procedure is invoked to process the "entry" Tcl
449     *      command.  See the user documentation for details on what
450     *      it does.
451     *
452     * Results:
453     *      A standard Tcl result.
454     *
455     * Side effects:
456     *      See the user documentation.
457     *
458     *--------------------------------------------------------------
459     */
460    
461    int
462    Tk_EntryObjCmd(clientData, interp, objc, objv)
463        ClientData clientData;      /* Either NULL or pointer to option table. */
464        Tcl_Interp *interp;         /* Current interpreter. */
465        int objc;                   /* Number of arguments. */
466        Tcl_Obj *CONST objv[];      /* Argument objects. */
467    {
468        register Entry *entryPtr;
469        Tk_OptionTable optionTable;
470        Tk_Window tkwin;
471    
472        optionTable = (Tk_OptionTable) clientData;
473        if (optionTable == NULL) {
474            Tcl_CmdInfo info;
475            char *name;
476    
477            /*
478             * We haven't created the option table for this widget class
479             * yet.  Do it now and save the table as the clientData for
480             * the command, so we'll have access to it in future
481             * invocations of the command.
482             */
483    
484            optionTable = Tk_CreateOptionTable(interp, optionSpecs);
485            name = Tcl_GetString(objv[0]);
486            Tcl_GetCommandInfo(interp, name, &info);
487            info.objClientData = (ClientData) optionTable;
488            Tcl_SetCommandInfo(interp, name, &info);
489        }
490    
491        if (objc < 2) {
492            Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
493            return TCL_ERROR;
494        }
495    
496        tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
497                Tcl_GetString(objv[1]), (char *) NULL);
498        if (tkwin == NULL) {
499            return TCL_ERROR;
500        }
501    
502        /*
503         * Initialize the fields of the structure that won't be initialized
504         * by ConfigureEntry, or that ConfigureEntry requires to be
505         * initialized already (e.g. resource pointers).
506         */
507    
508        entryPtr                    = (Entry *) ckalloc(sizeof(Entry));
509        entryPtr->tkwin             = tkwin;
510        entryPtr->display           = Tk_Display(tkwin);
511        entryPtr->interp            = interp;
512        entryPtr->widgetCmd         = Tcl_CreateObjCommand(interp,
513                Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd,
514                (ClientData) entryPtr, EntryCmdDeletedProc);
515        entryPtr->optionTable       = optionTable;
516        entryPtr->string            = (char *) ckalloc(1);
517        entryPtr->string[0]         = '\0';
518        entryPtr->insertPos         = 0;
519        entryPtr->selectFirst       = -1;
520        entryPtr->selectLast        = -1;
521        entryPtr->selectAnchor      = 0;
522        entryPtr->scanMarkX         = 0;
523        entryPtr->scanMarkIndex     = 0;
524    
525        entryPtr->normalBorder      = NULL;
526        entryPtr->borderWidth       = 0;
527        entryPtr->cursor            = None;
528        entryPtr->exportSelection   = 1;
529        entryPtr->tkfont            = NULL;
530        entryPtr->fgColorPtr        = NULL;
531        entryPtr->highlightBgColorPtr       = NULL;
532        entryPtr->highlightColorPtr = NULL;
533        entryPtr->highlightWidth    = 0;
534        entryPtr->insertBorder      = NULL;
535        entryPtr->insertBorderWidth = 0;
536        entryPtr->insertOffTime     = 0;
537        entryPtr->insertOnTime      = 0;
538        entryPtr->insertWidth       = 0;
539        entryPtr->justify           = TK_JUSTIFY_LEFT;
540        entryPtr->relief            = TK_RELIEF_FLAT;
541        entryPtr->selBorder         = NULL;
542        entryPtr->selBorderWidth    = 0;
543        entryPtr->selFgColorPtr     = NULL;
544        entryPtr->showChar          = NULL;
545        entryPtr->state             = STATE_NORMAL;
546        entryPtr->textVarName       = NULL;
547        entryPtr->takeFocus         = NULL;
548        entryPtr->prefWidth         = 0;
549        entryPtr->scrollCmd         = NULL;
550        entryPtr->numBytes          = 0;
551        entryPtr->numChars          = 0;
552        entryPtr->displayString     = entryPtr->string;
553        entryPtr->numDisplayBytes   = 0;
554        entryPtr->inset             = XPAD;
555        entryPtr->textLayout        = NULL;
556        entryPtr->layoutX           = 0;
557        entryPtr->layoutY           = 0;
558        entryPtr->leftX             = 0;
559        entryPtr->leftIndex         = 0;
560        entryPtr->insertBlinkHandler        = (Tcl_TimerToken) NULL;
561        entryPtr->textGC            = None;
562        entryPtr->selTextGC         = None;
563        entryPtr->highlightGC       = None;
564        entryPtr->avgWidth          = 1;
565        entryPtr->flags             = 0;
566        entryPtr->validateCmd       = NULL;
567        entryPtr->validate          = VALIDATE_NONE;
568        entryPtr->invalidCmd        = NULL;
569    
570        Tk_SetClass(entryPtr->tkwin, "Entry");
571        TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
572        Tk_CreateEventHandler(entryPtr->tkwin,
573                ExposureMask|StructureNotifyMask|FocusChangeMask,
574                EntryEventProc, (ClientData) entryPtr);
575        Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
576                EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
577    
578        if ((Tk_InitOptions(interp, (char *) entryPtr, optionTable, tkwin)
579                != TCL_OK) ||
580                (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK)) {
581            Tk_DestroyWindow(entryPtr->tkwin);
582            return TCL_ERROR;
583        }
584        
585        Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC);
586        return TCL_OK;
587    }
588    
589    /*
590     *--------------------------------------------------------------
591     *
592     * EntryWidgetObjCmd --
593     *
594     *      This procedure is invoked to process the Tcl command
595     *      that corresponds to a widget managed by this module.
596     *      See the user documentation for details on what it does.
597     *
598     * Results:
599     *      A standard Tcl result.
600     *
601     * Side effects:
602     *      See the user documentation.
603     *
604     *--------------------------------------------------------------
605     */
606    
607    static int
608    EntryWidgetObjCmd(clientData, interp, objc, objv)
609        ClientData clientData;      /* Information about entry widget. */
610        Tcl_Interp *interp;         /* Current interpreter. */
611        int objc;                   /* Number of arguments. */
612        Tcl_Obj *CONST objv[];      /* Argument objects. */
613    {
614        Entry *entryPtr = (Entry *) clientData;
615        int cmdIndex, selIndex, result;
616        Tcl_Obj *objPtr;
617    
618        if (objc < 2) {
619            Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
620            return TCL_ERROR;
621        }
622        Tcl_Preserve((ClientData) entryPtr);
623    
624        /*
625         * Parse the widget command by looking up the second token in
626         * the list of valid command names.
627         */
628    
629        result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
630                "option", 0, &cmdIndex);
631        if (result != TCL_OK) {
632            return result;
633        }
634    
635        switch (cmdIndex) {
636            case COMMAND_BBOX: {
637                int index, x, y, width, height;
638                char buf[TCL_INTEGER_SPACE * 4];
639    
640                if (objc != 3) {
641                    Tcl_WrongNumArgs(interp, 2, objv, "index");
642                    goto error;
643                }
644                if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
645                        &index) != TCL_OK) {
646                    goto error;
647                }
648                if ((index == entryPtr->numChars) && (index > 0)) {
649                    index--;
650                }
651                Tk_CharBbox(entryPtr->textLayout, index, &x, &y,
652                        &width, &height);
653                sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX,
654                        y + entryPtr->layoutY, width, height);
655                Tcl_SetResult(interp, buf, TCL_VOLATILE);
656                break;
657            }
658            
659            case COMMAND_CGET: {
660                if (objc != 3) {
661                    Tcl_WrongNumArgs(interp, 2, objv, "option");
662                    goto error;
663                }
664                
665                objPtr = Tk_GetOptionValue(interp, (char *) entryPtr,
666                        entryPtr->optionTable, objv[2], entryPtr->tkwin);
667                if (objPtr == NULL) {
668                     goto error;
669                } else {
670                    Tcl_SetObjResult(interp, objPtr);
671                }
672                break;
673            }
674    
675            case COMMAND_CONFIGURE: {
676                if (objc <= 3) {
677                    objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr,
678                            entryPtr->optionTable,
679                            (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
680                            entryPtr->tkwin);
681                    if (objPtr == NULL) {
682                        goto error;
683                    } else {
684                        Tcl_SetObjResult(interp, objPtr);
685                    }
686                } else {
687                    result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0);
688                }
689                break;
690            }
691    
692            case COMMAND_DELETE: {
693                int first, last;
694    
695                if ((objc < 3) || (objc > 4)) {
696                    Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
697                    goto error;
698                }
699                if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
700                        &first) != TCL_OK) {
701                    goto error;
702                }
703                if (objc == 3) {
704                    last = first + 1;
705                } else {
706                    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]),
707                            &last) != TCL_OK) {
708                        goto error;
709                    }
710                }
711                if ((last >= first) && (entryPtr->state == STATE_NORMAL)) {
712                    DeleteChars(entryPtr, first, last - first);
713                }
714                break;
715            }
716    
717            case COMMAND_GET: {
718                if (objc != 2) {
719                    Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
720                    goto error;
721                }
722                Tcl_SetResult(interp, entryPtr->string, TCL_STATIC);
723                break;
724            }
725    
726            case COMMAND_ICURSOR: {
727                if (objc != 3) {
728                    Tcl_WrongNumArgs(interp, 2, objv, "pos");
729                    goto error;
730                }
731                if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
732                        &entryPtr->insertPos) != TCL_OK) {
733                    goto error;
734                }
735                EventuallyRedraw(entryPtr);
736                break;
737            }
738            
739            case COMMAND_INDEX: {
740                int index;
741    
742                if (objc != 3) {
743                    Tcl_WrongNumArgs(interp, 2, objv, "string");
744                    goto error;
745                }
746                if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
747                        &index) != TCL_OK) {
748                    goto error;
749                }
750                Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
751                break;
752            }
753    
754            case COMMAND_INSERT: {
755                int index;
756    
757                if (objc != 4) {
758                    Tcl_WrongNumArgs(interp, 2, objv, "index text");
759                    goto error;
760                }
761                if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
762                        &index) != TCL_OK) {
763                    goto error;
764                }
765                if (entryPtr->state == STATE_NORMAL) {
766                    InsertChars(entryPtr, index, Tcl_GetString(objv[3]));
767                }
768                break;
769            }
770    
771            case COMMAND_SCAN: {
772                int x;
773                char *minorCmd;
774    
775                if (objc != 4) {
776                    Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x");
777                    goto error;
778                }
779                if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
780                    goto error;
781                }
782    
783                minorCmd = Tcl_GetString(objv[2]);
784                if (minorCmd[0] == 'm'
785                        && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) {
786                    entryPtr->scanMarkX = x;
787                    entryPtr->scanMarkIndex = entryPtr->leftIndex;
788                } else if ((minorCmd[0] == 'd')
789                    && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) {
790                    EntryScanTo(entryPtr, x);
791                } else {
792                    Tcl_AppendResult(interp, "bad scan option \"",
793                            Tcl_GetString(objv[2]), "\": must be mark or dragto",
794                            (char *) NULL);
795                    goto error;
796                }
797                break;
798            }
799                
800            case COMMAND_SELECTION: {
801                int index, index2;
802    
803                if (objc < 3) {
804                    Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
805                    goto error;
806                }
807    
808                /*
809                 * Parse the selection sub-command, using the command
810                 * table "selCommandNames" defined above.
811                 */
812                
813                result = Tcl_GetIndexFromObj(interp, objv[2], selCommandNames,
814                        "selection option", 0, &selIndex);
815                if (result != TCL_OK) {
816                    goto error;
817                }
818    
819                switch(selIndex) {
820                    case SELECTION_ADJUST: {
821                        if (objc != 4) {
822                            Tcl_WrongNumArgs(interp, 3, objv, "index");
823                            goto error;
824                        }
825                        if (GetEntryIndex(interp, entryPtr,
826                                Tcl_GetString(objv[3]), &index) != TCL_OK) {
827                            goto error;
828                        }
829                        if (entryPtr->selectFirst >= 0) {
830                            int half1, half2;
831                    
832                            half1 = (entryPtr->selectFirst
833                                    + entryPtr->selectLast)/2;
834                            half2 = (entryPtr->selectFirst
835                                    + entryPtr->selectLast + 1)/2;
836                            if (index < half1) {
837                                entryPtr->selectAnchor = entryPtr->selectLast;
838                            } else if (index > half2) {
839                                entryPtr->selectAnchor = entryPtr->selectFirst;
840                            } else {
841                              /*
842                               * We're at about the halfway point in the
843                               * selection; just keep the existing anchor.
844                               */
845                            }
846                        }
847                        EntrySelectTo(entryPtr, index);
848                        break;
849                    }
850    
851                    case SELECTION_CLEAR: {
852                        if (objc != 3) {
853                            Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
854                            goto error;
855                        }
856                        if (entryPtr->selectFirst >= 0) {
857                            entryPtr->selectFirst = -1;
858                            entryPtr->selectLast = -1;
859                            EventuallyRedraw(entryPtr);
860                        }
861                        goto done;
862                    }
863    
864                    case SELECTION_FROM: {
865                        if (objc != 4) {
866                            Tcl_WrongNumArgs(interp, 3, objv, "index");
867                            goto error;
868                        }
869                        if (GetEntryIndex(interp, entryPtr,
870                                Tcl_GetString(objv[3]), &index) != TCL_OK) {
871                            goto error;
872                        }
873                        entryPtr->selectAnchor = index;
874                        break;
875                    }
876    
877                    case SELECTION_PRESENT: {
878                        if (objc != 3) {
879                            Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
880                            goto error;
881                        }
882                        if (entryPtr->selectFirst < 0) {
883                            Tcl_SetResult(interp, "0", TCL_STATIC);
884                        } else {
885                            Tcl_SetResult(interp, "1", TCL_STATIC);
886                        }
887                        goto done;
888                    }
889    
890                    case SELECTION_RANGE: {
891                        if (objc != 5) {
892                            Tcl_WrongNumArgs(interp, 3, objv, "start end");
893                            goto error;
894                        }
895                        if (GetEntryIndex(interp, entryPtr,
896                                Tcl_GetString(objv[3]), &index) != TCL_OK) {
897                            goto error;
898                        }
899                        if (GetEntryIndex(interp, entryPtr,
900                                Tcl_GetString(objv[4]),& index2) != TCL_OK) {
901                            goto error;
902                        }
903                        if (index >= index2) {
904                            entryPtr->selectFirst = -1;
905                            entryPtr->selectLast = -1;
906                        } else {
907                            entryPtr->selectFirst = index;
908                            entryPtr->selectLast = index2;
909                        }
910                        if (!(entryPtr->flags & GOT_SELECTION)
911                                && (entryPtr->exportSelection)) {
912                            Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
913                                    EntryLostSelection, (ClientData) entryPtr);
914                            entryPtr->flags |= GOT_SELECTION;
915                        }
916                        EventuallyRedraw(entryPtr);
917                        break;
918                    }
919                    
920                    case SELECTION_TO: {
921                        if (objc != 4) {
922                            Tcl_WrongNumArgs(interp, 3, objv, "index");
923                            goto error;
924                        }
925                        if (GetEntryIndex(interp, entryPtr,
926                                Tcl_GetString(objv[3]), &index) != TCL_OK) {
927                            goto error;
928                        }
929                        EntrySelectTo(entryPtr, index);
930                        break;
931                    }
932                }
933                break;
934            }
935    
936            case COMMAND_VALIDATE: {
937                int code;
938    
939                if (objc != 2) {
940                    Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
941                    goto error;
942                }
943                selIndex = entryPtr->validate;
944                entryPtr->validate = VALIDATE_ALL;
945                code = EntryValidateChange(entryPtr, (char *) NULL,
946                                           entryPtr->string, -1, VALIDATE_FORCED);
947                if (entryPtr->validate != VALIDATE_NONE) {
948                    entryPtr->validate = selIndex;
949                }
950                Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK)));
951                break;
952            }
953    
954            case COMMAND_XVIEW: {
955                int index;
956    
957                if (objc == 2) {
958                    double first, last;
959                    char buf[TCL_DOUBLE_SPACE * 2];
960                
961                    EntryVisibleRange(entryPtr, &first, &last);
962                    sprintf(buf, "%g %g", first, last);
963                    Tcl_SetResult(interp, buf, TCL_VOLATILE);
964                    goto done;
965                } else if (objc == 3) {
966                    if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
967                            &index) != TCL_OK) {
968                        goto error;
969                    }
970                } else {
971                    double fraction;
972                    int count;
973    
974                    index = entryPtr->leftIndex;
975                    switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction,
976                            &count)) {
977                        case TK_SCROLL_ERROR: {
978                            goto error;
979                        }
980                        case TK_SCROLL_MOVETO: {
981                            index = (int) ((fraction * entryPtr->numChars) + 0.5);
982                            break;
983                        }
984                        case TK_SCROLL_PAGES: {
985                            int charsPerPage;
986                        
987                            charsPerPage = ((Tk_Width(entryPtr->tkwin)
988                                    - 2 * entryPtr->inset)
989                                    / entryPtr->avgWidth) - 2;
990                            if (charsPerPage < 1) {
991                                charsPerPage = 1;
992                            }
993                            index += count * charsPerPage;
994                            break;
995                        }
996                        case TK_SCROLL_UNITS: {
997                            index += count;
998                            break;
999                        }
1000                    }
1001                }
1002                if (index >= entryPtr->numChars) {
1003                    index = entryPtr->numChars - 1;
1004                }
1005                if (index < 0) {
1006                    index = 0;
1007                }
1008                entryPtr->leftIndex = index;
1009                entryPtr->flags |= UPDATE_SCROLLBAR;
1010                EntryComputeGeometry(entryPtr);
1011                EventuallyRedraw(entryPtr);
1012                break;
1013            }
1014        }
1015    
1016        done:
1017        Tcl_Release((ClientData) entryPtr);
1018        return result;
1019    
1020        error:
1021        Tcl_Release((ClientData) entryPtr);
1022        return TCL_ERROR;
1023    }
1024    
1025    /*
1026     *----------------------------------------------------------------------
1027     *
1028     * DestroyEntry --
1029     *
1030     *      This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
1031     *      to clean up the internal structure of an entry at a safe time
1032     *      (when no-one is using it anymore).
1033     *
1034     * Results:
1035     *      None.
1036     *
1037     * Side effects:
1038     *      Everything associated with the entry is freed up.
1039     *
1040     *----------------------------------------------------------------------
1041     */
1042    
1043    static void
1044    DestroyEntry(memPtr)
1045        char *memPtr;               /* Info about entry widget. */
1046    {
1047        Entry *entryPtr = (Entry *) memPtr;
1048        entryPtr->flags |= ENTRY_DELETED;
1049    
1050        Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
1051        if (entryPtr->flags & REDRAW_PENDING) {
1052            Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
1053        }
1054    
1055        /*
1056         * Free up all the stuff that requires special handling, then
1057         * let Tk_FreeOptions handle all the standard option-related
1058         * stuff.
1059         */
1060    
1061        ckfree(entryPtr->string);
1062        if (entryPtr->textVarName != NULL) {
1063            Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
1064                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1065                    EntryTextVarProc, (ClientData) entryPtr);
1066        }
1067        if (entryPtr->textGC != None) {
1068            Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1069        }
1070        if (entryPtr->selTextGC != None) {
1071            Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1072        }
1073        Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
1074        if (entryPtr->displayString != entryPtr->string) {
1075            ckfree(entryPtr->displayString);
1076        }
1077        Tk_FreeTextLayout(entryPtr->textLayout);
1078        Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable,
1079                entryPtr->tkwin);
1080        entryPtr->tkwin = NULL;
1081        ckfree((char *) entryPtr);
1082    }
1083    
1084    /*
1085     *----------------------------------------------------------------------
1086     *
1087     * ConfigureEntry --
1088     *
1089     *      This procedure is called to process an argv/argc list, plus
1090     *      the Tk option database, in order to configure (or reconfigure)
1091     *      an entry widget.
1092     *
1093     * Results:
1094     *      The return value is a standard Tcl result.  If TCL_ERROR is
1095     *      returned, then the interp's result contains an error message.
1096     *
1097     * Side effects:
1098     *      Configuration information, such as colors, border width,
1099     *      etc. get set for entryPtr;  old resources get freed,
1100     *      if there were any.
1101     *
1102     *----------------------------------------------------------------------
1103     */
1104    
1105    static int
1106    ConfigureEntry(interp, entryPtr, objc, objv, flags)
1107        Tcl_Interp *interp;         /* Used for error reporting. */
1108        Entry *entryPtr;            /* Information about widget; may or may not
1109                                     * already have values for some fields. */
1110        int objc;                   /* Number of valid entries in argv. */
1111        Tcl_Obj *CONST objv[];      /* Argument objects. */
1112        int flags;                  /* Flags to pass to Tk_ConfigureWidget. */
1113    {
1114        Tk_SavedOptions savedOptions;
1115        Tcl_Obj *errorResult = NULL;
1116        int error;
1117        int oldExport;
1118    
1119        /*
1120         * Eliminate any existing trace on a variable monitored by the entry.
1121         */
1122    
1123        if (entryPtr->textVarName != NULL) {
1124            Tcl_UntraceVar(interp, entryPtr->textVarName,
1125                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1126                    EntryTextVarProc, (ClientData) entryPtr);
1127        }
1128    
1129        oldExport = entryPtr->exportSelection;
1130    
1131        for (error = 0; error <= 1; error++) {
1132            if (!error) {
1133                /*
1134                 * First pass: set options to new values.
1135                 */
1136    
1137                if (Tk_SetOptions(interp, (char *) entryPtr,
1138                        entryPtr->optionTable, objc, objv,
1139                        entryPtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {
1140                    continue;
1141                }
1142            } else {
1143                /*
1144                 * Second pass: restore options to old values.
1145                 */
1146    
1147                errorResult = Tcl_GetObjResult(interp);
1148                Tcl_IncrRefCount(errorResult);
1149                Tk_RestoreSavedOptions(&savedOptions);
1150            }
1151    
1152            /*
1153             * A few other options also need special processing, such as parsing
1154             * the geometry and setting the background from a 3-D border.
1155             */
1156    
1157            Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
1158    
1159            if (entryPtr->insertWidth <= 0) {
1160                entryPtr->insertWidth = 2;
1161            }
1162            if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) {
1163                entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
1164            }
1165    
1166            /*
1167             * Restart the cursor timing sequence in case the on-time or
1168             * off-time just changed.  Set validate temporarily to none,
1169             * so the configure doesn't cause it to be triggered.
1170             */
1171    
1172            if (entryPtr->flags & GOT_FOCUS) {
1173                int validate = entryPtr->validate;
1174                entryPtr->validate = VALIDATE_NONE;
1175                EntryFocusProc(entryPtr, 1);
1176                entryPtr->validate = validate;
1177            }
1178    
1179            /*
1180             * Claim the selection if we've suddenly started exporting it.
1181             */
1182    
1183            if (entryPtr->exportSelection && (!oldExport)
1184                    && (entryPtr->selectFirst != -1)
1185                    && !(entryPtr->flags & GOT_SELECTION)) {
1186                Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
1187                        (ClientData) entryPtr);
1188                entryPtr->flags |= GOT_SELECTION;
1189            }
1190    
1191            /*
1192             * Recompute the window's geometry and arrange for it to be
1193             * redisplayed.
1194             */
1195    
1196            Tk_SetInternalBorder(entryPtr->tkwin,
1197                    entryPtr->borderWidth + entryPtr->highlightWidth);
1198            if (entryPtr->highlightWidth <= 0) {
1199                entryPtr->highlightWidth = 0;
1200            }
1201            entryPtr->inset = entryPtr->highlightWidth
1202                    + entryPtr->borderWidth + XPAD;
1203            break;
1204        }
1205        if (!error) {
1206            Tk_FreeSavedOptions(&savedOptions);
1207        }
1208    
1209        /*
1210         * If the entry is tied to the value of a variable, then set up
1211         * a trace on the variable's value, create the variable if it doesn't
1212         * exist, and set the entry's value from the variable's value.
1213         */
1214    
1215        if (entryPtr->textVarName != NULL) {
1216            char *value;
1217    
1218            value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
1219            if (value == NULL) {
1220                EntryValueChanged(entryPtr);
1221            } else {
1222                EntrySetValue(entryPtr, value);
1223            }
1224            Tcl_TraceVar(interp, entryPtr->textVarName,
1225                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1226                    EntryTextVarProc, (ClientData) entryPtr);
1227        }
1228    
1229        EntryWorldChanged((ClientData) entryPtr);
1230        if (error) {
1231            Tcl_SetObjResult(interp, errorResult);
1232            Tcl_DecrRefCount(errorResult);
1233            return TCL_ERROR;
1234        } else {
1235            return TCL_OK;
1236        }
1237    }
1238    
1239    /*
1240     *---------------------------------------------------------------------------
1241     *
1242     * EntryWorldChanged --
1243     *
1244     *      This procedure is called when the world has changed in some
1245     *      way and the widget needs to recompute all its graphics contexts
1246     *      and determine its new geometry.
1247     *
1248     * Results:
1249     *      None.
1250     *
1251     * Side effects:
1252     *      Entry will be relayed out and redisplayed.
1253     *
1254     *---------------------------------------------------------------------------
1255     */
1256    
1257    static void
1258    EntryWorldChanged(instanceData)
1259        ClientData instanceData;    /* Information about widget. */
1260    {
1261        XGCValues gcValues;
1262        GC gc = None;
1263        unsigned long mask;
1264        Entry *entryPtr;
1265    
1266        entryPtr = (Entry *) instanceData;
1267    
1268        entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
1269        if (entryPtr->avgWidth == 0) {
1270            entryPtr->avgWidth = 1;
1271        }
1272    
1273        if (entryPtr->normalBorder != NULL) {
1274            Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
1275        }
1276    
1277        gcValues.foreground = entryPtr->fgColorPtr->pixel;
1278        gcValues.font = Tk_FontId(entryPtr->tkfont);
1279        gcValues.graphics_exposures = False;
1280        mask = GCForeground | GCFont | GCGraphicsExposures;
1281        gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1282        if (entryPtr->textGC != None) {
1283            Tk_FreeGC(entryPtr->display, entryPtr->textGC);
1284        }
1285        entryPtr->textGC = gc;
1286    
1287        gcValues.foreground = entryPtr->selFgColorPtr->pixel;
1288        gcValues.font = Tk_FontId(entryPtr->tkfont);
1289        mask = GCForeground | GCFont;
1290        gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues);
1291        if (entryPtr->selTextGC != None) {
1292            Tk_FreeGC(entryPtr->display, entryPtr->selTextGC);
1293        }
1294        entryPtr->selTextGC = gc;
1295    
1296        /*
1297         * Recompute the window's geometry and arrange for it to be
1298         * redisplayed.
1299         */
1300    
1301        EntryComputeGeometry(entryPtr);
1302        entryPtr->flags |= UPDATE_SCROLLBAR;
1303        EventuallyRedraw(entryPtr);
1304    }
1305    
1306    /*
1307     *--------------------------------------------------------------
1308     *
1309     * DisplayEntry --
1310     *
1311     *      This procedure redraws the contents of an entry window.
1312     *
1313     * Results:
1314     *      None.
1315     *
1316     * Side effects:
1317     *      Information appears on the screen.
1318     *
1319     *--------------------------------------------------------------
1320     */
1321    
1322    static void
1323    DisplayEntry(clientData)
1324        ClientData clientData;      /* Information about window. */
1325    {
1326        Entry *entryPtr = (Entry *) clientData;
1327        Tk_Window tkwin = entryPtr->tkwin;
1328        int baseY, selStartX, selEndX, cursorX;
1329        int xBound;
1330        Tk_FontMetrics fm;
1331        Pixmap pixmap;
1332        int showSelection;
1333    
1334        entryPtr->flags &= ~REDRAW_PENDING;
1335        if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1336            return;
1337        }
1338    
1339        Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1340    
1341        /*
1342         * Update the scrollbar if that's needed.
1343         */
1344    
1345        if (entryPtr->flags & UPDATE_SCROLLBAR) {
1346            entryPtr->flags &= ~UPDATE_SCROLLBAR;
1347            EntryUpdateScrollbar(entryPtr);
1348        }
1349    
1350        /*
1351         * In order to avoid screen flashes, this procedure redraws the
1352         * textual area of the entry into off-screen memory, then copies
1353         * it back on-screen in a single operation.  This means there's
1354         * no point in time where the on-screen image has been cleared.
1355         */
1356    
1357        pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin),
1358                Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1359    
1360        /*
1361         * Compute x-coordinate of the pixel just after last visible
1362         * one, plus vertical position of baseline of text.
1363         */
1364    
1365        xBound = Tk_Width(tkwin) - entryPtr->inset;
1366        baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
1367    
1368        /*
1369         * On Windows and Mac, we need to hide the selection whenever we
1370         * don't have the focus.
1371         */
1372    
1373    #ifdef ALWAYS_SHOW_SELECTION
1374        showSelection = 1;
1375    #else
1376        showSelection = (entryPtr->flags & GOT_FOCUS);
1377    #endif
1378    
1379        /*
1380         * Draw the background in three layers.  From bottom to top the
1381         * layers are:  normal background, selection background, and
1382         * insertion cursor background.
1383         */
1384    
1385        Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1386                    0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
1387    
1388        if (showSelection
1389                && (entryPtr->selectLast > entryPtr->leftIndex)) {
1390            if (entryPtr->selectFirst <= entryPtr->leftIndex) {
1391                selStartX = entryPtr->leftX;
1392            } else {
1393                Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst,
1394                        &selStartX, NULL, NULL, NULL);
1395                selStartX += entryPtr->layoutX;
1396            }
1397            if ((selStartX - entryPtr->selBorderWidth) < xBound) {
1398                Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast,
1399                        &selEndX, NULL, NULL, NULL);
1400                selEndX += entryPtr->layoutX;
1401                Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder,
1402                        selStartX - entryPtr->selBorderWidth,
1403                        baseY - fm.ascent - entryPtr->selBorderWidth,
1404                        (selEndX - selStartX) + 2*entryPtr->selBorderWidth,
1405                        (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth,
1406                        entryPtr->selBorderWidth, TK_RELIEF_RAISED);
1407            }
1408        }
1409    
1410        /*
1411         * Draw a special background for the insertion cursor, overriding
1412         * even the selection background.  As a special hack to keep the
1413         * cursor visible when the insertion cursor color is the same as
1414         * the color for selected text (e.g., on mono displays), write
1415         * background in the cursor area (instead of nothing) when the
1416         * cursor isn't on.  Otherwise the selection would hide the cursor.
1417         */
1418    
1419        if ((entryPtr->insertPos >= entryPtr->leftIndex)
1420                && (entryPtr->state == STATE_NORMAL)
1421                && (entryPtr->flags & GOT_FOCUS)) {
1422            Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos, &cursorX, NULL,
1423                    NULL, NULL);
1424            cursorX += entryPtr->layoutX;
1425            cursorX -= (entryPtr->insertWidth)/2;
1426            if (cursorX < xBound) {
1427                if (entryPtr->flags & CURSOR_ON) {
1428                    Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
1429                            cursorX, baseY - fm.ascent, entryPtr->insertWidth,
1430                            fm.ascent + fm.descent, entryPtr->insertBorderWidth,
1431                            TK_RELIEF_RAISED);
1432                } else if (entryPtr->insertBorder == entryPtr->selBorder) {
1433                    Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1434                            cursorX, baseY - fm.ascent, entryPtr->insertWidth,
1435                            fm.ascent + fm.descent, 0, TK_RELIEF_FLAT);
1436                }
1437            }
1438        }
1439    
1440        /*
1441         * Draw the text in two pieces:  first the unselected portion, then the
1442         * selected portion on top of it.
1443         */
1444    
1445        Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC,
1446                entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1447                entryPtr->leftIndex, entryPtr->numChars);
1448    
1449        if (showSelection
1450                && (entryPtr->selTextGC != entryPtr->textGC)
1451                && (entryPtr->selectFirst < entryPtr->selectLast)) {
1452            int selFirst;
1453    
1454            if (entryPtr->selectFirst < entryPtr->leftIndex) {
1455                selFirst = entryPtr->leftIndex;
1456            } else {
1457                selFirst = entryPtr->selectFirst;
1458            }
1459            Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
1460                    entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
1461                    selFirst, entryPtr->selectLast);
1462        }
1463    
1464        /*
1465         * Draw the border and focus highlight last, so they will overwrite
1466         * any text that extends past the viewable part of the window.
1467         */
1468    
1469        if (entryPtr->relief != TK_RELIEF_FLAT) {
1470            Tk_Draw3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
1471                    entryPtr->highlightWidth, entryPtr->highlightWidth,
1472                    Tk_Width(tkwin) - 2 * entryPtr->highlightWidth,
1473                    Tk_Height(tkwin) - 2 * entryPtr->highlightWidth,
1474                    entryPtr->borderWidth, entryPtr->relief);
1475        }
1476        if (entryPtr->highlightWidth != 0) {
1477            GC fgGC, bgGC;
1478    
1479            bgGC = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
1480            if (entryPtr->flags & GOT_FOCUS) {
1481                fgGC = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
1482                TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
1483                        entryPtr->highlightWidth, pixmap);
1484            } else {
1485                TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
1486                        entryPtr->highlightWidth, pixmap);
1487            }
1488        }
1489    
1490        /*
1491         * Everything's been redisplayed;  now copy the pixmap onto the screen
1492         * and free up the pixmap.
1493         */
1494    
1495        XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC,
1496                0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin),
1497                0, 0);
1498        Tk_FreePixmap(entryPtr->display, pixmap);
1499        entryPtr->flags &= ~BORDER_NEEDED;
1500    }
1501    
1502    /*
1503     *----------------------------------------------------------------------
1504     *
1505     * EntryComputeGeometry --
1506     *
1507     *      This procedure is invoked to recompute information about where
1508     *      in its window an entry's string will be displayed.  It also
1509     *      computes the requested size for the window.
1510     *
1511     * Results:
1512     *      None.
1513     *
1514     * Side effects:
1515     *      The leftX and tabOrigin fields are recomputed for entryPtr,
1516     *      and leftIndex may be adjusted.  Tk_GeometryRequest is called
1517     *      to register the desired dimensions for the window.
1518     *
1519     *----------------------------------------------------------------------
1520     */
1521    
1522    static void
1523    EntryComputeGeometry(entryPtr)
1524        Entry *entryPtr;            /* Widget record for entry. */
1525    {
1526        int totalLength, overflow, maxOffScreen, rightX;
1527        int height, width, i;
1528        Tk_FontMetrics fm;
1529        char *p;
1530    
1531        if (entryPtr->displayString != entryPtr->string) {
1532            ckfree(entryPtr->displayString);
1533            entryPtr->displayString = entryPtr->string;
1534            entryPtr->numDisplayBytes = entryPtr->numBytes;
1535        }
1536    
1537        /*
1538         * If we're displaying a special character instead of the value of
1539         * the entry, recompute the displayString.
1540         */
1541    
1542        if (entryPtr->showChar != NULL) {
1543            Tcl_UniChar ch;
1544            char buf[TCL_UTF_MAX];
1545            int size;
1546    
1547            /*
1548             * Normalize the special character so we can safely duplicate it
1549             * in the display string.  If we didn't do this, then two malformed
1550             * characters might end up looking like one valid UTF character in
1551             * the resulting string.
1552             */
1553    
1554            Tcl_UtfToUniChar(entryPtr->showChar, &ch);
1555            size = Tcl_UniCharToUtf(ch, buf);
1556    
1557            entryPtr->numDisplayBytes = entryPtr->numChars * size;
1558            entryPtr->displayString =
1559                    (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1));
1560    
1561            p = entryPtr->displayString;
1562            for (i = entryPtr->numChars; --i >= 0; ) {
1563                p += Tcl_UniCharToUtf(ch, p);
1564            }
1565            *p = '\0';
1566        }
1567        Tk_FreeTextLayout(entryPtr->textLayout);
1568        entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
1569                entryPtr->displayString, entryPtr->numChars, 0,
1570                entryPtr->justify, TK_IGNORE_NEWLINES, &totalLength, &height);
1571    
1572        entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2;
1573    
1574        /*
1575         * Recompute where the leftmost character on the display will
1576         * be drawn (entryPtr->leftX) and adjust leftIndex if necessary
1577         * so that we don't let characters hang off the edge of the
1578         * window unless the entire window is full.
1579         */
1580    
1581        overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
1582        if (overflow <= 0) {
1583            entryPtr->leftIndex = 0;
1584            if (entryPtr->justify == TK_JUSTIFY_LEFT) {
1585                entryPtr->leftX = entryPtr->inset;
1586            } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) {
1587                entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset
1588                        - totalLength;
1589            } else {
1590                entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
1591            }
1592            entryPtr->layoutX = entryPtr->leftX;
1593        } else {
1594            /*
1595             * The whole string can't fit in the window.  Compute the
1596             * maximum number of characters that may be off-screen to
1597             * the left without leaving empty space on the right of the
1598             * window, then don't let leftIndex be any greater than that.
1599             */
1600    
1601            maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0);
1602            Tk_CharBbox(entryPtr->textLayout, maxOffScreen,
1603                    &rightX, NULL, NULL, NULL);
1604            if (rightX < overflow) {
1605                maxOffScreen++;
1606            }
1607            if (entryPtr->leftIndex > maxOffScreen) {
1608                entryPtr->leftIndex = maxOffScreen;
1609            }
1610            Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex, &rightX,
1611                    NULL, NULL, NULL);
1612            entryPtr->leftX = entryPtr->inset;
1613            entryPtr->layoutX = entryPtr->leftX - rightX;
1614        }
1615    
1616        Tk_GetFontMetrics(entryPtr->tkfont, &fm);
1617        height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD);
1618        if (entryPtr->prefWidth > 0) {
1619            width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset;
1620        } else {
1621            if (totalLength == 0) {
1622                width = entryPtr->avgWidth + 2*entryPtr->inset;
1623            } else {
1624                width = totalLength + 2*entryPtr->inset;
1625            }
1626        }
1627        Tk_GeometryRequest(entryPtr->tkwin, width, height);
1628    }
1629    
1630    /*
1631     *----------------------------------------------------------------------
1632     *
1633     * InsertChars --
1634     *
1635     *      Add new characters to an entry widget.
1636     *
1637     * Results:
1638     *      None.
1639     *
1640     * Side effects:
1641     *      New information gets added to entryPtr;  it will be redisplayed
1642     *      soon, but not necessarily immediately.
1643     *
1644     *----------------------------------------------------------------------
1645     */
1646    
1647    static void
1648    InsertChars(entryPtr, index, value)
1649        Entry *entryPtr;            /* Entry that is to get the new elements. */
1650        int index;                  /* Add the new elements before this
1651                                     * character index. */
1652        char *value;                /* New characters to add (NULL-terminated
1653                                     * string). */
1654    {
1655        int byteIndex, byteCount, oldChars, charsAdded, newByteCount;
1656        char *new, *string;
1657    
1658        string = entryPtr->string;
1659        byteIndex = Tcl_UtfAtIndex(string, index) - string;
1660        byteCount = strlen(value);
1661        if (byteCount == 0) {
1662            return;
1663        }
1664    
1665        newByteCount = entryPtr->numBytes + byteCount + 1;
1666        new = (char *) ckalloc((unsigned) newByteCount);
1667        memcpy(new, string, (size_t) byteIndex);
1668        strcpy(new + byteIndex, value);
1669        strcpy(new + byteIndex + byteCount, string + byteIndex);
1670    
1671        if ((entryPtr->validate == VALIDATE_KEY ||
1672             entryPtr->validate == VALIDATE_ALL) &&
1673            EntryValidateChange(entryPtr, value, new, index,
1674                                VALIDATE_INSERT) != TCL_OK) {
1675            ckfree(new);
1676            return;
1677        }
1678    
1679        ckfree(string);
1680        entryPtr->string = new;
1681    
1682        /*
1683         * The following construction is used because inserting improperly
1684         * formed UTF-8 sequences between other improperly formed UTF-8
1685         * sequences could result in actually forming valid UTF-8 sequences;
1686         * the number of characters added may not be Tcl_NumUtfChars(string, -1),
1687         * because of context.  The actual number of characters added is how
1688         * many characters are in the string now minus the number that
1689         * used to be there.
1690         */
1691    
1692        oldChars = entryPtr->numChars;
1693        entryPtr->numChars = Tcl_NumUtfChars(new, -1);
1694        charsAdded = entryPtr->numChars - oldChars;
1695        entryPtr->numBytes += byteCount;
1696    
1697        if (entryPtr->displayString == string) {
1698            entryPtr->displayString = new;
1699            entryPtr->numDisplayBytes = entryPtr->numBytes;
1700        }
1701    
1702        /*
1703         * Inserting characters invalidates all indexes into the string.
1704         * Touch up the indexes so that they still refer to the same
1705         * characters (at new positions).  When updating the selection
1706         * end-points, don't include the new text in the selection unless
1707         * it was completely surrounded by the selection.
1708         */
1709    
1710        if (entryPtr->selectFirst >= index) {
1711            entryPtr->selectFirst += charsAdded;
1712        }
1713        if (entryPtr->selectLast > index) {
1714            entryPtr->selectLast += charsAdded;
1715        }
1716        if ((entryPtr->selectAnchor > index)
1717                || (entryPtr->selectFirst >= index)) {
1718            entryPtr->selectAnchor += charsAdded;
1719        }
1720        if (entryPtr->leftIndex > index) {
1721            entryPtr->leftIndex += charsAdded;
1722        }
1723        if (entryPtr->insertPos >= index) {
1724            entryPtr->insertPos += charsAdded;
1725        }
1726        EntryValueChanged(entryPtr);
1727    }
1728    
1729    /*
1730     *----------------------------------------------------------------------
1731     *
1732     * DeleteChars --
1733     *
1734     *      Remove one or more characters from an entry widget.
1735     *
1736     * Results:
1737     *      None.
1738     *
1739     * Side effects:
1740     *      Memory gets freed, the entry gets modified and (eventually)
1741     *      redisplayed.
1742     *
1743     *----------------------------------------------------------------------
1744     */
1745    
1746    static void
1747    DeleteChars(entryPtr, index, count)
1748        Entry *entryPtr;            /* Entry widget to modify. */
1749        int index;                  /* Index of first character to delete. */
1750        int count;                  /* How many characters to delete. */
1751    {
1752        int byteIndex, byteCount, newByteCount;
1753        char *new, *string, *todelete;
1754    
1755        if ((index + count) > entryPtr->numChars) {
1756            count = entryPtr->numChars - index;
1757        }
1758        if (count <= 0) {
1759            return;
1760        }
1761    
1762        string = entryPtr->string;
1763        byteIndex = Tcl_UtfAtIndex(string, index) - string;
1764        byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string + byteIndex);
1765    
1766        newByteCount = entryPtr->numBytes + 1 - byteCount;
1767        new = (char *) ckalloc((unsigned) newByteCount);
1768        memcpy(new, string, (size_t) byteIndex);
1769        strcpy(new + byteIndex, string + byteIndex + byteCount);
1770    
1771        todelete = (char *) ckalloc((unsigned) (byteCount + 1));
1772        memcpy(todelete, string + byteIndex, (size_t) byteCount);
1773        todelete[byteCount] = '\0';
1774    
1775        if ((entryPtr->validate == VALIDATE_KEY ||
1776             entryPtr->validate == VALIDATE_ALL) &&
1777            EntryValidateChange(entryPtr, todelete, new, index,
1778                                VALIDATE_DELETE) != TCL_OK) {
1779            ckfree(new);
1780            ckfree(todelete);
1781            return;
1782        }
1783    
1784        ckfree(todelete);
1785        ckfree(entryPtr->string);
1786        entryPtr->string = new;
1787        entryPtr->numChars -= count;
1788        entryPtr->numBytes -= byteCount;
1789    
1790        if (entryPtr->displayString == string) {
1791            entryPtr->displayString = new;
1792            entryPtr->numDisplayBytes = entryPtr->numBytes;
1793        }
1794    
1795        /*
1796         * Deleting characters results in the remaining characters being
1797         * renumbered.  Update the various indexes into the string to reflect
1798         * this change.
1799         */
1800    
1801        if (entryPtr->selectFirst >= index) {
1802            if (entryPtr->selectFirst >= (index + count)) {
1803                entryPtr->selectFirst -= count;
1804            } else {
1805                entryPtr->selectFirst = index;
1806            }
1807        }
1808        if (entryPtr->selectLast >= index) {
1809            if (entryPtr->selectLast >= (index + count)) {
1810                entryPtr->selectLast -= count;
1811            } else {
1812                entryPtr->selectLast = index;
1813            }
1814        }
1815        if (entryPtr->selectLast <= entryPtr->selectFirst) {
1816            entryPtr->selectFirst = -1;
1817            entryPtr->selectLast = -1;
1818        }
1819        if (entryPtr->selectAnchor >= index) {
1820            if (entryPtr->selectAnchor >= (index+count)) {
1821                entryPtr->selectAnchor -= count;
1822            } else {
1823                entryPtr->selectAnchor = index;
1824            }
1825        }
1826        if (entryPtr->leftIndex > index) {
1827            if (entryPtr->leftIndex >= (index + count)) {
1828                entryPtr->leftIndex -= count;
1829            } else {
1830                entryPtr->leftIndex = index;
1831            }
1832        }
1833        if (entryPtr->insertPos >= index) {
1834            if (entryPtr->insertPos >= (index + count)) {
1835                entryPtr->insertPos -= count;
1836            } else {
1837                entryPtr->insertPos = index;
1838            }
1839        }
1840        EntryValueChanged(entryPtr);
1841    }
1842    
1843    /*
1844     *----------------------------------------------------------------------
1845     *
1846     * EntryValueChanged --
1847     *
1848     *      This procedure is invoked when characters are inserted into
1849     *      an entry or deleted from it.  It updates the entry's associated
1850     *      variable, if there is one, and does other bookkeeping such
1851     *      as arranging for redisplay.
1852     *
1853     * Results:
1854     *      None.
1855     *
1856     * Side effects:
1857     *      None.
1858     *
1859     *----------------------------------------------------------------------
1860     */
1861    
1862    static void
1863    EntryValueChanged(entryPtr)
1864        Entry *entryPtr;            /* Entry whose value just changed. */
1865    {
1866        char *newValue;
1867    
1868        if (entryPtr->textVarName == NULL) {
1869            newValue = NULL;
1870        } else {
1871            newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
1872                    entryPtr->string, TCL_GLOBAL_ONLY);
1873        }
1874    
1875        if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
1876            /*
1877             * The value of the variable is different than what we asked for.
1878             * This means that a trace on the variable modified it.  In this
1879             * case our trace procedure wasn't invoked since the modification
1880             * came while a trace was already active on the variable.  So,
1881             * update our value to reflect the variable's latest value.
1882             */
1883    
1884            EntrySetValue(entryPtr, newValue);
1885        } else {
1886            /*
1887             * Arrange for redisplay.
1888             */
1889    
1890            entryPtr->flags |= UPDATE_SCROLLBAR;
1891            EntryComputeGeometry(entryPtr);
1892            EventuallyRedraw(entryPtr);
1893        }
1894    }
1895    
1896    /*
1897     *----------------------------------------------------------------------
1898     *
1899     * EntrySetValue --
1900     *
1901     *      Replace the contents of a text entry with a given value.  This
1902     *      procedure is invoked when updating the entry from the entry's
1903     *      associated variable.
1904     *
1905     * Results:
1906     *      None.
1907     *
1908     * Side effects:
1909     *      The string displayed in the entry will change.  The selection,
1910     *      insertion point, and view may have to be adjusted to keep them
1911     *      within the bounds of the new string.  Note: this procedure does
1912     *      *not* update the entry's associated variable, since that could
1913     *      result in an infinite loop.
1914     *
1915     *----------------------------------------------------------------------
1916     */
1917    
1918    static void
1919    EntrySetValue(entryPtr, value)
1920        Entry *entryPtr;            /* Entry whose value is to be changed. */
1921        char *value;                /* New text to display in entry. */
1922    {
1923        char *oldSource;
1924        int code, valueLen, malloced = 0;
1925    
1926        if (strcmp(value, entryPtr->string) == 0) {
1927            return;
1928        }
1929        valueLen = strlen(value);
1930    
1931        if (entryPtr->flags & VALIDATE_VAR) {
1932            entryPtr->flags |= VALIDATE_ABORT;
1933        } else {
1934            /*
1935             * If we validate, we create a copy of the value, as it may
1936             * point to volatile memory, like the value of the -textvar
1937             * which may get freed during validation
1938             */
1939            oldSource = (char *) ckalloc((unsigned) (valueLen + 1));
1940            strcpy(oldSource, value);
1941            value = oldSource;
1942            malloced = 1;
1943    
1944            entryPtr->flags |= VALIDATE_VAR;
1945            code = EntryValidateChange(entryPtr, (char *) NULL, value, -1,
1946                    VALIDATE_FORCED);
1947            entryPtr->flags &= ~VALIDATE_VAR;
1948            /*
1949             * If VALIDATE_ABORT has been set, then this operation should be
1950             * aborted because the validatecommand did something else instead
1951             */
1952            if (entryPtr->flags & VALIDATE_ABORT) {
1953                entryPtr->flags &= ~VALIDATE_ABORT;
1954                ckfree(value);
1955                return;
1956            }
1957        }
1958    
1959        oldSource = entryPtr->string;
1960        ckfree(entryPtr->string);
1961    
1962        if (malloced) {
1963            entryPtr->string = value;
1964        } else {
1965            entryPtr->string   = (char *) ckalloc((unsigned) (valueLen + 1));
1966            strcpy(entryPtr->string, value);
1967        }
1968        entryPtr->numBytes = valueLen;
1969        entryPtr->numChars = Tcl_NumUtfChars(value, valueLen);
1970    
1971        if (entryPtr->displayString == oldSource) {
1972            entryPtr->displayString = entryPtr->string;
1973            entryPtr->numDisplayBytes = entryPtr->numBytes;
1974        }
1975    
1976        if (entryPtr->selectFirst >= 0) {
1977            if (entryPtr->selectFirst >= entryPtr->numChars) {
1978                entryPtr->selectFirst = -1;
1979                entryPtr->selectLast = -1;
1980            } else if (entryPtr->selectLast > entryPtr->numChars) {
1981                entryPtr->selectLast = entryPtr->numChars;
1982            }
1983        }
1984        if (entryPtr->leftIndex >= entryPtr->numChars) {
1985            if (entryPtr->numChars > 0) {
1986                entryPtr->leftIndex = entryPtr->numChars - 1;
1987            } else {
1988                entryPtr->leftIndex = 0;
1989            }
1990        }
1991        if (entryPtr->insertPos > entryPtr->numChars) {
1992            entryPtr->insertPos = entryPtr->numChars;
1993        }
1994    
1995        entryPtr->flags |= UPDATE_SCROLLBAR;
1996        EntryComputeGeometry(entryPtr);
1997        EventuallyRedraw(entryPtr);
1998    }
1999    
2000    /*
2001     *--------------------------------------------------------------
2002     *
2003     * EntryEventProc --
2004     *
2005     *      This procedure is invoked by the Tk dispatcher for various
2006     *      events on entryes.
2007     *
2008     * Results:
2009     *      None.
2010     *
2011     * Side effects:
2012     *      When the window gets deleted, internal structures get
2013     *      cleaned up.  When it gets exposed, it is redisplayed.
2014     *
2015     *--------------------------------------------------------------
2016     */
2017    
2018    static void
2019    EntryEventProc(clientData, eventPtr)
2020        ClientData clientData;      /* Information about window. */
2021        XEvent *eventPtr;           /* Information about event. */
2022    {
2023        Entry *entryPtr = (Entry *) clientData;
2024        if (eventPtr->type == Expose) {
2025            EventuallyRedraw(entryPtr);
2026            entryPtr->flags |= BORDER_NEEDED;
2027        } else if (eventPtr->type == DestroyNotify) {
2028            DestroyEntry((char *) clientData);
2029        } else if (eventPtr->type == ConfigureNotify) {
2030            Tcl_Preserve((ClientData) entryPtr);
2031            entryPtr->flags |= UPDATE_SCROLLBAR;
2032            EntryComputeGeometry(entryPtr);
2033            EventuallyRedraw(entryPtr);
2034            Tcl_Release((ClientData) entryPtr);
2035        } else if (eventPtr->type == FocusIn) {
2036            if (eventPtr->xfocus.detail != NotifyInferior) {
2037                EntryFocusProc(entryPtr, 1);
2038            }
2039        } else if (eventPtr->type == FocusOut) {
2040            if (eventPtr->xfocus.detail != NotifyInferior) {
2041                EntryFocusProc(entryPtr, 0);
2042            }
2043        }
2044    }
2045    
2046    /*
2047     *----------------------------------------------------------------------
2048     *
2049     * EntryCmdDeletedProc --
2050     *
2051     *      This procedure is invoked when a widget command is deleted.  If
2052     *      the widget isn't already in the process of being destroyed,
2053     *      this command destroys it.
2054     *
2055     * Results:
2056     *      None.
2057     *
2058     * Side effects:
2059     *      The widget is destroyed.
2060     *
2061     *----------------------------------------------------------------------
2062     */
2063    
2064    static void
2065    EntryCmdDeletedProc(clientData)
2066        ClientData clientData;      /* Pointer to widget record for widget. */
2067    {
2068        Entry *entryPtr = (Entry *) clientData;
2069    
2070        /*
2071         * This procedure could be invoked either because the window was
2072         * destroyed and the command was then deleted (in which case tkwin
2073         * is NULL) or because the command was deleted, and then this procedure
2074         * destroys the widget.
2075         */
2076    
2077        if (!(entryPtr->flags & ENTRY_DELETED)) {
2078            Tk_DestroyWindow(entryPtr->tkwin);
2079        }
2080    }
2081    
2082    /*
2083     *---------------------------------------------------------------------------
2084     *
2085     * GetEntryIndex --
2086     *
2087     *      Parse an index into an entry and return either its value
2088     *      or an error.
2089     *
2090     * Results:
2091     *      A standard Tcl result.  If all went well, then *indexPtr is
2092     *      filled in with the character index (into entryPtr) corresponding to
2093     *      string.  The index value is guaranteed to lie between 0 and
2094     *      the number of characters in the string, inclusive.  If an
2095     *      error occurs then an error message is left in the interp's result.
2096     *
2097     * Side effects:
2098     *      None.
2099     *
2100     *---------------------------------------------------------------------------
2101     */
2102    
2103    static int
2104    GetEntryIndex(interp, entryPtr, string, indexPtr)
2105        Tcl_Interp *interp;         /* For error messages. */
2106        Entry *entryPtr;            /* Entry for which the index is being
2107                                     * specified. */
2108        char *string;               /* Specifies character in entryPtr. */
2109        int *indexPtr;              /* Where to store converted character
2110                                     * index. */
2111    {
2112        size_t length;
2113    
2114        length = strlen(string);
2115    
2116        if (string[0] == 'a') {
2117            if (strncmp(string, "anchor", length) == 0) {
2118                *indexPtr = entryPtr->selectAnchor;
2119            } else {
2120                badIndex:
2121    
2122                /*
2123                 * Some of the paths here leave messages in the interp's result,
2124                 * so we have to clear it out before storing our own message.
2125                 */
2126    
2127                Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2128                Tcl_AppendResult(interp, "bad entry index \"", string,
2129                        "\"", (char *) NULL);
2130                return TCL_ERROR;
2131            }
2132        } else if (string[0] == 'e') {
2133            if (strncmp(string, "end", length) == 0) {
2134                *indexPtr = entryPtr->numChars;
2135            } else {
2136                goto badIndex;
2137            }
2138        } else if (string[0] == 'i') {
2139            if (strncmp(string, "insert", length) == 0) {
2140                *indexPtr = entryPtr->insertPos;
2141            } else {
2142                goto badIndex;
2143            }
2144        } else if (string[0] == 's') {
2145            if (entryPtr->selectFirst < 0) {
2146                Tcl_SetResult(interp, "selection isn't in entry", TCL_STATIC);
2147                return TCL_ERROR;
2148            }
2149            if (length < 5) {
2150                goto badIndex;
2151            }
2152            if (strncmp(string, "sel.first", length) == 0) {
2153                *indexPtr = entryPtr->selectFirst;
2154            } else if (strncmp(string, "sel.last", length) == 0) {
2155                *indexPtr = entryPtr->selectLast;
2156            } else {
2157                goto badIndex;
2158            }
2159        } else if (string[0] == '@') {
2160            int x, roundUp;
2161    
2162            if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) {
2163                goto badIndex;
2164            }
2165            if (x < entryPtr->inset) {
2166                x = entryPtr->inset;
2167            }
2168            roundUp = 0;
2169            if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
2170                x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
2171                roundUp = 1;
2172            }
2173            *indexPtr = Tk_PointToChar(entryPtr->textLayout,
2174                    x - entryPtr->layoutX, 0);
2175    
2176            /*
2177             * Special trick:  if the x-position was off-screen to the right,
2178             * round the index up to refer to the character just after the
2179             * last visible one on the screen.  This is needed to enable the
2180             * last character to be selected, for example.
2181             */
2182    
2183            if (roundUp && (*indexPtr < entryPtr->numChars)) {
2184                *indexPtr += 1;
2185            }
2186        } else {
2187            if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
2188                goto badIndex;
2189            }
2190            if (*indexPtr < 0){
2191                *indexPtr = 0;
2192            } else if (*indexPtr > entryPtr->numChars) {
2193                *indexPtr = entryPtr->numChars;
2194            }
2195        }
2196        return TCL_OK;
2197    }
2198    
2199    /*
2200     *----------------------------------------------------------------------
2201     *
2202     * EntryScanTo --
2203     *
2204     *      Given a y-coordinate (presumably of the curent mouse location)
2205     *      drag the view in the window to implement the scan operation.
2206     *
2207     * Results:
2208     *      None.
2209     *
2210     * Side effects:
2211     *      The view in the window may change.
2212     *
2213     *----------------------------------------------------------------------
2214     */
2215    
2216    static void
2217    EntryScanTo(entryPtr, x)
2218        Entry *entryPtr;            /* Information about widget. */
2219        int x;                      /* X-coordinate to use for scan operation. */
2220    {
2221        int newLeftIndex;
2222    
2223        /*
2224         * Compute new leftIndex for entry by amplifying the difference
2225         * between the current position and the place where the scan
2226         * started (the "mark" position).  If we run off the left or right
2227         * side of the entry, then reset the mark point so that the current
2228         * position continues to correspond to the edge of the window.
2229         * This means that the picture will start dragging as soon as the
2230         * mouse reverses direction (without this reset, might have to slide
2231         * mouse a long ways back before the picture starts moving again).
2232         */
2233    
2234        newLeftIndex = entryPtr->scanMarkIndex
2235                - (10 * (x - entryPtr->scanMarkX)) / entryPtr->avgWidth;
2236        if (newLeftIndex >= entryPtr->numChars) {
2237            newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars - 1;
2238            entryPtr->scanMarkX = x;
2239        }
2240        if (newLeftIndex < 0) {
2241            newLeftIndex = entryPtr->scanMarkIndex = 0;
2242            entryPtr->scanMarkX = x;
2243        }
2244    
2245        if (newLeftIndex != entryPtr->leftIndex) {
2246            entryPtr->leftIndex = newLeftIndex;
2247            entryPtr->flags |= UPDATE_SCROLLBAR;
2248            EntryComputeGeometry(entryPtr);
2249            if (newLeftIndex != entryPtr->leftIndex) {
2250                entryPtr->scanMarkIndex = entryPtr->leftIndex;
2251                entryPtr->scanMarkX = x;
2252            }
2253            EventuallyRedraw(entryPtr);
2254        }
2255    }
2256    
2257    /*
2258     *----------------------------------------------------------------------
2259     *
2260     * EntrySelectTo --
2261     *
2262     *      Modify the selection by moving its un-anchored end.  This could
2263     *      make the selection either larger or smaller.
2264     *
2265     * Results:
2266     *      None.
2267     *
2268     * Side effects:
2269     *      The selection changes.
2270     *
2271     *----------------------------------------------------------------------
2272     */
2273    
2274    static void
2275    EntrySelectTo(entryPtr, index)
2276        Entry *entryPtr;            /* Information about widget. */
2277        int index;                  /* Character index of element that is to
2278                                     * become the "other" end of the selection. */
2279    {
2280        int newFirst, newLast;
2281    
2282        /*
2283         * Grab the selection if we don't own it already.
2284         */
2285    
2286        if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) {
2287            Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection,
2288                    (ClientData) entryPtr);
2289            entryPtr->flags |= GOT_SELECTION;
2290        }
2291    
2292        /*
2293         * Pick new starting and ending points for the selection.
2294         */
2295    
2296        if (entryPtr->selectAnchor > entryPtr->numChars) {
2297            entryPtr->selectAnchor = entryPtr->numChars;
2298        }
2299        if (entryPtr->selectAnchor <= index) {
2300            newFirst = entryPtr->selectAnchor;
2301            newLast = index;
2302        } else {
2303            newFirst = index;
2304            newLast = entryPtr->selectAnchor;
2305            if (newLast < 0) {
2306                newFirst = newLast = -1;
2307            }
2308        }
2309        if ((entryPtr->selectFirst == newFirst)
2310                && (entryPtr->selectLast == newLast)) {
2311            return;
2312        }
2313        entryPtr->selectFirst = newFirst;
2314        entryPtr->selectLast = newLast;
2315        EventuallyRedraw(entryPtr);
2316    }
2317    
2318    /*
2319     *----------------------------------------------------------------------
2320     *
2321     * EntryFetchSelection --
2322     *
2323     *      This procedure is called back by Tk when the selection is
2324     *      requested by someone.  It returns part or all of the selection
2325     *      in a buffer provided by the caller.
2326     *
2327     * Results:
2328     *      The return value is the number of non-NULL bytes stored
2329     *      at buffer.  Buffer is filled (or partially filled) with a
2330     *      NULL-terminated string containing part or all of the selection,
2331     *      as given by offset and maxBytes.
2332     *
2333     * Side effects:
2334     *      None.
2335     *
2336     *----------------------------------------------------------------------
2337     */
2338    
2339    static int
2340    EntryFetchSelection(clientData, offset, buffer, maxBytes)
2341        ClientData clientData;      /* Information about entry widget. */
2342        int offset;                 /* Byte offset within selection of first
2343                                     * character to be returned. */
2344        char *buffer;               /* Location in which to place selection. */
2345        int maxBytes;               /* Maximum number of bytes to place at
2346                                     * buffer, not including terminating NULL
2347                                     * character. */
2348    {
2349        Entry *entryPtr = (Entry *) clientData;
2350        int byteCount;
2351        char *string, *selStart, *selEnd;
2352    
2353        if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
2354            return -1;
2355        }
2356        string = entryPtr->displayString;
2357        selStart = Tcl_UtfAtIndex(string, entryPtr->selectFirst);
2358        selEnd = Tcl_UtfAtIndex(selStart,
2359                entryPtr->selectLast - entryPtr->selectFirst);
2360        byteCount = selEnd - selStart - offset;
2361        if (byteCount > maxBytes) {
2362            byteCount = maxBytes;
2363        }
2364        if (byteCount <= 0) {
2365            return 0;
2366        }
2367        memcpy(buffer, selStart + offset, (size_t) byteCount);
2368        buffer[byteCount] = '\0';
2369        return byteCount;
2370    }
2371    
2372    /*
2373     *----------------------------------------------------------------------
2374     *
2375     * EntryLostSelection --
2376     *
2377     *      This procedure is called back by Tk when the selection is
2378     *      grabbed away from an entry widget.
2379     *
2380     * Results:
2381     *      None.
2382     *
2383     * Side effects:
2384     *      The existing selection is unhighlighted, and the window is
2385     *      marked as not containing a selection.
2386     *
2387     *----------------------------------------------------------------------
2388     */
2389    
2390    static void
2391    EntryLostSelection(clientData)
2392        ClientData clientData;      /* Information about entry widget. */
2393    {
2394        Entry *entryPtr = (Entry *) clientData;
2395    
2396        entryPtr->flags &= ~GOT_SELECTION;
2397    
2398        /*
2399         * On Windows and Mac systems, we want to remember the selection
2400         * for the next time the focus enters the window.  On Unix, we need
2401         * to clear the selection since it is always visible.
2402         */
2403    
2404    #ifdef ALWAYS_SHOW_SELECTION
2405        if ((entryPtr->selectFirst >= 0) && entryPtr->exportSelection) {
2406            entryPtr->selectFirst = -1;
2407            entryPtr->selectLast = -1;
2408            EventuallyRedraw(entryPtr);
2409        }
2410    #endif
2411    }
2412    
2413    /*
2414     *----------------------------------------------------------------------
2415     *
2416     * EventuallyRedraw --
2417     *
2418     *      Ensure that an entry is eventually redrawn on the display.
2419     *
2420     * Results:
2421     *      None.
2422     *
2423     * Side effects:
2424     *      Information gets redisplayed.  Right now we don't do selective
2425     *      redisplays:  the whole window will be redrawn.  This doesn't
2426     *      seem to hurt performance noticeably, but if it does then this
2427     *      could be changed.
2428     *
2429     *----------------------------------------------------------------------
2430     */
2431    
2432    static void
2433    EventuallyRedraw(entryPtr)
2434        Entry *entryPtr;            /* Information about widget. */
2435    {
2436        if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
2437            return;
2438        }
2439    
2440        /*
2441         * Right now we don't do selective redisplays:  the whole window
2442         * will be redrawn.  This doesn't seem to hurt performance noticeably,
2443         * but if it does then this could be changed.
2444         */
2445    
2446        if (!(entryPtr->flags & REDRAW_PENDING)) {
2447            entryPtr->flags |= REDRAW_PENDING;
2448            Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr);
2449        }
2450    }
2451    
2452    /*
2453     *----------------------------------------------------------------------
2454     *
2455     * EntryVisibleRange --
2456     *
2457     *      Return information about the range of the entry that is
2458     *      currently visible.
2459     *
2460     * Results:
2461     *      *firstPtr and *lastPtr are modified to hold fractions between
2462     *      0 and 1 identifying the range of characters visible in the
2463     *      entry.
2464     *
2465     * Side effects:
2466     *      None.
2467     *
2468     *----------------------------------------------------------------------
2469     */
2470    
2471    static void
2472    EntryVisibleRange(entryPtr, firstPtr, lastPtr)
2473        Entry *entryPtr;            /* Information about widget. */
2474        double *firstPtr;           /* Return position of first visible
2475                                     * character in widget. */
2476        double *lastPtr;            /* Return position of char just after last
2477                                     * visible one. */
2478    {
2479        int charsInWindow;
2480    
2481        if (entryPtr->numChars == 0) {
2482            *firstPtr = 0.0;
2483            *lastPtr = 1.0;
2484        } else {
2485            charsInWindow = Tk_PointToChar(entryPtr->textLayout,
2486                    Tk_Width(entryPtr->tkwin) - entryPtr->inset
2487                            - entryPtr->layoutX - 1, 0);
2488            if (charsInWindow < entryPtr->numChars) {
2489                charsInWindow++;
2490            }
2491            charsInWindow -= entryPtr->leftIndex;
2492            if (charsInWindow == 0) {
2493                charsInWindow = 1;
2494            }
2495    
2496            *firstPtr = (double) entryPtr->leftIndex / entryPtr->numChars;
2497            *lastPtr = (double) (entryPtr->leftIndex + charsInWindow)
2498                    / entryPtr->numChars;
2499        }
2500    }
2501    
2502    /*
2503     *----------------------------------------------------------------------
2504     *
2505     * EntryUpdateScrollbar --
2506     *
2507     *      This procedure is invoked whenever information has changed in
2508     *      an entry in a way that would invalidate a scrollbar display.
2509     *      If there is an associated scrollbar, then this procedure updates
2510     *      it by invoking a Tcl command.
2511     *
2512     * Results:
2513     *      None.
2514     *
2515     * Side effects:
2516     *      A Tcl command is invoked, and an additional command may be
2517     *      invoked to process errors in the command.
2518     *
2519     *----------------------------------------------------------------------
2520     */
2521    
2522    static void
2523    EntryUpdateScrollbar(entryPtr)
2524        Entry *entryPtr;                    /* Information about widget. */
2525    {
2526        char args[TCL_DOUBLE_SPACE * 2];
2527        int code;
2528        double first, last;
2529        Tcl_Interp *interp;
2530    
2531        if (entryPtr->scrollCmd == NULL) {
2532            return;
2533        }
2534    
2535        interp = entryPtr->interp;
2536        Tcl_Preserve((ClientData) interp);
2537        EntryVisibleRange(entryPtr, &first, &last);
2538        sprintf(args, " %g %g", first, last);
2539        code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
2540        if (code != TCL_OK) {
2541            Tcl_AddErrorInfo(interp,
2542                    "\n    (horizontal scrolling command executed by entry)");
2543            Tcl_BackgroundError(interp);
2544        }
2545        Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
2546        Tcl_Release((ClientData) interp);
2547    }
2548    
2549    /*
2550     *----------------------------------------------------------------------
2551     *
2552     * EntryBlinkProc --
2553     *
2554     *      This procedure is called as a timer handler to blink the
2555     *      insertion cursor off and on.
2556     *
2557     * Results:
2558     *      None.
2559     *
2560     * Side effects:
2561     *      The cursor gets turned on or off, redisplay gets invoked,
2562     *      and this procedure reschedules itself.
2563     *
2564     *----------------------------------------------------------------------
2565     */
2566    
2567    static void
2568    EntryBlinkProc(clientData)
2569        ClientData clientData;      /* Pointer to record describing entry. */
2570    {
2571        Entry *entryPtr = (Entry *) clientData;
2572    
2573        if ((entryPtr->state == STATE_DISABLED) ||
2574                !(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) {
2575            return;
2576        }
2577        if (entryPtr->flags & CURSOR_ON) {
2578            entryPtr->flags &= ~CURSOR_ON;
2579            entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2580                    entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
2581        } else {
2582            entryPtr->flags |= CURSOR_ON;
2583            entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2584                    entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
2585        }
2586        EventuallyRedraw(entryPtr);
2587    }
2588    
2589    /*
2590     *----------------------------------------------------------------------
2591     *
2592     * EntryFocusProc --
2593     *
2594     *      This procedure is called whenever the entry gets or loses the
2595     *      input focus.  It's also called whenever the window is reconfigured
2596     *      while it has the focus.
2597     *
2598     * Results:
2599     *      None.
2600     *
2601     * Side effects:
2602     *      The cursor gets turned on or off.
2603     *
2604     *----------------------------------------------------------------------
2605     */
2606    
2607    static void
2608    EntryFocusProc(entryPtr, gotFocus)
2609        Entry *entryPtr;            /* Entry that got or lost focus. */
2610        int gotFocus;               /* 1 means window is getting focus, 0 means
2611                                     * it's losing it. */
2612    {
2613        Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
2614        if (gotFocus) {
2615            entryPtr->flags |= GOT_FOCUS | CURSOR_ON;
2616            if (entryPtr->insertOffTime != 0) {
2617                entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
2618                        entryPtr->insertOnTime, EntryBlinkProc,
2619                        (ClientData) entryPtr);
2620            }
2621            if (entryPtr->validate == VALIDATE_ALL ||
2622                entryPtr->validate == VALIDATE_FOCUS ||
2623                entryPtr->validate == VALIDATE_FOCUSIN) {
2624                EntryValidateChange(entryPtr, (char *) NULL,
2625                                    entryPtr->string, -1, VALIDATE_FOCUSIN);
2626            }
2627        } else {
2628            entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
2629            entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
2630            if (entryPtr->validate == VALIDATE_ALL ||
2631                entryPtr->validate == VALIDATE_FOCUS ||
2632                entryPtr->validate == VALIDATE_FOCUSOUT) {
2633                EntryValidateChange(entryPtr, (char *) NULL,
2634                                    entryPtr->string, -1, VALIDATE_FOCUSOUT);
2635            }
2636        }
2637        EventuallyRedraw(entryPtr);
2638    }
2639    
2640    /*
2641     *--------------------------------------------------------------
2642     *
2643     * EntryTextVarProc --
2644     *
2645     *      This procedure is invoked when someone changes the variable
2646     *      whose contents are to be displayed in an entry.
2647     *
2648     * Results:
2649     *      NULL is always returned.
2650     *
2651     * Side effects:
2652     *      The text displayed in the entry will change to match the
2653     *      variable.
2654     *
2655     *--------------------------------------------------------------
2656     */
2657    
2658            /* ARGSUSED */
2659    static char *
2660    EntryTextVarProc(clientData, interp, name1, name2, flags)
2661        ClientData clientData;      /* Information about button. */
2662        Tcl_Interp *interp;         /* Interpreter containing variable. */
2663        char *name1;                /* Not used. */
2664        char *name2;                /* Not used. */
2665        int flags;                  /* Information about what happened. */
2666    {
2667        Entry *entryPtr = (Entry *) clientData;
2668        char *value;
2669    
2670        /*
2671         * If the variable is unset, then immediately recreate it unless
2672         * the whole interpreter is going away.
2673         */
2674    
2675        if (flags & TCL_TRACE_UNSETS) {
2676            if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
2677                Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
2678                        TCL_GLOBAL_ONLY);
2679                Tcl_TraceVar(interp, entryPtr->textVarName,
2680                        TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
2681                        EntryTextVarProc, clientData);
2682            }
2683            return (char *) NULL;
2684        }
2685    
2686        /*
2687         * Update the entry's text with the value of the variable, unless
2688         * the entry already has that value (this happens when the variable
2689         * changes value because we changed it because someone typed in
2690         * the entry).
2691         */
2692    
2693        value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
2694        if (value == NULL) {
2695            value = "";
2696        }
2697        EntrySetValue(entryPtr, value);
2698        return (char *) NULL;
2699    }
2700    
2701    /*
2702     *--------------------------------------------------------------
2703     *
2704     * EntryValidate --
2705     *
2706     *      This procedure is invoked when any character is added or
2707     *      removed from the entry widget, or a focus has trigerred validation.
2708     *
2709     * Results:
2710     *      TCL_OK if the validatecommand passes the new string.
2711     *      TCL_BREAK if the vcmd executed OK, but rejects the string.
2712     *      TCL_ERROR if an error occurred while executing the vcmd
2713     *      or a valid Tcl_Bool is not returned.
2714     *
2715     * Side effects:
2716     *      An error condition may arise
2717     *
2718     *--------------------------------------------------------------
2719     */
2720    
2721    static int
2722    EntryValidate(entryPtr, cmd)
2723         register Entry *entryPtr;  /* Entry that needs validation. */
2724         register char *cmd;        /* Validation command (NULL-terminated
2725                                     * string). */
2726    {
2727        register Tcl_Interp *interp = entryPtr->interp;
2728        int code, bool;
2729    
2730        code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
2731    
2732        if (code != TCL_OK && code != TCL_RETURN) {
2733            Tcl_AddErrorInfo(interp,
2734                             "\n\t(in validation command executed by entry)");
2735            Tcl_BackgroundError(interp);
2736            return TCL_ERROR;
2737        }
2738    
2739        if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),
2740                                  &bool) != TCL_OK) {
2741            Tcl_AddErrorInfo(interp,
2742                     "\nvalid boolean not returned by validation command");
2743            Tcl_BackgroundError(interp);
2744            Tcl_SetResult(interp, NULL, 0);
2745            return TCL_ERROR;
2746        }
2747    
2748        Tcl_SetResult(interp, NULL, 0);
2749        return (bool ? TCL_OK : TCL_BREAK);
2750    }
2751    
2752    /*
2753     *--------------------------------------------------------------
2754     *
2755     * EntryValidateChange --
2756     *
2757     *      This procedure is invoked when any character is added or
2758     *      removed from the entry widget, or a focus has trigerred validation.
2759     *
2760     * Results:
2761     *      TCL_OK if the validatecommand accepts the new string,
2762     *      TCL_ERROR if any problems occured with validatecommand.
2763     *
2764     * Side effects:
2765     *      The insertion/deletion may be aborted, and the
2766     *      validatecommand might turn itself off (if an error
2767     *      or loop condition arises).
2768     *
2769     *--------------------------------------------------------------
2770     */
2771    
2772    static int
2773    EntryValidateChange(entryPtr, change, new, index, type)
2774         register Entry *entryPtr;  /* Entry that needs validation. */
2775         char *change;              /* Characters to be added/deleted
2776                                     * (NULL-terminated string). */
2777         char *new;                 /* Potential new value of entry string */
2778         int index;                 /* index of insert/delete, -1 otherwise */
2779         int type;                  /* forced, delete, insert,
2780                                     * focusin or focusout */
2781    {
2782        int code, varValidate = (entryPtr->flags & VALIDATE_VAR);
2783        char *p;
2784        Tcl_DString script;
2785        
2786        if (entryPtr->validateCmd == NULL ||
2787            entryPtr->validate == VALIDATE_NONE) {
2788            return (varValidate ? TCL_ERROR : TCL_OK);
2789        }
2790    
2791        /*
2792         * If we're already validating, then we're hitting a loop condition
2793         * Return and set validate to 0 to disallow further validations
2794         * and prevent current validation from finishing
2795         */
2796        if (entryPtr->flags & VALIDATING) {
2797            entryPtr->validate = VALIDATE_NONE;
2798            return (varValidate ? TCL_ERROR : TCL_OK);
2799        }
2800    
2801        entryPtr->flags |= VALIDATING;
2802    
2803        /*
2804         * Now form command string and run through the -validatecommand
2805         */
2806    
2807        Tcl_DStringInit(&script);
2808        ExpandPercents(entryPtr, entryPtr->validateCmd,
2809                change, new, index, type, &script);
2810        Tcl_DStringAppend(&script, "", 1);
2811    
2812        p = Tcl_DStringValue(&script);
2813        code = EntryValidate(entryPtr, p);
2814        Tcl_DStringFree(&script);
2815    
2816        /*
2817         * If e->validate has become VALIDATE_NONE during the validation, or
2818         * we now have VALIDATE_VAR set (from EntrySetValue) and didn't before,
2819         * it means that a loop condition almost occured.  Do not allow
2820         * this validation result to finish.
2821         */
2822        if (entryPtr->validate == VALIDATE_NONE
2823                || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) {
2824            code = TCL_ERROR;
2825        }
2826        /*
2827         * If validate will return ERROR, then disallow further validations
2828         * Otherwise, if it didn't accept the new string (returned TCL_BREAK)
2829         * then eval the invalidCmd (if it's set)
2830         */
2831        if (code == TCL_ERROR) {
2832            entryPtr->validate = VALIDATE_NONE;
2833        } else if (code == TCL_BREAK) {
2834            /*
2835             * If we were doing forced validation (like via a variable
2836             * trace) and the command returned 0, the we turn off validation
2837             * because we assume that textvariables have precedence in
2838             * managing the value.  We also don't call the invcmd, as it
2839             * may want to do entry manipulation which the setting of the
2840             * var will later wipe anyway.
2841             */
2842            if (varValidate) {
2843                entryPtr->validate = VALIDATE_NONE;
2844            } else if (entryPtr->invalidCmd != NULL) {
2845                Tcl_DStringInit(&script);
2846                ExpandPercents(entryPtr, entryPtr->invalidCmd,
2847                               change, new, index, type, &script);
2848                Tcl_DStringAppend(&script, "", 1);
2849                p = Tcl_DStringValue(&script);
2850                if (Tcl_EvalEx(entryPtr->interp, p, -1,
2851                        TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT) != TCL_OK) {
2852                    Tcl_AddErrorInfo(entryPtr->interp,
2853                                     "\n\t(in invalidcommand executed by entry)");
2854                    Tcl_BackgroundError(entryPtr->interp);
2855                    code = TCL_ERROR;
2856                    entryPtr->validate = VALIDATE_NONE;
2857                }
2858                Tcl_DStringFree(&script);
2859            }
2860        }
2861    
2862        entryPtr->flags &= ~VALIDATING;
2863    
2864        return code;
2865    }
2866    
2867    /*
2868     *--------------------------------------------------------------
2869     *
2870     * ExpandPercents --
2871     *
2872     *      Given a command and an event, produce a new command
2873     *      by replacing % constructs in the original command
2874     *      with information from the X event.
2875     *
2876     * Results:
2877     *      The new expanded command is appended to the dynamic string
2878     *      given by dsPtr.
2879     *
2880     * Side effects:
2881     *      None.
2882     *
2883     *--------------------------------------------------------------
2884     */
2885    
2886    static void
2887    ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
2888         register Entry *entryPtr;  /* Entry that needs validation. */
2889         register char *before;     /* Command containing percent
2890                                     * expressions to be replaced. */
2891         char *change;              /* Characters to added/deleted
2892                                     * (NULL-terminated string). */
2893         char *new;                 /* Potential new value of entry string */
2894         int index;                 /* index of insert/delete */
2895         int type;                  /* INSERT or DELETE */
2896         Tcl_DString *dsPtr;        /* Dynamic string in which to append
2897                                     * new command. */
2898    {
2899        int spaceNeeded, cvtFlags;  /* Used to substitute string as proper Tcl
2900                                     * list element. */
2901        int number, length;
2902        register char *string;
2903        Tcl_UniChar ch;
2904        char numStorage[2*TCL_INTEGER_SPACE];
2905    
2906        while (1) {
2907            if (*before == '\0') {
2908                break;
2909            }
2910            /*
2911             * Find everything up to the next % character and append it
2912             * to the result string.
2913             */
2914    
2915            string = before;
2916            /* No need to convert '%', as it is in ascii range */
2917            string = Tcl_UtfFindFirst(before, '%');
2918            if (string == (char *) NULL) {
2919                Tcl_DStringAppend(dsPtr, before, -1);
2920                break;
2921            } else if (string != before) {
2922                Tcl_DStringAppend(dsPtr, before, string-before);
2923                before = string;
2924            }
2925    
2926            /*
2927             * There's a percent sequence here.  Process it.
2928             */
2929    
2930            before++; /* skip over % */
2931            if (*before != '\0') {
2932                before += Tcl_UtfToUniChar(before, &ch);
2933            } else {
2934                ch = '%';
2935            }
2936            switch (ch) {
2937            case 'd': /* Type of call that caused validation */
2938                switch (type) {
2939                case VALIDATE_INSERT:
2940                    number = 1;
2941                    break;
2942                case VALIDATE_DELETE:
2943                    number = 0;
2944                    break;
2945                default:
2946                    number = -1;
2947                    break;
2948                }
2949                sprintf(numStorage, "%d", number);
2950                string = numStorage;
2951                break;
2952            case 'i': /* index of insert/delete */
2953                sprintf(numStorage, "%d", index);
2954                string = numStorage;
2955                break;
2956            case 'P': /* 'Peeked' new value of the string */
2957                string = new;
2958                break;
2959            case 's': /* Current string value of entry */
2960                string = entryPtr->string;
2961                break;
2962            case 'S': /* string to be inserted/deleted, if any */
2963                string = change;
2964                break;
2965            case 'v': /* type of validation currently set */
2966                string = validateStrings[entryPtr->validate];
2967                break;
2968            case 'V': /* type of validation in effect */
2969                switch (type) {
2970                case VALIDATE_INSERT:
2971                case VALIDATE_DELETE:
2972                    string = validateStrings[VALIDATE_KEY];
2973                    break;
2974                case VALIDATE_FORCED:
2975                    string = "forced";
2976                    break;
2977                default:
2978                    string = validateStrings[type];
2979                    break;
2980                }
2981                break;
2982            case 'W': /* widget name */
2983                string = Tk_PathName(entryPtr->tkwin);
2984                break;
2985            default:
2986                length = Tcl_UniCharToUtf(ch, numStorage);
2987                numStorage[length] = '\0';
2988                string = numStorage;
2989                break;
2990            }
2991    
2992            spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);
2993            length = Tcl_DStringLength(dsPtr);
2994            Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2995            spaceNeeded = Tcl_ConvertCountedElement(string, -1,
2996                    Tcl_DStringValue(dsPtr) + length,
2997                    cvtFlags | TCL_DONT_USE_BRACES);
2998            Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
2999        }
3000    }
3001    
3002    /* End of tkentry.c */

Legend:
Removed from v.42  
changed lines
  Added in v.98

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25