Logo Search packages:      
Sourcecode: tcltk8.0-ja version File versions  Download package

tkEntry.c

/* 
 * 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-1996 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.8 1998/12/18 12:42:34 m-hirano Exp $
 */

#ifdef KANJI
#define TK_KANJI_OK
#endif /* KANJI */

#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. */

    /*
     * Fields that are set by widget commands other than "configure".
     */

#ifdef TK_KANJI_OK     
    wchar *string;            /* Pointer to storage for string;
                         * NULL-terminated;  malloc-ed. */
#else
    char *string;       /* Pointer to storage for string;
                         * NULL-terminated;  malloc-ed. */
#endif /* TK_KANJI_OK */
    int insertPos;            /* Index of character before which next
                         * typed character will be inserted. */

    /*
     * Information about what's selected, if any.
     */

    int selectFirst;          /* Index of first selected character (-1 means
                         * nothing selected. */
    int selectLast;           /* Index of 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;        /* 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. */
#ifdef TK_KANJI_OK
    wchar *showChar;          /* Value of -show option.  If non-NULL, first
                         * character is used for displaying all
                         * characters in entry.  Malloc'ed. */
#else
    char *showChar;           /* Value of -show option.  If non-NULL, first
                         * character is used for displaying all
                         * characters in entry.  Malloc'ed. */
#endif /* TK_KANJI_OK */
    Tk_Uid 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 numChars;       /* Number of non-NULL characters in
                         * string (may be 0). */
#ifdef TK_KANJI_OK
    wchar *displayString;     /* If non-NULL, points to string with same
                         * length as string but whose characters
                         * are all equal to showChar.  Malloc'ed. */
#else
    char *displayString;      /* If non-NULL, points to string with same
                         * length as string but whose characters
                         * are all equal to showChar.  Malloc'ed. */
#endif /* TK_KANJI_OK */
    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 leftIndex;            /* Index of left-most character visible in
                         * window. */
    int leftX;                /* X position at which character at leftIndex
                         * is drawn (varies depending on justify). */
    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. */
} 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.
 */

#define REDRAW_PENDING        1
#define BORDER_NEEDED         2
#define CURSOR_ON       4
#define GOT_FOCUS       8
#define UPDATE_SCROLLBAR      0x10
#define GOT_SELECTION         0x20

/*
 * 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

/*
 * Information used for argv parsing.
 */

static Tk_ConfigSpec configSpecs[] = {
    {TK_CONFIG_BORDER, "-background", "background", "Background",
      DEF_ENTRY_BG_COLOR, Tk_Offset(Entry, normalBorder),
      TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-background", "background", "Background",
      DEF_ENTRY_BG_MONO, Tk_Offset(Entry, normalBorder),
      TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
      (char *) NULL, 0, 0},
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
      (char *) NULL, 0, 0},
    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
      DEF_ENTRY_BORDER_WIDTH, Tk_Offset(Entry, borderWidth), 0},
    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
      DEF_ENTRY_CURSOR, Tk_Offset(Entry, cursor), TK_CONFIG_NULL_OK},
    {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
      "ExportSelection", DEF_ENTRY_EXPORT_SELECTION,
      Tk_Offset(Entry, exportSelection), 0},
    {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
      (char *) NULL, 0, 0},
    {TK_CONFIG_FONT, "-font", "font", "Font",
      DEF_ENTRY_FONT, Tk_Offset(Entry, tkfont), 0},
    {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
      DEF_ENTRY_FG, Tk_Offset(Entry, fgColorPtr), 0},
    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
      "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
      Tk_Offset(Entry, highlightBgColorPtr), 0},
    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
      DEF_ENTRY_HIGHLIGHT, Tk_Offset(Entry, highlightColorPtr), 0},
    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
      "HighlightThickness",
      DEF_ENTRY_HIGHLIGHT_WIDTH, Tk_Offset(Entry, highlightWidth), 0},
    {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
      DEF_ENTRY_INSERT_BG, Tk_Offset(Entry, insertBorder), 0},
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
      DEF_ENTRY_INSERT_BD_COLOR, Tk_Offset(Entry, insertBorderWidth),
      TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
      DEF_ENTRY_INSERT_BD_MONO, Tk_Offset(Entry, insertBorderWidth),
      TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
      DEF_ENTRY_INSERT_OFF_TIME, Tk_Offset(Entry, insertOffTime), 0},
    {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
      DEF_ENTRY_INSERT_ON_TIME, Tk_Offset(Entry, insertOnTime), 0},
    {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
      DEF_ENTRY_INSERT_WIDTH, Tk_Offset(Entry, insertWidth), 0},
    {TK_CONFIG_JUSTIFY, "-justify", "justify", "Justify",
      DEF_ENTRY_JUSTIFY, Tk_Offset(Entry, justify), 0},
    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
      DEF_ENTRY_RELIEF, Tk_Offset(Entry, relief), 0},
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
      DEF_ENTRY_SELECT_COLOR, Tk_Offset(Entry, selBorder),
      TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
      DEF_ENTRY_SELECT_MONO, Tk_Offset(Entry, selBorder),
      TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
      DEF_ENTRY_SELECT_BD_COLOR, Tk_Offset(Entry, selBorderWidth),
      TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
      DEF_ENTRY_SELECT_BD_MONO, Tk_Offset(Entry, selBorderWidth),
      TK_CONFIG_MONO_ONLY},
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
      DEF_ENTRY_SELECT_FG_COLOR, Tk_Offset(Entry, selFgColorPtr),
      TK_CONFIG_COLOR_ONLY},
    {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
      DEF_ENTRY_SELECT_FG_MONO, Tk_Offset(Entry, selFgColorPtr),
      TK_CONFIG_MONO_ONLY},
#ifdef TK_KANJI_OK
    {TK_CONFIG_WSTRING, "-show", "show", "Show",
      DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
#else
    {TK_CONFIG_STRING, "-show", "show", "Show",
      DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_OK},
#endif /* TK_KANJI_OK */
    {TK_CONFIG_UID, "-state", "state", "State",
      DEF_ENTRY_STATE, Tk_Offset(Entry, state), 0},
    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
      DEF_ENTRY_TAKE_FOCUS, Tk_Offset(Entry, takeFocus), TK_CONFIG_NULL_OK},
    {TK_CONFIG_STRING, "-textvariable", "textVariable", "Variable",
      DEF_ENTRY_TEXT_VARIABLE, Tk_Offset(Entry, textVarName),
      TK_CONFIG_NULL_OK},
    {TK_CONFIG_INT, "-width", "width", "Width",
      DEF_ENTRY_WIDTH, Tk_Offset(Entry, prefWidth), 0},
    {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
      DEF_ENTRY_SCROLL_COMMAND, Tk_Offset(Entry, scrollCmd),
      TK_CONFIG_NULL_OK},
    {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
      (char *) NULL, 0, 0}
};

/*
 * Flags for GetEntryIndex procedure:
 */

#define ZERO_OK               1
#define LAST_PLUS_ONE_OK      2

/*
 * Forward declarations for procedures defined later in this file:
 */

static int        ConfigureEntry _ANSI_ARGS_((Tcl_Interp *interp,
                      Entry *entryPtr, int argc, char **argv,
                      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));
#ifdef TK_KANJI_OK
static int        EntryFetchSelectionCtext _ANSI_ARGS_((
                      ClientData clientData,
                      int offset, char *buffer, int maxBytes));
#endif /* TK_KANJI_OK */
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 void       EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
static void       EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
                      double *firstPtr, double *lastPtr));
static int        EntryWidgetCmd _ANSI_ARGS_((ClientData clientData,
                      Tcl_Interp *interp, int argc, char **argv));
static void       EntryWorldChanged _ANSI_ARGS_((
                      ClientData instanceData));
#if defined(KINPUT2) || defined(XIM_IMPROVE) || defined(IME_AWARE)
static int        EntryXYPos _ANSI_ARGS_((Tcl_Interp *interp,
                                    Entry *entryPtr, int index));
#endif /* KINPUT2 || XIM_IMPROVE || IME_AWARE */
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_EntryCmd --
 *
 *    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_EntryCmd(clientData, interp, argc, argv)
    ClientData clientData;    /* Main window associated with
                         * interpreter. */
    Tcl_Interp *interp;       /* Current interpreter. */
    int argc;                 /* Number of arguments. */
    char **argv;        /* Argument strings. */
{
    Tk_Window tkwin = (Tk_Window) clientData;
    register Entry *entryPtr;
    Tk_Window new;

    if (argc < 2) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
            argv[0], " pathName ?options?\"", (char *) NULL);
      return TCL_ERROR;
    }

    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
    if (new == 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 = new;
    entryPtr->display = Tk_Display(new);
    entryPtr->interp = interp;
    entryPtr->widgetCmd = Tcl_CreateCommand(interp,
          Tk_PathName(entryPtr->tkwin), EntryWidgetCmd,
          (ClientData) entryPtr, EntryCmdDeletedProc);
#ifdef TK_KANJI_OK
    entryPtr->string = Tcl_GetWStr(NULL, "", NULL);
#else
    entryPtr->string = (char *) ckalloc(1);
    entryPtr->string[0] = '\0';
#endif
    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 = tkNormalUid;
    entryPtr->textVarName = NULL;
    entryPtr->takeFocus = NULL;
    entryPtr->prefWidth = 0;
    entryPtr->scrollCmd = NULL;

    entryPtr->numChars = 0;
    entryPtr->displayString = NULL;
    entryPtr->inset = XPAD;
    entryPtr->textLayout = NULL;
    entryPtr->layoutX = 0;
    entryPtr->layoutY = 0;
    entryPtr->leftIndex = 0;
    entryPtr->leftX = 0;
    entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
    entryPtr->textGC = None;
    entryPtr->selTextGC = None;
    entryPtr->highlightGC = None;
    entryPtr->avgWidth = 1;
    entryPtr->flags = 0;

    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);
#ifdef TK_KANJI_OK
    {
      Atom textatom = Tk_InternAtom(tkwin, "TEXT");
      Atom ctextatom = Tk_InternAtom(tkwin, "COMPOUND_TEXT");
      Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, textatom,
                      EntryFetchSelectionCtext,
                      (ClientData) entryPtr, ctextatom);
      Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, ctextatom,
                      EntryFetchSelectionCtext,
                      (ClientData) entryPtr, ctextatom);
    }
#endif /* TK_KANJI_OK */
    if (ConfigureEntry(interp, entryPtr, argc-2, argv+2, 0) != TCL_OK) {
      goto error;
    }

    interp->result = Tk_PathName(entryPtr->tkwin);
    return TCL_OK;

    error:
    Tk_DestroyWindow(entryPtr->tkwin);
    return TCL_ERROR;
}

/*
 *--------------------------------------------------------------
 *
 * EntryWidgetCmd --
 *
 *    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
EntryWidgetCmd(clientData, interp, argc, argv)
    ClientData clientData;          /* Information about entry widget. */
    Tcl_Interp *interp;             /* Current interpreter. */
    int argc;                       /* Number of arguments. */
    char **argv;              /* Argument strings. */
{
    register Entry *entryPtr = (Entry *) clientData;
    int result = TCL_OK;
    size_t length;
    int c;

    if (argc < 2) {
      Tcl_AppendResult(interp, "wrong # args: should be \"",
            argv[0], " option ?arg arg ...?\"", (char *) NULL);
      return TCL_ERROR;
    }
    Tcl_Preserve((ClientData) entryPtr);
    c = argv[1][0];
    length = strlen(argv[1]);
    if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)) {
      int index;
      int x, y, width, height;

      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " bbox index\"",
                (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
          goto error;
      }
      if ((index == entryPtr->numChars) && (index > 0)) {
          index--;
      }
      Tk_CharBbox(entryPtr->textLayout, index, &x, &y, &width, &height);
      sprintf(interp->result, "%d %d %d %d",
            x + entryPtr->layoutX, y + entryPtr->layoutY, width, height);
    } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
          && (length >= 2)) {
      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " cget option\"",
                (char *) NULL);
          goto error;
      }
      result = Tk_ConfigureValue(interp, entryPtr->tkwin, configSpecs,
            (char *) entryPtr, argv[2], 0);
    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
          && (length >= 2)) {
      if (argc == 2) {
          result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
                (char *) entryPtr, (char *) NULL, 0);
      } else if (argc == 3) {
          result = Tk_ConfigureInfo(interp, entryPtr->tkwin, configSpecs,
                (char *) entryPtr, argv[2], 0);
      } else {
          result = ConfigureEntry(interp, entryPtr, argc-2, argv+2,
                TK_CONFIG_ARGV_ONLY);
      }
    } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
      int first, last;

      if ((argc < 3) || (argc > 4)) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " delete firstIndex ?lastIndex?\"",
                (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &first) != TCL_OK) {
          goto error;
      }
      if (argc == 3) {
          last = first+1;
      } else {
          if (GetEntryIndex(interp, entryPtr, argv[3], &last) != TCL_OK) {
            goto error;
          }
      }
      if ((last >= first) && (entryPtr->state == tkNormalUid)) {
          DeleteChars(entryPtr, first, last-first);
      }
    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
      if (argc != 2) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " get\"", (char *) NULL);
          goto error;
      }
#ifdef TK_KANJI_OK
      interp->result = Tcl_DecodeWStr(interp, entryPtr->string, NULL);
#else
      interp->result = entryPtr->string;
#endif /* TK_KANJI_OK */
    } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
          && (length >= 2)) {
      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " icursor pos\"",
                (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &entryPtr->insertPos)
            != TCL_OK) {
          goto error;
      }
      EventuallyRedraw(entryPtr);
    } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
          && (length >= 3)) {
      int index;

      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " index string\"", (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
          goto error;
      }
      sprintf(interp->result, "%d", index);
    } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
          && (length >= 3)) {
      int index;

      if (argc != 4) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " insert index text\"",
                (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
          goto error;
      }
      if (entryPtr->state == tkNormalUid) {
          InsertChars(entryPtr, index, argv[3]);
      }
    } else if ((c == 's') && (length >= 2)
          && (strncmp(argv[1], "scan", length) == 0)) {
      int x;

      if (argc != 4) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " scan mark|dragto x\"", (char *) NULL);
          goto error;
      }
      if (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) {
          goto error;
      }
      if ((argv[2][0] == 'm')
            && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
          entryPtr->scanMarkX = x;
          entryPtr->scanMarkIndex = entryPtr->leftIndex;
      } else if ((argv[2][0] == 'd')
            && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
          EntryScanTo(entryPtr, x);
      } else {
          Tcl_AppendResult(interp, "bad scan option \"", argv[2],
                "\": must be mark or dragto", (char *) NULL);
          goto error;
      }
    } else if ((c == 's') && (length >= 2)
          && (strncmp(argv[1], "selection", length) == 0)) {
      int index, index2;

      if (argc < 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                argv[0], " select option ?index?\"", (char *) NULL);
          goto error;
      }
      length = strlen(argv[2]);
      c = argv[2][0];
      if ((c == 'c') && (strncmp(argv[2], "clear", length) == 0)) {
          if (argc != 3) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection clear\"", (char *) NULL);
            goto error;
          }
          if (entryPtr->selectFirst != -1) {
            entryPtr->selectFirst = entryPtr->selectLast = -1;
            EventuallyRedraw(entryPtr);
          }
          goto done;
      } else if ((c == 'p') && (strncmp(argv[2], "present", length) == 0)) {
          if (argc != 3) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection present\"", (char *) NULL);
            goto error;
          }
          if (entryPtr->selectFirst == -1) {
            interp->result = "0";
          } else {
            interp->result = "1";
          }
          goto done;
      }
      if (argc >= 4) {
          if (GetEntryIndex(interp, entryPtr, argv[3], &index) != TCL_OK) {
            goto error;
          }
      }
      if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
          if (argc != 4) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection adjust index\"",
                  (char *) NULL);
            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);
      } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
          if (argc != 4) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection from index\"",
                  (char *) NULL);
            goto error;
          }
          entryPtr->selectAnchor = index;
      } else if ((c == 'r') && (strncmp(argv[2], "range", length) == 0)) {
          if (argc != 5) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection range start end\"",
                  (char *) NULL);
            goto error;
          }
          if (GetEntryIndex(interp, entryPtr, argv[4], &index2) != TCL_OK) {
            goto error;
          }
          if (index >= index2) {
            entryPtr->selectFirst = 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);
      } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
          if (argc != 4) {
            Tcl_AppendResult(interp, "wrong # args: should be \"",
                  argv[0], " selection to index\"",
                  (char *) NULL);
            goto error;
          }
          EntrySelectTo(entryPtr, index);
      } else {
          Tcl_AppendResult(interp, "bad selection option \"", argv[2],
                "\": must be adjust, clear, from, present, range, or to",
                (char *) NULL);
          goto error;
      }
    } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
      int index, type, count, charsPerPage;
      double fraction, first, last;

      if (argc == 2) {
          EntryVisibleRange(entryPtr, &first, &last);
          sprintf(interp->result, "%g %g", first, last);
          goto done;
      } else if (argc == 3) {
          if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK) {
            goto error;
          }
      } else {
          type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
          index = entryPtr->leftIndex;
          switch (type) {
            case TK_SCROLL_ERROR:
                goto error;
            case TK_SCROLL_MOVETO:
                index = (int) ((fraction * entryPtr->numChars) + 0.5);
                break;
            case TK_SCROLL_PAGES:
                charsPerPage = ((Tk_Width(entryPtr->tkwin)
                      - 2*entryPtr->inset) / entryPtr->avgWidth) - 2;
                if (charsPerPage < 1) {
                  charsPerPage = 1;
                }
                index += charsPerPage*count;
                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);
#if defined(KINPUT2) || defined(XIM_IMPROVE) || defined(IME_AWARE)
    } else if ((c == 'x') && (strncmp(argv[1], "xypos", length) == 0)) {
      int index;

      if (argc != 3) {
          Tcl_AppendResult(interp, "wrong # args: should be \"",
                       argv[0], " xypos index\"",
                       (char *) NULL);
          goto error;
      }
      if (GetEntryIndex(interp, entryPtr, argv[2], &index) != TCL_OK ||
          EntryXYPos(interp, entryPtr, index) != TCL_OK) {
          goto error;
      }
#endif /* KINPUT2 || XIM_IMPROVE || IME_AWARE */
    } else {
      Tcl_AppendResult(interp, "bad option \"", argv[1],
            "\": must be bbox, cget, configure, delete, get, ",
#if defined(KINPUT2) || defined(XIM_IMPROVE) || defined(IME_AWARE)
            "icursor, index, insert, scan, selection, xview, or xypos",
#else
            "icursor, index, insert, scan, selection, or xview",
#endif /* KINPUT2 || XIM_IMPROVE || IME_AWARE */
            (char *) NULL);
      goto error;
    }
    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. */
{
    register Entry *entryPtr = (Entry *) memPtr;

    /*
     * Free up all the stuff that requires special handling, then
     * let Tk_FreeOptions handle all the standard option-related
     * stuff.
     */

#ifdef TK_KANJI_OK
    Tcl_FreeWStr(entryPtr->string);
#else
    ckfree(entryPtr->string);
#endif /* TK_KANJI_OK */
    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 != NULL) {
#ifdef TK_KANJI_OK
      ckfree((char *) entryPtr->displayString);
#else
      ckfree(entryPtr->displayString);
#endif /* TK_KANJI_OK */
    }
    Tk_FreeTextLayout(entryPtr->textLayout);
    Tk_FreeOptions(configSpecs, (char *) entryPtr, entryPtr->display, 0);
    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 interp->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, argc, argv, flags)
    Tcl_Interp *interp;       /* Used for error reporting. */
    register Entry *entryPtr; /* Information about widget;  may or may
                         * not already have values for some fields. */
    int argc;                 /* Number of valid entries in argv. */
    char **argv;        /* Arguments. */
    int flags;                /* Flags to pass to Tk_ConfigureWidget. */
{
    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;
    if (Tk_ConfigureWidget(interp, entryPtr->tkwin, configSpecs,
          argc, argv, (char *) entryPtr, flags) != TCL_OK) {
      return TCL_ERROR;
    }

    /*
     * 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);
    }

    /*
     * A few other options also need special processing, such as parsing
     * the geometry and setting the background from a 3-D border.
     */

    if ((entryPtr->state != tkNormalUid)
          && (entryPtr->state != tkDisabledUid)) {
      Tcl_AppendResult(interp, "bad state value \"", entryPtr->state,
            "\": must be normal or disabled", (char *) NULL);
      entryPtr->state = tkNormalUid;
      return TCL_ERROR;
    }

    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.
     */

    if (entryPtr->flags & GOT_FOCUS) {
      EntryFocusProc(entryPtr, 1);
    }

    /*
     * 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;

    EntryWorldChanged((ClientData) entryPtr);
    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;
    unsigned long mask;
    Entry *entryPtr;

    entryPtr = (Entry *) instanceData;

#ifdef TK_KANJI_OK
    {
      wchar wTmp = '0';
      entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, &wTmp, 1);
    }
#else
    entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
#endif /* TK_KANJI_OK */
    if (entryPtr->avgWidth == 0) {
      entryPtr->avgWidth = 1;
    }
    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. */
{
    register Entry *entryPtr = (Entry *) clientData;
    register Tk_Window tkwin = entryPtr->tkwin;
    int baseY, selStartX, selEndX, cursorX, x, w;
    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,
                &x, NULL, NULL, NULL);
          selStartX = x + entryPtr->layoutX;
      }
      if ((selStartX - entryPtr->selBorderWidth) < xBound) {
          Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast - 1,
                &x, NULL, &w, NULL);
          selEndX = x + w + 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 == tkNormalUid)
          && (entryPtr->flags & GOT_FOCUS)) {
      if (entryPtr->insertPos == 0) {
          cursorX = 0;
      } else if (entryPtr->insertPos >= entryPtr->numChars) {
          Tk_CharBbox(entryPtr->textLayout, entryPtr->numChars - 1,
                &x, NULL, &w, NULL);
          cursorX = x + w;
      } else {
          Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos,
                &x, NULL, NULL, NULL);
          cursorX = x;
      }
      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 first;

      if (entryPtr->selectFirst - entryPtr->leftIndex < 0) {
          first = entryPtr->leftIndex;
      } else {
          first = entryPtr->selectFirst;
      }
      Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC,
            entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
            first, 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 gc;

      if (entryPtr->flags & GOT_FOCUS) {
          gc = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
      } else {
          gc = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
      }
      Tk_DrawFocusHighlight(tkwin, gc, 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;
#ifdef TK_KANJI_OK
    wchar *p, *displayString;
#else
    char *p, *displayString;
#endif /* TK_KANJI_OK */

    /*
     * If we're displaying a special character instead of the value of
     * the entry, recompute the displayString.
     */

    if (entryPtr->displayString != NULL) {
#ifdef TK_KANJI_OK
      ckfree((char *) entryPtr->displayString);
#else
      ckfree(entryPtr->displayString);
#endif /* TK_KANJI_OK */
      entryPtr->displayString = NULL;
    }
    if (entryPtr->showChar != NULL) {
#ifdef TK_KANJI_OK
      entryPtr->displayString = 
      (wchar *) ckalloc(sizeof(wchar) * ((unsigned)(entryPtr->numChars + 1)));
#else
      entryPtr->displayString = (char *) ckalloc((unsigned)
            (entryPtr->numChars + 1));
#endif /* TK_KANJI_OK */
      for (p = entryPtr->displayString, i = entryPtr->numChars; i > 0;
            i--, p++) {
          *p = entryPtr->showChar[0];
      }
      *p = 0;
      displayString = entryPtr->displayString;
    } else {
      displayString = entryPtr->string;
    }
    Tk_FreeTextLayout(entryPtr->textLayout);
    entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
          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 += 1;
      }
      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, string)
    register Entry *entryPtr; /* Entry that is to get the new
                         * elements. */
    int index;                /* Add the new elements before this
                         * element. */
    char *string;       /* New characters to add (NULL-terminated
                         * string). */
{
    int length;
#ifdef TK_KANJI_OK
    wchar *wstr;

    wstr = Tcl_GetWStr(NULL, string, NULL);
    length = Tcl_WStrlen(wstr);
    if( length == 0 ) return;
    entryPtr->string = Tcl_InsertWStr(entryPtr->interp,
                              entryPtr->string, index, wstr);
    Tcl_FreeWStr(wstr);
#else
    char *new;

    length = strlen(string);
    if (length == 0) {
      return;
    }
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + length + 1));
    strncpy(new, entryPtr->string, (size_t) index);
    strcpy(new+index, string);
    strcpy(new+index+length, entryPtr->string+index);
    ckfree(entryPtr->string);
    entryPtr->string = new;
#endif /* TK_KANJI_OK */
    entryPtr->numChars += length;

    /*
     * 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 += length;
    }
    if (entryPtr->selectLast > index) {
      entryPtr->selectLast += length;
    }
    if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) {
      entryPtr->selectAnchor += length;
    }
    if (entryPtr->leftIndex > index) {
      entryPtr->leftIndex += length;
    }
    if (entryPtr->insertPos >= index) {
      entryPtr->insertPos += length;
    }
    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)
    register Entry *entryPtr; /* Entry widget to modify. */
    int index;                /* Index of first character to delete. */
    int count;                /* How many characters to delete. */
{
#ifndef TK_KANJI_OK
    char *new;
#endif /* TK_KANJI_OK */

    if ((index + count) > entryPtr->numChars) {
      count = entryPtr->numChars - index;
    }
    if (count <= 0) {
      return;
    }

#ifdef TK_KANJI_OK
    entryPtr->string = Tcl_DeleteWStr(entryPtr->interp, entryPtr->string,
                              index, count);
#else
    new = (char *) ckalloc((unsigned) (entryPtr->numChars + 1 - count));
    strncpy(new, entryPtr->string, (size_t) index);
    strcpy(new+index, entryPtr->string+index+count);
    ckfree(entryPtr->string);
    entryPtr->string = new;
#endif /* TK_KANJI_OK */
    entryPtr->numChars -= count;

    /*
     * 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 = 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;
#ifdef TK_KANJI_OK
    char *str = Tcl_DecodeWStr(entryPtr->interp, entryPtr->string, NULL);
#endif /* TK_KANJI_OK */

    if (entryPtr->textVarName == NULL) {
      newValue = NULL;
    } else {
      newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName,
#ifdef TK_KANJI_OK
            str, TCL_GLOBAL_ONLY);
#else
            entryPtr->string, TCL_GLOBAL_ONLY);
#endif /* TK_KANJI_OK */
    }

#ifdef TK_KANJI_OK
    if ((newValue != NULL) && (strcmp(newValue, str) != 0)) {
#else
    if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) {
#endif /* TK_KANJI_OK */
      /*
       * 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)
    register Entry *entryPtr;       /* Entry whose value is to be
                               * changed. */
    char *value;              /* New text to display in entry. */
{
#ifdef TK_KANJI_OK
    wchar *old = entryPtr->string;

    entryPtr->string = Tcl_GetWStr(NULL, value, NULL);
    entryPtr->numChars = Tcl_WStrlen(entryPtr->string);
    Tcl_FreeWStr(old);
#else
    ckfree(entryPtr->string);
    entryPtr->numChars = strlen(value);
    entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1));
    strcpy(entryPtr->string, value);
#endif /* TK_KANJI_OK */
    if (entryPtr->selectFirst != -1) {
      if (entryPtr->selectFirst >= entryPtr->numChars) {
          entryPtr->selectFirst = entryPtr->selectLast = -1;
      } else if (entryPtr->selectLast > entryPtr->numChars) {
          entryPtr->selectLast = entryPtr->numChars;
      }
    }
    if (entryPtr->leftIndex >= entryPtr->numChars) {
      entryPtr->leftIndex = entryPtr->numChars-1;
      if (entryPtr->leftIndex < 0) {
          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) {
      if (entryPtr->tkwin != NULL) {
          entryPtr->tkwin = NULL;
            Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
      }
      if (entryPtr->flags & REDRAW_PENDING) {
          Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
      }
      Tcl_EventuallyFree((ClientData) entryPtr, DestroyEntry);
    } 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;
    Tk_Window tkwin = entryPtr->tkwin;

    /*
     * 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 (tkwin != NULL) {
      entryPtr->tkwin = NULL;
      Tk_DestroyWindow(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 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 interp->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 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 interp->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 == -1) {
          interp->result = "selection isn't in entry";
          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)
    register 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);
      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)
    register Entry *entryPtr;       /* Information about widget. */
    int index;                      /* 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;                     /* 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 count;
#ifdef TK_KANJI_OK
    char *str;
    wchar *displayString;
#else
    char *displayString;
#endif /* TK_KANJI_OK */

    if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
      return -1;
    }
#ifdef TK_KANJI_OK
    if (entryPtr->displayString == NULL) {
        displayString = entryPtr->string;
    } else {
        displayString = entryPtr->displayString;
    }
    count = entryPtr->selectLast - entryPtr->selectFirst;
    str = Tk_WStrToString(displayString + entryPtr->selectFirst, count);
    if (str == NULL) return 0;
    count = strlen(str) - offset;
#else
    count = entryPtr->selectLast - entryPtr->selectFirst - offset;
#endif /* TK_KANJI_OK */
    if (count > maxBytes) {
      count = maxBytes;
    }
    if (count <= 0) {
#ifdef TK_KANJI_OK
      ckfree(str);
#endif /* TK_KANJI_OK */
      return 0;
    }
#ifdef TK_KANJI_OK
    strncpy(buffer, str + offset, (size_t) count);
    ckfree(str);
#else
    if (entryPtr->displayString == NULL) {
      displayString = entryPtr->string;
    } else {
      displayString = entryPtr->displayString;
    }
    strncpy(buffer, displayString + entryPtr->selectFirst + offset,
          (size_t) count);
#endif /* TK_KANJI_OK */
    buffer[count] = '\0';
    return count;
}
#ifdef TK_KANJI_OK

/*
 *----------------------------------------------------------------------
 *
 * EntryFetchSelectionCtext --
 *
 *    This procedure is same as EntryFetchSelection except it
 *    converts the selection data to compound text encoding.
 *
 * Results:
 *    See EntryFetchSelection.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

static int
EntryFetchSelectionCtext(clientData, offset, buffer, maxBytes)
    ClientData clientData;          /* Information about entry widget. */
    int offset;                     /* 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 count;
    char *str;
    wchar *displayString;

    if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
      return -1;
    }
    if (entryPtr->displayString == NULL) {
      displayString = entryPtr->string;
    } else {
      displayString = entryPtr->displayString;
    }
    count = entryPtr->selectLast - entryPtr->selectFirst;
    str = Tk_WStrToCtext(displayString + entryPtr->selectFirst, count);
    if (str == NULL) return 0;

    count = strlen(str) - offset;
    if (count > maxBytes) {
      count = maxBytes;
    }
    if (count <= 0) {
      ckfree(str);
      return 0;
    }
    strncpy(buffer, str + offset, (size_t) count);
    ckfree(str);
    buffer[count] = '\0';
    return count;
}
#endif /* TK_KANJI_OK */

/*
 *----------------------------------------------------------------------
 *
 * 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 != -1) && 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)
    register 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) + 1;
      if (charsInWindow > entryPtr->numChars) {
          /*
           * If all chars were visible, then charsInWindow will be
           * the index just after the last char that was visible.
           */
           
          charsInWindow = entryPtr->numChars;
      }
      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[100];
    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. */
{
    register Entry *entryPtr = (Entry *) clientData;

    if (!(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)
    register 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);
      }
    } else {
      entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON);
      entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
    }
    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. */
{
    register Entry *entryPtr = (Entry *) clientData;
    char *value;
#ifdef TK_KANJI_OK
    wchar *wstr;
#endif /* TK_KANJI_OK */

    /*
     * 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)) {
#ifdef TK_KANJI_OK
          Tcl_SetVar(interp, entryPtr->textVarName,
                   Tcl_DecodeWStr(entryPtr->interp, entryPtr->string, NULL),
                   TCL_GLOBAL_ONLY);
#else
          Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string,
                TCL_GLOBAL_ONLY);
#endif /* TK_KANJI_OK */
          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 = "";
    }
#ifdef TK_KANJI_OK
    wstr = Tcl_GetWStr(NULL, value, NULL);
    if (Tcl_WStrcmp(wstr, entryPtr->string) != 0) {
      EntrySetValue(entryPtr, value);
    }
    Tcl_FreeWStr(wstr);
#else
    if (strcmp(value, entryPtr->string) != 0) {
      EntrySetValue(entryPtr, value);
    }
#endif /* TK_KANJI_OK */
    return (char *) NULL;
}
#if defined(KINPUT2) || defined(XIM_IMPROVE) || defined(IME_AWARE)

/*
 *--------------------------------------------------------------
 *
 * EntryXYPos --
 *
 *    This procedure returns XY coordinates of the point
 *    specified by its index.  The Y coordinate is of
 *    the baseline.
 *
 * Results:
 *    The return value is always TCL_OK (i.e. no errors possible).
 *    If the point is visible, this procedure will return a list
 *    containing its X and Y position.  Othewise, an empty string
 *    will be returned.
 *
 * Side effects:
 *    None.
 *
 *--------------------------------------------------------------
 */

static int
EntryXYPos(interp, entryPtr, index)
    Tcl_Interp *interp;       /* For the result */
    Entry *entryPtr;          /* Entry for which the index is being
                         * specified. */
    int index;                /* Character index for which its position
                         * is to be retrieved. */
{
    Tk_Window tkwin = entryPtr->tkwin;
    int leftidx = entryPtr->leftIndex;
    int posX, posY;
    int nchars;

    if (entryPtr->tkwin != NULL && Tk_IsMapped(tkwin) && index >= leftidx) {
      Tk_FontMetrics fm;
      
      /*
       * Compute X-coordinate of the "index" character
       */
      
      if (index == leftidx) {
          posX = entryPtr->inset;
      } else {
          nchars = Tk_MeasureChars(entryPtr->tkfont, 
                             entryPtr->string + leftidx,
                             index - leftidx,
                             Tk_Width(tkwin) - entryPtr->inset,
                             TK_PARTIAL_OK,
                             &posX);
          posX += entryPtr->inset;
          if (nchars < index - leftidx) return TCL_OK;
      }
      
      /*
       * Y-coordinate is of the baseline
       */
      Tk_GetFontMetrics(entryPtr->tkfont, &fm);
      posY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
      sprintf(interp->result, "%d %d", posX, posY);
    }

    return TCL_OK;
}
#endif /* KINPUT2 || XIM_IMPROVE || IME_AWARE */

Generated by  Doxygen 1.6.0   Back to index