diff options
Diffstat (limited to 'generic/tkEntry.c')
-rw-r--r-- | generic/tkEntry.c | 2313 |
1 files changed, 2313 insertions, 0 deletions
diff --git a/generic/tkEntry.c b/generic/tkEntry.c new file mode 100644 index 0000000..35cc66c --- /dev/null +++ b/generic/tkEntry.c @@ -0,0 +1,2313 @@ +/* + * 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. + * + * SCCS: @(#) tkEntry.c 1.112 97/11/06 16:56:16 + */ + +#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". + */ + + char *string; /* Pointer to storage for string; + * NULL-terminated; malloc-ed. */ + 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. */ + char *showChar; /* Value of -show option. If non-NULL, first + * character is used for displaying all + * characters in entry. Malloc'ed. */ + 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). */ + char *displayString; /* If non-NULL, points to string with same + * length as string but whose characters + * are all equal to showChar. Malloc'ed. */ + 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}, + {TK_CONFIG_STRING, "-show", "show", "Show", + DEF_ENTRY_SHOW, Tk_Offset(Entry, showChar), TK_CONFIG_NULL_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)); +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)); +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); + 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 = 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); + 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; + } + interp->result = entryPtr->string; + } 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); + } else { + Tcl_AppendResult(interp, "bad option \"", argv[1], + "\": must be bbox, cget, configure, delete, get, ", + "icursor, index, insert, scan, selection, or xview", + (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. + */ + + 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 != NULL) { + ckfree(entryPtr->displayString); + } + 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; + + entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1); + 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; + char *p, *displayString; + + /* + * If we're displaying a special character instead of the value of + * the entry, recompute the displayString. + */ + + if (entryPtr->displayString != NULL) { + ckfree(entryPtr->displayString); + entryPtr->displayString = NULL; + } + if (entryPtr->showChar != NULL) { + entryPtr->displayString = (char *) ckalloc((unsigned) + (entryPtr->numChars + 1)); + 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; + 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; + 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. */ +{ + char *new; + + if ((index + count) > entryPtr->numChars) { + count = entryPtr->numChars - index; + } + if (count <= 0) { + return; + } + + 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; + 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; + + 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) + register Entry *entryPtr; /* Entry whose value is to be + * changed. */ + char *value; /* New text to display in entry. */ +{ + ckfree(entryPtr->string); + entryPtr->numChars = strlen(value); + entryPtr->string = (char *) ckalloc((unsigned) (entryPtr->numChars + 1)); + strcpy(entryPtr->string, value); + 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; + char *displayString; + + if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) { + return -1; + } + count = entryPtr->selectLast - entryPtr->selectFirst - offset; + if (count > maxBytes) { + count = maxBytes; + } + if (count <= 0) { + return 0; + } + if (entryPtr->displayString == NULL) { + displayString = entryPtr->string; + } else { + displayString = entryPtr->displayString; + } + strncpy(buffer, displayString + entryPtr->selectFirst + offset, + (size_t) count); + buffer[count] = '\0'; + return count; +} + +/* + *---------------------------------------------------------------------- + * + * 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; + + /* + * 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 = ""; + } + if (strcmp(value, entryPtr->string) != 0) { + EntrySetValue(entryPtr, value); + } + return (char *) NULL; +} |