summaryrefslogtreecommitdiff
path: root/tk/generic/tkEntry.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk/generic/tkEntry.c')
-rw-r--r--tk/generic/tkEntry.c2179
1 files changed, 1899 insertions, 280 deletions
diff --git a/tk/generic/tkEntry.c b/tk/generic/tkEntry.c
index 6ed11d6f9b7..5ab3846d292 100644
--- a/tk/generic/tkEntry.c
+++ b/tk/generic/tkEntry.c
@@ -1,12 +1,15 @@
/*
- * tkEntry.c --
+ * Entry.c --
*
- * This module implements entry widgets for the Tk
- * toolkit. An entry displays a string and allows
- * the string to be edited.
+ * This module implements entry and spinbox widgets for the Tk toolkit.
+ * An entry displays a string and allows the string to be edited.
+ * A spinbox expands on the entry by adding up/down buttons that control
+ * the value of the entry widget.
*
* Copyright (c) 1990-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Ajuba Solutions.
+ * Copyright (c) 2002 ActiveState Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -17,8 +20,12 @@
#include "tkInt.h"
#include "default.h"
+enum EntryType {
+ TK_ENTRY, TK_SPINBOX
+};
+
/*
- * A data structure of the following type is kept for each entry
+ * A data structure of the following type is kept for each Entry
* widget managed by this file:
*/
@@ -34,13 +41,13 @@ typedef struct {
Tcl_Command widgetCmd; /* Token for entry's widget command. */
Tk_OptionTable optionTable; /* Table that defines configuration options
* available for this widget. */
-
+ enum EntryType type; /* Specialized type of Entry widget */
/*
* Fields that are set by widget commands other than "configure".
*/
- char *string; /* Pointer to storage for string;
+ CONST char *string; /* Pointer to storage for string;
* NULL-terminated; malloc-ed. */
int insertPos; /* Character index before which next typed
* character will be inserted. */
@@ -72,12 +79,19 @@ typedef struct {
Tk_3DBorder normalBorder; /* Used for drawing border around whole
* window, plus used for background. */
+ Tk_3DBorder disabledBorder; /* Used for drawing border around whole
+ * window in disabled state, plus used for
+ * background. */
+ Tk_3DBorder readonlyBorder; /* Used for drawing border around whole
+ * window in readonly state, 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 *dfgColorPtr; /* Text color in disabled mode. */
XColor *highlightBgColorPtr;/* Color for drawing traversal highlight
* area when highlight is off. */
XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
@@ -99,9 +113,6 @@ typedef struct {
* characters. */
int selBorderWidth; /* Width of border around selection. */
XColor *selFgColorPtr; /* Foreground color for selected text. */
- char *showChar; /* Value of -show option. If non-NULL, first
- * character is used for displaying all
- * characters in entry. Malloc'ed. */
int state; /* Normal or disabled. Entry is read-only
* when disabled. */
char *textVarName; /* Name of variable (malloc'ed) or NULL.
@@ -115,23 +126,27 @@ typedef struct {
char *scrollCmd; /* Command prefix for communicating with
* scrollbar(s). Malloc'ed. NULL means
* no command to issue. */
+ char *showChar; /* Value of -show option. If non-NULL, first
+ * character is used for displaying all
+ * characters in entry. Malloc'ed.
+ * This is only used by the Entry widget. */
/*
* Fields whose values are derived from the current values of the
* configuration settings above.
*/
+ CONST char *displayString; /* String to use when displaying. This may
+ * be a pointer to string, or a pointer to
+ * malloced memory with the same character
+ * length as string but whose characters
+ * are all equal to showChar. */
int numBytes; /* Length of string in bytes. */
int numChars; /* Length of string in characters. Both
* string and displayString have the same
* character length, but may have different
* byte lengths due to being made from
* different UTF-8 characters. */
- char *displayString; /* String to use when displaying. This may
- * be a pointer to string, or a pointer to
- * malloced memory with the same character
- * length as string but whose characters
- * are all equal to showChar. */
int numDisplayBytes; /* Length of displayString in bytes. */
int inset; /* Number of pixels on the left and right
* sides that are taken up by XPAD, borderWidth
@@ -149,19 +164,78 @@ typedef struct {
GC selTextGC; /* For drawing selected text. */
GC highlightGC; /* For drawing traversal highlight. */
int avgWidth; /* Width of average character. */
+ int xWidth; /* Extra width to reserve for widget.
+ * Used by spinboxes for button space. */
int flags; /* Miscellaneous flags; see below for
* definitions. */
- Tk_TSOffset tsoffset;
+ int validate; /* Non-zero means try to validate */
char *validateCmd; /* Command prefix to use when invoking
* validate command. NULL means don't
* invoke commands. Malloc'ed. */
- int validate; /* Non-zero means try to validate */
char *invalidCmd; /* Command called when a validation returns 0
* (successfully fails), defaults to {}. */
+
} Entry;
/*
+ * A data structure of the following type is kept for each spinbox
+ * widget managed by this file:
+ */
+
+typedef struct {
+ Entry entry; /* A pointer to the generic entry structure.
+ * This must be the first element of the
+ * Spinbox. */
+
+ /*
+ * Spinbox specific configuration settings.
+ */
+
+ Tk_3DBorder activeBorder; /* Used for drawing border around active
+ * buttons. */
+ Tk_3DBorder buttonBorder; /* Used for drawing border around buttons. */
+ Tk_Cursor bCursor; /* cursor for buttons, or None. */
+ int bdRelief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
+ int buRelief; /* 3-D effect: TK_RELIEF_RAISED, etc. */
+ char *command; /* Command to invoke for spin buttons.
+ * NULL means no command to issue. */
+
+ /*
+ * Spinbox specific fields for use with configuration settings above.
+ */
+
+ int wrap; /* whether to wrap around when spinning */
+
+ int selElement; /* currently selected control */
+ int curElement; /* currently mouseover control */
+
+ int repeatDelay; /* repeat delay */
+ int repeatInterval; /* repeat interval */
+
+ double fromValue; /* Value corresponding to left/top of dial */
+ double toValue; /* Value corresponding to right/bottom
+ * of dial */
+ double increment; /* If > 0, all values are rounded to an
+ * even multiple of this value. */
+ char *formatBuf; /* string into which to format value.
+ * Malloc'ed. */
+ char *reqFormat; /* Sprintf conversion specifier used for the
+ * value that the users requests. Malloc'ed. */
+ char *valueFormat; /* Sprintf conversion specifier used for
+ * the value. */
+ char digitFormat[10]; /* Sprintf conversion specifier computed from
+ * digits and other information; used for
+ * the value. */
+
+ char *valueStr; /* Values List. Malloc'ed. */
+ Tcl_Obj *listObj; /* Pointer to the list object being used */
+ int eIndex; /* Holds the current index into elements */
+ int nElements; /* Holds the current count of elements */
+
+} Spinbox;
+
+/*
* Assigned bits of "flags" fields of Entry structures, and what those
* bits mean:
*
@@ -205,17 +279,23 @@ typedef struct {
#define YPAD 1
/*
+ * A comparison function for double values. For Spinboxes.
+ */
+#define MIN_DBL_VAL 1E-9
+#define DOUBLES_EQ(d1, d2) (fabs((d1) - (d2)) < MIN_DBL_VAL)
+
+/*
* The following enum is used to define a type for the -state option
* of the Entry widget. These values are used as indices into the
* string table below.
*/
enum state {
- STATE_DISABLED, STATE_NORMAL
+ STATE_DISABLED, STATE_NORMAL, STATE_READONLY
};
static char *stateStrings[] = {
- "disabled", "normal", (char *) NULL
+ "disabled", "normal", "readonly", (char *) NULL
};
/*
@@ -231,16 +311,16 @@ enum validateType {
/*
* These extra enums are for use with EntryValidateChange
*/
- VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT
+ VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT, VALIDATE_BUTTON
};
#define DEF_ENTRY_VALIDATE "none"
#define DEF_ENTRY_INVALIDCMD ""
/*
- * Information used for argv parsing.
+ * Information used for Entry objv parsing.
*/
-static Tk_OptionSpec optionSpecs[] = {
+static Tk_OptionSpec entryOptSpec[] = {
{TK_OPTION_BORDER, "-background", "background", "Background",
DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder),
0, (ClientData) DEF_ENTRY_BG_MONO, 0},
@@ -254,6 +334,13 @@ static Tk_OptionSpec optionSpecs[] = {
{TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor),
TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_BORDER, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_ENTRY_DISABLED_BG_COLOR, -1,
+ Tk_Offset(Entry, disabledBorder), TK_OPTION_NULL_OK,
+ (ClientData) DEF_ENTRY_DISABLED_BG_MONO, 0},
+ {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_ENTRY_DISABLED_FG, -1,
+ Tk_Offset(Entry, dfgColorPtr), TK_OPTION_NULL_OK, 0, 0},
{TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
"ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1,
Tk_Offset(Entry, exportSelection), 0, 0, 0},
@@ -298,6 +385,10 @@ static Tk_OptionSpec optionSpecs[] = {
(char *) NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0},
{TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0},
+ {TK_OPTION_BORDER, "-readonlybackground", "readonlyBackground",
+ "ReadonlyBackground", DEF_ENTRY_READONLY_BG_COLOR, -1,
+ Tk_Offset(Entry, readonlyBorder), TK_OPTION_NULL_OK,
+ (ClientData) DEF_ENTRY_READONLY_BG_MONO, 0},
{TK_OPTION_RELIEF, "-relief", "relief", "Relief",
DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief),
0, 0, 0},
@@ -341,11 +432,166 @@ static Tk_OptionSpec optionSpecs[] = {
};
/*
- * Flags for GetEntryIndex procedure:
+ * Information used for Spinbox objv parsing.
*/
-#define ZERO_OK 1
-#define LAST_PLUS_ONE_OK 2
+#define DEF_SPINBOX_REPEAT_DELAY "400"
+#define DEF_SPINBOX_REPEAT_INTERVAL "100"
+
+#define DEF_SPINBOX_CMD ""
+
+#define DEF_SPINBOX_FROM "0"
+#define DEF_SPINBOX_TO "0"
+#define DEF_SPINBOX_INCREMENT "1"
+#define DEF_SPINBOX_FORMAT ""
+
+#define DEF_SPINBOX_VALUES ""
+#define DEF_SPINBOX_WRAP "0"
+
+static Tk_OptionSpec sbOptSpec[] = {
+ {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Background",
+ DEF_BUTTON_ACTIVE_BG_COLOR, -1, Tk_Offset(Spinbox, activeBorder),
+ 0, (ClientData) DEF_BUTTON_ACTIVE_BG_MONO, 0},
+ {TK_OPTION_BORDER, "-background", "background", "Background",
+ DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder),
+ 0, (ClientData) DEF_ENTRY_BG_MONO, 0},
+ {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
+ {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
+ {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth),
+ 0, 0, 0},
+ {TK_OPTION_BORDER, "-buttonbackground", "Button.background", "Background",
+ DEF_BUTTON_BG_COLOR, -1, Tk_Offset(Spinbox, buttonBorder),
+ 0, (ClientData) DEF_BUTTON_BG_MONO, 0},
+ {TK_OPTION_CURSOR, "-buttoncursor", "Button.cursor", "Cursor",
+ DEF_BUTTON_CURSOR, -1, Tk_Offset(Spinbox, bCursor),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_RELIEF, "-buttondownrelief", "Button.relief", "Relief",
+ DEF_BUTTON_RELIEF, -1, Tk_Offset(Spinbox, bdRelief),
+ 0, 0, 0},
+ {TK_OPTION_RELIEF, "-buttonuprelief", "Button.relief", "Relief",
+ DEF_BUTTON_RELIEF, -1, Tk_Offset(Spinbox, buRelief),
+ 0, 0, 0},
+ {TK_OPTION_STRING, "-command", "command", "Command",
+ DEF_SPINBOX_CMD, -1, Tk_Offset(Spinbox, command),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_BORDER, "-disabledbackground", "disabledBackground",
+ "DisabledBackground", DEF_ENTRY_DISABLED_BG_COLOR, -1,
+ Tk_Offset(Entry, disabledBorder), TK_OPTION_NULL_OK,
+ (ClientData) DEF_ENTRY_DISABLED_BG_MONO, 0},
+ {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground",
+ "DisabledForeground", DEF_ENTRY_DISABLED_FG, -1,
+ Tk_Offset(Entry, dfgColorPtr), TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection",
+ "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1,
+ Tk_Offset(Entry, exportSelection), 0, 0, 0},
+ {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
+ (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
+ {TK_OPTION_FONT, "-font", "font", "Font",
+ DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0},
+ {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
+ DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0,
+ 0, 0},
+ {TK_OPTION_STRING, "-format", "format", "Format",
+ DEF_SPINBOX_FORMAT, -1, Tk_Offset(Spinbox, reqFormat),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_DOUBLE, "-from", "from", "From",
+ DEF_SPINBOX_FROM, -1, Tk_Offset(Spinbox, fromValue), 0, 0, 0},
+ {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground",
+ "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG,
+ -1, Tk_Offset(Entry, highlightBgColorPtr),
+ 0, 0, 0},
+ {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
+ DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr),
+ 0, 0, 0},
+ {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
+ "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1,
+ Tk_Offset(Entry, highlightWidth), 0, 0, 0},
+ {TK_OPTION_DOUBLE, "-increment", "increment", "Increment",
+ DEF_SPINBOX_INCREMENT, -1, Tk_Offset(Spinbox, increment), 0, 0, 0},
+ {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground",
+ DEF_ENTRY_INSERT_BG, -1, Tk_Offset(Entry, insertBorder),
+ 0, 0, 0},
+ {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth",
+ "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1,
+ Tk_Offset(Entry, insertBorderWidth), 0,
+ (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0},
+ {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime",
+ DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime),
+ 0, 0, 0},
+ {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime",
+ DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime),
+ 0, 0, 0},
+ {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
+ DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth),
+ 0, 0, 0},
+ {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand",
+ DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_SYNONYM, "-invcmd", (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0},
+ {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify",
+ DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0},
+ {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
+ DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief),
+ 0, 0, 0},
+ {TK_OPTION_BORDER, "-readonlybackground", "readonlyBackground",
+ "ReadonlyBackground", DEF_ENTRY_READONLY_BG_COLOR, -1,
+ Tk_Offset(Entry, readonlyBorder), TK_OPTION_NULL_OK,
+ (ClientData) DEF_ENTRY_READONLY_BG_MONO, 0},
+ {TK_OPTION_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
+ DEF_SPINBOX_REPEAT_DELAY, -1, Tk_Offset(Spinbox, repeatDelay),
+ 0, 0, 0},
+ {TK_OPTION_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
+ DEF_SPINBOX_REPEAT_INTERVAL, -1, Tk_Offset(Spinbox, repeatInterval),
+ 0, 0, 0},
+ {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground",
+ DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder),
+ 0, (ClientData) DEF_ENTRY_SELECT_MONO, 0},
+ {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth",
+ "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1,
+ Tk_Offset(Entry, selBorderWidth),
+ 0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0},
+ {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background",
+ DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr),
+ 0, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0},
+ {TK_OPTION_STRING_TABLE, "-state", "state", "State",
+ DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state),
+ 0, (ClientData) stateStrings, 0},
+ {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
+ DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus),
+ TK_CONFIG_NULL_OK, 0, 0},
+ {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable",
+ DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName),
+ TK_CONFIG_NULL_OK, 0, 0},
+ {TK_OPTION_DOUBLE, "-to", "to", "To",
+ DEF_SPINBOX_TO, -1, Tk_Offset(Spinbox, toValue), 0, 0, 0},
+ {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate",
+ DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate),
+ 0, (ClientData) validateStrings, 0},
+ {TK_OPTION_STRING, "-validatecommand", "validateCommand", "ValidateCommand",
+ (char *) NULL, -1, Tk_Offset(Entry, validateCmd),
+ TK_CONFIG_NULL_OK, 0, 0},
+ {TK_OPTION_STRING, "-values", "values", "Values",
+ DEF_SPINBOX_VALUES, -1, Tk_Offset(Spinbox, valueStr),
+ TK_OPTION_NULL_OK, 0, 0},
+ {TK_OPTION_SYNONYM, "-vcmd", (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, -1, 0, (ClientData) "-validatecommand", 0},
+ {TK_OPTION_INT, "-width", "width", "Width",
+ DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0},
+ {TK_OPTION_BOOLEAN, "-wrap", "wrap", "Wrap",
+ DEF_SPINBOX_WRAP, -1, Tk_Offset(Spinbox, wrap), 0, 0, 0},
+ {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
+ DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd),
+ TK_CONFIG_NULL_OK, 0, 0},
+ {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, -1, 0, 0, 0}
+};
/*
* The following tables define the entry widget commands (and sub-
@@ -353,27 +599,74 @@ static Tk_OptionSpec optionSpecs[] = {
* enumerated types used to dispatch the entry widget command.
*/
-static char *commandNames[] = {
+static CONST char *entryCmdNames[] = {
"bbox", "cget", "configure", "delete", "get", "icursor", "index",
"insert", "scan", "selection", "validate", "xview", (char *) NULL
};
-enum command {
+enum entryCmd {
COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE,
COMMAND_GET, COMMAND_ICURSOR, COMMAND_INDEX, COMMAND_INSERT,
COMMAND_SCAN, COMMAND_SELECTION, COMMAND_VALIDATE, COMMAND_XVIEW
};
-static char *selCommandNames[] = {
+static CONST char *selCmdNames[] = {
"adjust", "clear", "from", "present", "range", "to", (char *) NULL
};
-enum selcommand {
+enum selCmd {
SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM,
SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO
};
/*
+ * The following tables define the spinbox widget commands (and sub-
+ * commands) and map the indexes into the string tables into
+ * enumerated types used to dispatch the spinbox widget command.
+ */
+
+static CONST char *sbCmdNames[] = {
+ "bbox", "cget", "configure", "delete", "get", "icursor", "identify",
+ "index", "insert", "invoke", "scan", "selection", "set",
+ "validate", "xview", (char *) NULL
+};
+
+enum sbCmd {
+ SB_CMD_BBOX, SB_CMD_CGET, SB_CMD_CONFIGURE, SB_CMD_DELETE,
+ SB_CMD_GET, SB_CMD_ICURSOR, SB_CMD_IDENTIFY, SB_CMD_INDEX,
+ SB_CMD_INSERT, SB_CMD_INVOKE, SB_CMD_SCAN, SB_CMD_SELECTION,
+ SB_CMD_SET, SB_CMD_VALIDATE, SB_CMD_XVIEW
+};
+
+static CONST char *sbSelCmdNames[] = {
+ "adjust", "clear", "element", "from", "present", "range", "to",
+ (char *) NULL
+};
+
+enum sbselCmd {
+ SB_SEL_ADJUST, SB_SEL_CLEAR, SB_SEL_ELEMENT, SB_SEL_FROM,
+ SB_SEL_PRESENT, SB_SEL_RANGE, SB_SEL_TO
+};
+
+/*
+ * Extra for selection of elements
+ */
+
+static CONST char *selElementNames[] = {
+ "none", "buttondown", "buttonup", (char *) NULL, "entry"
+};
+enum selelement {
+ SEL_NONE, SEL_BUTTONDOWN, SEL_BUTTONUP, SEL_NULL, SEL_ENTRY
+};
+
+/*
+ * Flags for GetEntryIndex procedure:
+ */
+
+#define ZERO_OK 1
+#define LAST_PLUS_ONE_OK 2
+
+/*
* Forward declarations for procedures defined later in this file:
*/
@@ -399,21 +692,23 @@ static void EntryLostSelection _ANSI_ARGS_((
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));
+ CONST 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));
+ Tcl_Interp *interp, CONST char *name1,
+ CONST char *name2, int flags));
static void EntryUpdateScrollbar _ANSI_ARGS_((Entry *entryPtr));
static int EntryValidate _ANSI_ARGS_((Entry *entryPtr,
char *cmd));
static int EntryValidateChange _ANSI_ARGS_((Entry *entryPtr,
- char *change, char *new, int index, int type));
+ char *change, CONST char *new, int index,
+ int type));
static void ExpandPercents _ANSI_ARGS_((Entry *entryPtr,
- char *before, char *change, char *new,
+ CONST char *before, char *change, CONST char *new,
int index, int type, Tcl_DString *dsPtr));
-static void EntryValueChanged _ANSI_ARGS_((Entry *entryPtr));
+static void EntryValueChanged _ANSI_ARGS_((Entry *entryPtr,
+ CONST char *newValue));
static void EntryVisibleRange _ANSI_ARGS_((Entry *entryPtr,
double *firstPtr, double *lastPtr));
static int EntryWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
@@ -427,14 +722,26 @@ static void InsertChars _ANSI_ARGS_((Entry *entryPtr, int index,
char *string));
/*
- * The structure below defines entry class behavior by means of procedures
+ * These forward declarations are the spinbox specific ones:
+ */
+
+static int SpinboxWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int GetSpinboxElement _ANSI_ARGS_((Spinbox *sbPtr,
+ int x, int y));
+static int SpinboxInvoke _ANSI_ARGS_((Tcl_Interp *interp,
+ Spinbox *sbPtr, int element));
+static int ComputeFormat _ANSI_ARGS_((Spinbox *sbPtr));
+
+/*
+ * The structure below defines widget class behavior by means of procedures
* that can be invoked from generic window code.
*/
-static TkClassProcs entryClass = {
- NULL, /* createProc. */
- EntryWorldChanged, /* geometryProc. */
- NULL /* modalProc. */
+static Tk_ClassProcs entryClass = {
+ sizeof(Tk_ClassProcs), /* size */
+ EntryWorldChanged, /* worldChangedProc */
};
@@ -458,7 +765,7 @@ static TkClassProcs entryClass = {
int
Tk_EntryObjCmd(clientData, interp, objc, objv)
- ClientData clientData; /* Either NULL or pointer to option table. */
+ ClientData clientData; /* NULL. */
Tcl_Interp *interp; /* Current interpreter. */
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
@@ -466,25 +773,7 @@ Tk_EntryObjCmd(clientData, interp, objc, objv)
register Entry *entryPtr;
Tk_OptionTable optionTable;
Tk_Window tkwin;
-
- optionTable = (Tk_OptionTable) clientData;
- if (optionTable == NULL) {
- Tcl_CmdInfo info;
- char *name;
-
- /*
- * We haven't created the option table for this widget class
- * yet. Do it now and save the table as the clientData for
- * the command, so we'll have access to it in future
- * invocations of the command.
- */
-
- optionTable = Tk_CreateOptionTable(interp, optionSpecs);
- name = Tcl_GetString(objv[0]);
- Tcl_GetCommandInfo(interp, name, &info);
- info.objClientData = (ClientData) optionTable;
- Tcl_SetCommandInfo(interp, name, &info);
- }
+ char *tmp;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
@@ -498,12 +787,22 @@ Tk_EntryObjCmd(clientData, interp, objc, objv)
}
/*
+ * Create the option table for this widget class. If it has already
+ * been created, Tk will return the cached value.
+ */
+
+ optionTable = Tk_CreateOptionTable(interp, entryOptSpec);
+
+ /*
* 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).
+ * initialized already (e.g. resource pointers). Only the non-NULL/0
+ * data must be initialized as memset covers the rest.
*/
entryPtr = (Entry *) ckalloc(sizeof(Entry));
+ memset((VOID *) entryPtr, 0, sizeof(Entry));
+
entryPtr->tkwin = tkwin;
entryPtr->display = Tk_Display(tkwin);
entryPtr->interp = interp;
@@ -511,62 +810,35 @@ Tk_EntryObjCmd(clientData, interp, objc, objv)
Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd,
(ClientData) entryPtr, EntryCmdDeletedProc);
entryPtr->optionTable = optionTable;
- entryPtr->string = (char *) ckalloc(1);
- entryPtr->string[0] = '\0';
- entryPtr->insertPos = 0;
+ entryPtr->type = TK_ENTRY;
+ tmp = (char *) ckalloc(1);
+ tmp[0] = '\0';
+ entryPtr->string = tmp;
entryPtr->selectFirst = -1;
entryPtr->selectLast = -1;
- entryPtr->selectAnchor = 0;
- entryPtr->scanMarkX = 0;
- entryPtr->scanMarkIndex = 0;
- entryPtr->normalBorder = NULL;
- entryPtr->borderWidth = 0;
entryPtr->cursor = None;
entryPtr->exportSelection = 1;
- entryPtr->tkfont = NULL;
- entryPtr->fgColorPtr = NULL;
- entryPtr->highlightBgColorPtr = NULL;
- entryPtr->highlightColorPtr = NULL;
- entryPtr->highlightWidth = 0;
- entryPtr->insertBorder = NULL;
- entryPtr->insertBorderWidth = 0;
- entryPtr->insertOffTime = 0;
- entryPtr->insertOnTime = 0;
- entryPtr->insertWidth = 0;
entryPtr->justify = TK_JUSTIFY_LEFT;
entryPtr->relief = TK_RELIEF_FLAT;
- entryPtr->selBorder = NULL;
- entryPtr->selBorderWidth = 0;
- entryPtr->selFgColorPtr = NULL;
- entryPtr->showChar = NULL;
entryPtr->state = STATE_NORMAL;
- entryPtr->textVarName = NULL;
- entryPtr->takeFocus = NULL;
- entryPtr->prefWidth = 0;
- entryPtr->scrollCmd = NULL;
- entryPtr->numBytes = 0;
- entryPtr->numChars = 0;
entryPtr->displayString = entryPtr->string;
- entryPtr->numDisplayBytes = 0;
entryPtr->inset = XPAD;
- entryPtr->textLayout = NULL;
- entryPtr->layoutX = 0;
- entryPtr->layoutY = 0;
- entryPtr->leftX = 0;
- entryPtr->leftIndex = 0;
- entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
entryPtr->textGC = None;
entryPtr->selTextGC = None;
entryPtr->highlightGC = None;
entryPtr->avgWidth = 1;
- entryPtr->flags = 0;
- entryPtr->validateCmd = NULL;
entryPtr->validate = VALIDATE_NONE;
- entryPtr->invalidCmd = NULL;
+
+ /*
+ * Keep a hold of the associated tkwin until we destroy the listbox,
+ * otherwise Tk might free it while we still need it.
+ */
+
+ Tcl_Preserve((ClientData) entryPtr->tkwin);
Tk_SetClass(entryPtr->tkwin, "Entry");
- TkSetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
+ Tk_SetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
Tk_CreateEventHandler(entryPtr->tkwin,
ExposureMask|StructureNotifyMask|FocusChangeMask,
EntryEventProc, (ClientData) entryPtr);
@@ -579,7 +851,7 @@ Tk_EntryObjCmd(clientData, interp, objc, objv)
Tk_DestroyWindow(entryPtr->tkwin);
return TCL_ERROR;
}
-
+
Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC);
return TCL_OK;
}
@@ -617,20 +889,20 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
- Tcl_Preserve((ClientData) entryPtr);
/*
* Parse the widget command by looking up the second token in
* the list of valid command names.
*/
- result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
+ result = Tcl_GetIndexFromObj(interp, objv[1], entryCmdNames,
"option", 0, &cmdIndex);
if (result != TCL_OK) {
return result;
}
- switch (cmdIndex) {
+ Tcl_Preserve((ClientData) entryPtr);
+ switch ((enum entryCmd) cmdIndex) {
case COMMAND_BBOX: {
int index, x, y, width, height;
char buf[TCL_INTEGER_SPACE * 4];
@@ -717,7 +989,7 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
goto error;
}
- Tcl_SetResult(interp, entryPtr->string, TCL_STATIC);
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1);
break;
}
@@ -799,19 +1071,27 @@ EntryWidgetObjCmd(clientData, interp, objc, objv)
int index, index2;
if (objc < 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
+ Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
goto error;
}
-
+
/*
* Parse the selection sub-command, using the command
- * table "selCommandNames" defined above.
+ * table "selCmdNames" defined above.
*/
- result = Tcl_GetIndexFromObj(interp, objv[2], selCommandNames,
- "selection option", 0, &selIndex);
+ result = Tcl_GetIndexFromObj(interp, objv[2], selCmdNames,
+ "selection option", 0, &selIndex);
if (result != TCL_OK) {
- goto error;
+ goto error;
+ }
+
+ /*
+ * Disabled entries don't allow the selection to be modified.
+ */
+
+ if (entryPtr->state == STATE_DISABLED) {
+ goto done;
}
switch(selIndex) {
@@ -1043,12 +1323,6 @@ DestroyEntry(memPtr)
char *memPtr; /* Info about entry widget. */
{
Entry *entryPtr = (Entry *) memPtr;
- entryPtr->flags |= ENTRY_DELETED;
-
- Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd);
- if (entryPtr->flags & REDRAW_PENDING) {
- Tcl_CancelIdleCall(DisplayEntry, (ClientData) entryPtr);
- }
/*
* Free up all the stuff that requires special handling, then
@@ -1056,7 +1330,7 @@ DestroyEntry(memPtr)
* stuff.
*/
- ckfree(entryPtr->string);
+ ckfree((char *)entryPtr->string);
if (entryPtr->textVarName != NULL) {
Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
@@ -1070,12 +1344,25 @@ DestroyEntry(memPtr)
}
Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler);
if (entryPtr->displayString != entryPtr->string) {
- ckfree(entryPtr->displayString);
+ ckfree((char *)entryPtr->displayString);
+ }
+ if (entryPtr->type == TK_SPINBOX) {
+ Spinbox *sbPtr = (Spinbox *) entryPtr;
+
+ if (sbPtr->listObj != NULL) {
+ Tcl_DecrRefCount(sbPtr->listObj);
+ sbPtr->listObj = NULL;
+ }
+ if (sbPtr->formatBuf) {
+ ckfree(sbPtr->formatBuf);
+ }
}
Tk_FreeTextLayout(entryPtr->textLayout);
Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable,
entryPtr->tkwin);
+ Tcl_Release((ClientData) entryPtr->tkwin);
entryPtr->tkwin = NULL;
+
ckfree((char *) entryPtr);
}
@@ -1110,9 +1397,17 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
int flags; /* Flags to pass to Tk_ConfigureWidget. */
{
Tk_SavedOptions savedOptions;
+ Tk_3DBorder border;
Tcl_Obj *errorResult = NULL;
+ Spinbox *sbPtr = (Spinbox *) entryPtr; /* Only used when this widget
+ * is of type TK_SPINBOX */
+ char *oldValues = NULL; /* lint initialization */
+ char *oldFormat = NULL; /* lint initialization */
int error;
- int oldExport;
+ int oldExport = 0; /* lint initialization */
+ int valuesChanged = 0; /* lint initialization */
+ double oldFrom = 0.0; /* lint initialization */
+ double oldTo = 0.0; /* lint initialization */
/*
* Eliminate any existing trace on a variable monitored by the entry.
@@ -1124,7 +1419,17 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
EntryTextVarProc, (ClientData) entryPtr);
}
- oldExport = entryPtr->exportSelection;
+ /*
+ * Store old values that we need to effect certain behavior if
+ * they change value
+ */
+ oldExport = entryPtr->exportSelection;
+ if (entryPtr->type == TK_SPINBOX) {
+ oldValues = sbPtr->valueStr;
+ oldFormat = sbPtr->reqFormat;
+ oldFrom = sbPtr->fromValue;
+ oldTo = sbPtr->toValue;
+ }
for (error = 0; error <= 1; error++) {
if (!error) {
@@ -1152,7 +1457,16 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
* the geometry and setting the background from a 3-D border.
*/
- Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
+ if ((entryPtr->state == STATE_DISABLED) &&
+ (entryPtr->disabledBorder != NULL)) {
+ border = entryPtr->disabledBorder;
+ } else if ((entryPtr->state == STATE_READONLY) &&
+ (entryPtr->readonlyBorder != NULL)) {
+ border = entryPtr->readonlyBorder;
+ } else {
+ border = entryPtr->normalBorder;
+ }
+ Tk_SetBackgroundFromBorder(entryPtr->tkwin, border);
if (entryPtr->insertWidth <= 0) {
entryPtr->insertWidth = 2;
@@ -1161,6 +1475,80 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
entryPtr->insertBorderWidth = entryPtr->insertWidth/2;
}
+ if (entryPtr->type == TK_SPINBOX) {
+ if (sbPtr->fromValue > sbPtr->toValue) {
+ Tcl_SetResult(interp,
+ "-to value must be greater than -from value",
+ TCL_VOLATILE);
+ continue;
+ }
+
+ if (sbPtr->reqFormat && (oldFormat != sbPtr->reqFormat)) {
+ /*
+ * Make sure that the given format is somewhat correct, and
+ * calculate the minimum space we'll need for the values as
+ * strings.
+ */
+ int min, max;
+ size_t formatLen, formatSpace = TCL_DOUBLE_SPACE;
+ char fbuf[4], *fmt = sbPtr->reqFormat;
+
+ formatLen = strlen(fmt);
+ if ((fmt[0] != '%') || (fmt[formatLen-1] != 'f')) {
+ badFormatOpt:
+ Tcl_AppendResult(interp, "bad spinbox format specifier \"",
+ sbPtr->reqFormat, "\"", (char *) NULL);
+ continue;
+ }
+ if ((sscanf(fmt, "%%%d.%d%[f]", &min, &max, fbuf) == 3)
+ && (max >= 0)) {
+ formatSpace = min + max + 1;
+ } else if (((sscanf(fmt, "%%.%d%[f]", &min, fbuf) == 2)
+ || (sscanf(fmt, "%%%d%[f]", &min, fbuf) == 2)
+ || (sscanf(fmt, "%%%d.%[f]", &min, fbuf) == 2))
+ && (min >= 0)) {
+ formatSpace = min + 1;
+ } else {
+ goto badFormatOpt;
+ }
+ if (formatSpace < TCL_DOUBLE_SPACE) {
+ formatSpace = TCL_DOUBLE_SPACE;
+ }
+ sbPtr->formatBuf = ckrealloc(sbPtr->formatBuf, formatSpace);
+ /*
+ * We perturb the value of oldFrom to allow us to go into
+ * the branch below that will reformat the displayed value.
+ */
+ oldFrom = sbPtr->fromValue - 1;
+ }
+
+ /*
+ * See if we have to rearrange our listObj data
+ */
+ if (oldValues != sbPtr->valueStr) {
+ if (sbPtr->listObj != NULL) {
+ Tcl_DecrRefCount(sbPtr->listObj);
+ }
+ sbPtr->listObj = NULL;
+ if (sbPtr->valueStr != NULL) {
+ Tcl_Obj *newObjPtr;
+ int nelems;
+
+ newObjPtr = Tcl_NewStringObj(sbPtr->valueStr, -1);
+ if (Tcl_ListObjLength(interp, newObjPtr, &nelems)
+ != TCL_OK) {
+ valuesChanged = -1;
+ continue;
+ }
+ sbPtr->listObj = newObjPtr;
+ Tcl_IncrRefCount(sbPtr->listObj);
+ sbPtr->nElements = nelems;
+ sbPtr->eIndex = 0;
+ valuesChanged++;
+ }
+ }
+ }
+
/*
* Restart the cursor timing sequence in case the on-time or
* off-time just changed. Set validate temporarily to none,
@@ -1205,20 +1593,67 @@ ConfigureEntry(interp, entryPtr, objc, objv, flags)
}
/*
- * 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 the entry is tied to the value of a variable, 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;
+ CONST char *value;
value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY);
if (value == NULL) {
- EntryValueChanged(entryPtr);
+ EntryValueChanged(entryPtr, NULL);
} else {
EntrySetValue(entryPtr, value);
}
+ }
+
+ if (entryPtr->type == TK_SPINBOX) {
+ ComputeFormat(sbPtr);
+
+ if (valuesChanged > 0) {
+ Tcl_Obj *objPtr;
+
+ /*
+ * No check for error return, because there shouldn't be one
+ * given the check for valid list above
+ */
+ Tcl_ListObjIndex(interp, sbPtr->listObj, 0, &objPtr);
+ EntryValueChanged(entryPtr, Tcl_GetString(objPtr));
+ } else if ((sbPtr->valueStr == NULL)
+ && !DOUBLES_EQ(sbPtr->fromValue, sbPtr->toValue)
+ && (!DOUBLES_EQ(sbPtr->fromValue, oldFrom)
+ || !DOUBLES_EQ(sbPtr->toValue, oldTo))) {
+ /*
+ * If the valueStr is empty and -from && -to are specified, check
+ * to see if the current string is within the range. If not,
+ * it will be constrained to the nearest edge. If the current
+ * string isn't a double value, we set it to -from.
+ */
+ int code;
+ double dvalue;
+
+ code = Tcl_GetDouble(NULL, entryPtr->string, &dvalue);
+ if (code != TCL_OK) {
+ dvalue = sbPtr->fromValue;
+ } else {
+ if (dvalue > sbPtr->toValue) {
+ dvalue = sbPtr->toValue;
+ } else if (dvalue < sbPtr->fromValue) {
+ dvalue = sbPtr->fromValue;
+ }
+ }
+ sprintf(sbPtr->formatBuf, sbPtr->valueFormat, dvalue);
+ EntryValueChanged(entryPtr, sbPtr->formatBuf);
+ }
+ }
+
+ /*
+ * Set up a trace on the variable's value after we've possibly
+ * constrained the value according to new -from/-to values.
+ */
+
+ if (entryPtr->textVarName != NULL) {
Tcl_TraceVar(interp, entryPtr->textVarName,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
EntryTextVarProc, (ClientData) entryPtr);
@@ -1259,20 +1694,52 @@ EntryWorldChanged(instanceData)
XGCValues gcValues;
GC gc = None;
unsigned long mask;
- Entry *entryPtr;
-
- entryPtr = (Entry *) instanceData;
+ Tk_3DBorder border;
+ XColor *colorPtr;
+ Entry *entryPtr = (Entry *) instanceData;
entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1);
if (entryPtr->avgWidth == 0) {
entryPtr->avgWidth = 1;
}
- if (entryPtr->normalBorder != NULL) {
- Tk_SetBackgroundFromBorder(entryPtr->tkwin, entryPtr->normalBorder);
+ if (entryPtr->type == TK_SPINBOX) {
+ /*
+ * Compute the button width for a spinbox
+ */
+
+ entryPtr->xWidth = entryPtr->avgWidth + 2 * (1+XPAD);
+ if (entryPtr->xWidth < 11) {
+ entryPtr->xWidth = 11; /* we want a min visible size */
+ }
+ }
+
+ /*
+ * Default background and foreground are from the normal state.
+ * In a disabled state, both of those may be overridden; in the readonly
+ * state, the background may be overridden.
+ */
+
+ border = entryPtr->normalBorder;
+ colorPtr = entryPtr->fgColorPtr;
+ switch (entryPtr->state) {
+ case STATE_DISABLED:
+ if (entryPtr->disabledBorder != NULL) {
+ border = entryPtr->disabledBorder;
+ }
+ if (entryPtr->dfgColorPtr != NULL) {
+ colorPtr = entryPtr->dfgColorPtr;
+ }
+ break;
+ case STATE_READONLY:
+ if (entryPtr->readonlyBorder != NULL) {
+ border = entryPtr->readonlyBorder;
+ }
+ break;
}
- gcValues.foreground = entryPtr->fgColorPtr->pixel;
+ Tk_SetBackgroundFromBorder(entryPtr->tkwin, border);
+ gcValues.foreground = colorPtr->pixel;
gcValues.font = Tk_FontId(entryPtr->tkfont);
gcValues.graphics_exposures = False;
mask = GCForeground | GCFont | GCGraphicsExposures;
@@ -1324,13 +1791,13 @@ DisplayEntry(clientData)
Entry *entryPtr = (Entry *) clientData;
Tk_Window tkwin = entryPtr->tkwin;
int baseY, selStartX, selEndX, cursorX;
- int xBound;
+ int showSelection, xBound;
Tk_FontMetrics fm;
Pixmap pixmap;
- int showSelection;
+ Tk_3DBorder border;
entryPtr->flags &= ~REDRAW_PENDING;
- if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) {
return;
}
@@ -1342,7 +1809,20 @@ DisplayEntry(clientData)
if (entryPtr->flags & UPDATE_SCROLLBAR) {
entryPtr->flags &= ~UPDATE_SCROLLBAR;
+
+ /*
+ * Preserve/Release because updating the scrollbar can have
+ * the side-effect of destroying or unmapping the entry widget.
+ */
+
+ Tcl_Preserve((ClientData) entryPtr);
EntryUpdateScrollbar(entryPtr);
+
+ if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) {
+ Tcl_Release((ClientData) entryPtr);
+ return;
+ }
+ Tcl_Release((ClientData) entryPtr);
}
/*
@@ -1360,7 +1840,7 @@ DisplayEntry(clientData)
* one, plus vertical position of baseline of text.
*/
- xBound = Tk_Width(tkwin) - entryPtr->inset;
+ xBound = Tk_Width(tkwin) - entryPtr->inset - entryPtr->xWidth;
baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2;
/*
@@ -1380,10 +1860,19 @@ DisplayEntry(clientData)
* insertion cursor background.
*/
- Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
- 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
+ if ((entryPtr->state == STATE_DISABLED) &&
+ (entryPtr->disabledBorder != NULL)) {
+ border = entryPtr->disabledBorder;
+ } else if ((entryPtr->state == STATE_READONLY) &&
+ (entryPtr->readonlyBorder != NULL)) {
+ border = entryPtr->readonlyBorder;
+ } else {
+ border = entryPtr->normalBorder;
+ }
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
- if (showSelection
+ if (showSelection && (entryPtr->state != STATE_DISABLED)
&& (entryPtr->selectLast > entryPtr->leftIndex)) {
if (entryPtr->selectFirst <= entryPtr->leftIndex) {
selStartX = entryPtr->leftX;
@@ -1414,23 +1903,26 @@ DisplayEntry(clientData)
* cursor isn't on. Otherwise the selection would hide the cursor.
*/
- if ((entryPtr->insertPos >= entryPtr->leftIndex)
- && (entryPtr->state == STATE_NORMAL)
- && (entryPtr->flags & GOT_FOCUS)) {
+ if ((entryPtr->state == STATE_NORMAL) && (entryPtr->flags & GOT_FOCUS)) {
Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos, &cursorX, NULL,
NULL, NULL);
cursorX += entryPtr->layoutX;
cursorX -= (entryPtr->insertWidth)/2;
- if (cursorX < xBound) {
- if (entryPtr->flags & CURSOR_ON) {
- Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder,
- cursorX, baseY - fm.ascent, entryPtr->insertWidth,
- fm.ascent + fm.descent, entryPtr->insertBorderWidth,
- TK_RELIEF_RAISED);
- } else if (entryPtr->insertBorder == entryPtr->selBorder) {
- Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->normalBorder,
- cursorX, baseY - fm.ascent, entryPtr->insertWidth,
- fm.ascent + fm.descent, 0, TK_RELIEF_FLAT);
+ Tk_SetCaretPos(entryPtr->tkwin, cursorX, baseY - fm.ascent,
+ fm.ascent + fm.descent);
+ if (entryPtr->insertPos >= entryPtr->leftIndex) {
+ 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, border,
+ cursorX, baseY - fm.ascent, entryPtr->insertWidth,
+ fm.ascent + fm.descent, 0, TK_RELIEF_FLAT);
+ }
}
}
}
@@ -1444,7 +1936,7 @@ DisplayEntry(clientData)
entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY,
entryPtr->leftIndex, entryPtr->numChars);
- if (showSelection
+ if (showSelection && (entryPtr->state != STATE_DISABLED)
&& (entryPtr->selTextGC != entryPtr->textGC)
&& (entryPtr->selectFirst < entryPtr->selectLast)) {
int selFirst;
@@ -1459,29 +1951,106 @@ DisplayEntry(clientData)
selFirst, entryPtr->selectLast);
}
+ if (entryPtr->type == TK_SPINBOX) {
+ int startx, height, inset, pad, tHeight, xWidth;
+ Spinbox *sbPtr = (Spinbox *) entryPtr;
+
+ /*
+ * Draw the spin button controls.
+ */
+ xWidth = entryPtr->xWidth;
+ pad = XPAD + 1;
+ inset = entryPtr->inset - XPAD;
+ startx = Tk_Width(tkwin) - (xWidth + inset);
+ height = (Tk_Height(tkwin) - 2*inset)/2;
+#if 0
+ Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder,
+ startx, inset, xWidth, height, 1, sbPtr->buRelief);
+ Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder,
+ startx, inset+height, xWidth, height, 1, sbPtr->bdRelief);
+#else
+ Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder,
+ startx, inset, xWidth, height, 1,
+ (sbPtr->selElement == SEL_BUTTONUP) ?
+ TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+ Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder,
+ startx, inset+height, xWidth, height, 1,
+ (sbPtr->selElement == SEL_BUTTONDOWN) ?
+ TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+#endif
+
+ xWidth -= 2*pad;
+ /*
+ * Only draw the triangles if we have enough display space
+ */
+ if ((xWidth > 1)) {
+ XPoint points[3];
+ int starty, space, offset;
+
+ space = height - 2*pad;
+ /*
+ * Ensure width of triangle is odd to guarantee a sharp tip
+ */
+ if (!(xWidth % 2)) {
+ xWidth++;
+ }
+ tHeight = (xWidth + 1) / 2;
+ if (tHeight > space) {
+ tHeight = space;
+ }
+ space = (space - tHeight) / 2;
+ startx += pad;
+ starty = inset + height - pad - space;
+ offset = (sbPtr->selElement == SEL_BUTTONUP);
+ /*
+ * The points are slightly different for the up and down arrows
+ * because (for *.x), we need to account for a bug in the way
+ * XFillPolygon draws triangles, and we want to shift
+ * the arrows differently when allowing for depressed behavior.
+ */
+ points[0].x = startx + offset;
+ points[0].y = starty + (offset ? 0 : -1);
+ points[1].x = startx + xWidth/2 + offset;
+ points[1].y = starty - tHeight + (offset ? 0 : -1);
+ points[2].x = startx + xWidth + offset;
+ points[2].y = points[0].y;
+ XFillPolygon(entryPtr->display, pixmap, entryPtr->textGC,
+ points, 3, Convex, CoordModeOrigin);
+
+ starty = inset + height + pad + space;
+ offset = (sbPtr->selElement == SEL_BUTTONDOWN);
+ points[0].x = startx + 1 + offset;
+ points[0].y = starty + (offset ? 1 : 0);
+ points[1].x = startx + xWidth/2 + offset;
+ points[1].y = starty + tHeight + (offset ? 0 : -1);
+ points[2].x = startx - 1 + xWidth + offset;
+ points[2].y = points[0].y;
+ XFillPolygon(entryPtr->display, pixmap, entryPtr->textGC,
+ points, 3, Convex, CoordModeOrigin);
+ }
+ }
+
/*
* Draw the border and focus highlight last, so they will overwrite
* any text that extends past the viewable part of the window.
*/
+ xBound = entryPtr->highlightWidth;
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,
+ Tk_Draw3DRectangle(tkwin, pixmap, border, xBound, xBound,
+ Tk_Width(tkwin) - 2 * xBound,
+ Tk_Height(tkwin) - 2 * xBound,
entryPtr->borderWidth, entryPtr->relief);
}
- if (entryPtr->highlightWidth != 0) {
+ if (xBound > 0) {
GC fgGC, bgGC;
bgGC = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap);
if (entryPtr->flags & GOT_FOCUS) {
fgGC = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap);
- TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
- entryPtr->highlightWidth, pixmap);
+ TkpDrawHighlightBorder(tkwin, fgGC, bgGC, xBound, pixmap);
} else {
- TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
- entryPtr->highlightWidth, pixmap);
+ TkpDrawHighlightBorder(tkwin, bgGC, bgGC, xBound, pixmap);
}
}
@@ -1527,7 +2096,7 @@ EntryComputeGeometry(entryPtr)
char *p;
if (entryPtr->displayString != entryPtr->string) {
- ckfree(entryPtr->displayString);
+ ckfree((char *)entryPtr->displayString);
entryPtr->displayString = entryPtr->string;
entryPtr->numDisplayBytes = entryPtr->numBytes;
}
@@ -1553,15 +2122,15 @@ EntryComputeGeometry(entryPtr)
size = Tcl_UniCharToUtf(ch, buf);
entryPtr->numDisplayBytes = entryPtr->numChars * size;
- entryPtr->displayString =
- (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1));
+ p = (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1));
+ entryPtr->displayString = p;
- p = entryPtr->displayString;
for (i = entryPtr->numChars; --i >= 0; ) {
p += Tcl_UniCharToUtf(ch, p);
}
*p = '\0';
}
+
Tk_FreeTextLayout(entryPtr->textLayout);
entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont,
entryPtr->displayString, entryPtr->numChars, 0,
@@ -1576,16 +2145,18 @@ EntryComputeGeometry(entryPtr)
* window unless the entire window is full.
*/
- overflow = totalLength - (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset);
+ overflow = totalLength -
+ (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset - entryPtr->xWidth);
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;
+ - entryPtr->xWidth - totalLength;
} else {
- entryPtr->leftX = (Tk_Width(entryPtr->tkwin) - totalLength)/2;
+ entryPtr->leftX = (Tk_Width(entryPtr->tkwin)
+ - entryPtr->xWidth - totalLength)/2;
}
entryPtr->layoutX = entryPtr->leftX;
} else {
@@ -1622,6 +2193,12 @@ EntryComputeGeometry(entryPtr)
width = totalLength + 2*entryPtr->inset;
}
}
+
+ /*
+ * Add one extra length for the spin buttons
+ */
+ width += entryPtr->xWidth;
+
Tk_GeometryRequest(entryPtr->tkwin, width, height);
}
@@ -1651,7 +2228,8 @@ InsertChars(entryPtr, index, value)
* string). */
{
int byteIndex, byteCount, oldChars, charsAdded, newByteCount;
- char *new, *string;
+ CONST char *string;
+ char *new;
string = entryPtr->string;
byteIndex = Tcl_UtfAtIndex(string, index) - string;
@@ -1674,7 +2252,7 @@ InsertChars(entryPtr, index, value)
return;
}
- ckfree(string);
+ ckfree((char *)string);
entryPtr->string = new;
/*
@@ -1721,7 +2299,7 @@ InsertChars(entryPtr, index, value)
if (entryPtr->insertPos >= index) {
entryPtr->insertPos += charsAdded;
}
- EntryValueChanged(entryPtr);
+ EntryValueChanged(entryPtr, NULL);
}
/*
@@ -1748,7 +2326,8 @@ DeleteChars(entryPtr, index, count)
int count; /* How many characters to delete. */
{
int byteIndex, byteCount, newByteCount;
- char *new, *string, *todelete;
+ CONST char *string;
+ char *new, *todelete;
if ((index + count) > entryPtr->numChars) {
count = entryPtr->numChars - index;
@@ -1780,7 +2359,7 @@ DeleteChars(entryPtr, index, count)
}
ckfree(todelete);
- ckfree(entryPtr->string);
+ ckfree((char *)entryPtr->string);
entryPtr->string = new;
entryPtr->numChars -= count;
entryPtr->numBytes -= byteCount;
@@ -1835,7 +2414,7 @@ DeleteChars(entryPtr, index, count)
entryPtr->insertPos = index;
}
}
- EntryValueChanged(entryPtr);
+ EntryValueChanged(entryPtr, NULL);
}
/*
@@ -1858,10 +2437,14 @@ DeleteChars(entryPtr, index, count)
*/
static void
-EntryValueChanged(entryPtr)
+EntryValueChanged(entryPtr, newValue)
Entry *entryPtr; /* Entry whose value just changed. */
+ CONST char *newValue; /* If this value is not NULL, we first
+ * force the value of the entry to this */
{
- char *newValue;
+ if (newValue != NULL) {
+ EntrySetValue(entryPtr, newValue);
+ }
if (entryPtr->textVarName == NULL) {
newValue = NULL;
@@ -1916,9 +2499,9 @@ EntryValueChanged(entryPtr)
static void
EntrySetValue(entryPtr, value)
Entry *entryPtr; /* Entry whose value is to be changed. */
- char *value; /* New text to display in entry. */
+ CONST char *value; /* New text to display in entry. */
{
- char *oldSource;
+ CONST char *oldSource;
int code, valueLen, malloced = 0;
if (strcmp(value, entryPtr->string) == 0) {
@@ -1934,9 +2517,9 @@ EntrySetValue(entryPtr, value)
* point to volatile memory, like the value of the -textvar
* which may get freed during validation
*/
- oldSource = (char *) ckalloc((unsigned) (valueLen + 1));
- strcpy(oldSource, value);
- value = oldSource;
+ char *tmp = (char *) ckalloc((unsigned) (valueLen + 1));
+ strcpy(tmp, value);
+ value = tmp;
malloced = 1;
entryPtr->flags |= VALIDATE_VAR;
@@ -1949,19 +2532,20 @@ EntrySetValue(entryPtr, value)
*/
if (entryPtr->flags & VALIDATE_ABORT) {
entryPtr->flags &= ~VALIDATE_ABORT;
- ckfree(value);
+ ckfree((char *)value);
return;
}
}
oldSource = entryPtr->string;
- ckfree(entryPtr->string);
+ ckfree((char *)entryPtr->string);
if (malloced) {
entryPtr->string = value;
} else {
- entryPtr->string = (char *) ckalloc((unsigned) (valueLen + 1));
- strcpy(entryPtr->string, value);
+ char *tmp = (char *) ckalloc((unsigned) (valueLen + 1));
+ strcpy(tmp, value);
+ entryPtr->string = tmp;
}
entryPtr->numBytes = valueLen;
entryPtr->numChars = Tcl_NumUtfChars(value, valueLen);
@@ -2001,7 +2585,7 @@ EntrySetValue(entryPtr, value)
* EntryEventProc --
*
* This procedure is invoked by the Tk dispatcher for various
- * events on entryes.
+ * events on entries.
*
* Results:
* None.
@@ -2019,25 +2603,62 @@ EntryEventProc(clientData, eventPtr)
XEvent *eventPtr; /* Information about event. */
{
Entry *entryPtr = (Entry *) clientData;
- if (eventPtr->type == Expose) {
- EventuallyRedraw(entryPtr);
- entryPtr->flags |= BORDER_NEEDED;
- } else if (eventPtr->type == DestroyNotify) {
- DestroyEntry((char *) clientData);
- } else if (eventPtr->type == ConfigureNotify) {
- Tcl_Preserve((ClientData) entryPtr);
- entryPtr->flags |= UPDATE_SCROLLBAR;
- EntryComputeGeometry(entryPtr);
- EventuallyRedraw(entryPtr);
- Tcl_Release((ClientData) entryPtr);
- } else if (eventPtr->type == FocusIn) {
- if (eventPtr->xfocus.detail != NotifyInferior) {
- EntryFocusProc(entryPtr, 1);
- }
- } else if (eventPtr->type == FocusOut) {
- if (eventPtr->xfocus.detail != NotifyInferior) {
- EntryFocusProc(entryPtr, 0);
+
+ if ((entryPtr->type == TK_SPINBOX) && (eventPtr->type == MotionNotify)) {
+ Spinbox *sbPtr = (Spinbox *) clientData;
+ int elem;
+
+ elem = GetSpinboxElement(sbPtr, eventPtr->xmotion.x,
+ eventPtr->xmotion.y);
+ if (elem != sbPtr->curElement) {
+ Tk_Cursor cursor;
+
+ sbPtr->curElement = elem;
+ if (elem == SEL_ENTRY) {
+ cursor = entryPtr->cursor;
+ } else if ((elem == SEL_BUTTONDOWN) || (elem == SEL_BUTTONUP)) {
+ cursor = sbPtr->bCursor;
+ } else {
+ cursor = None;
+ }
+ if (cursor != None) {
+ Tk_DefineCursor(entryPtr->tkwin, cursor);
+ } else {
+ Tk_UndefineCursor(entryPtr->tkwin);
+ }
}
+ return;
+ }
+
+ switch (eventPtr->type) {
+ case Expose:
+ EventuallyRedraw(entryPtr);
+ entryPtr->flags |= BORDER_NEEDED;
+ break;
+ case DestroyNotify:
+ if (!(entryPtr->flags & ENTRY_DELETED)) {
+ entryPtr->flags |= (ENTRY_DELETED | VALIDATE_ABORT);
+ Tcl_DeleteCommandFromToken(entryPtr->interp,
+ entryPtr->widgetCmd);
+ if (entryPtr->flags & REDRAW_PENDING) {
+ Tcl_CancelIdleCall(DisplayEntry, clientData);
+ }
+ Tcl_EventuallyFree(clientData, DestroyEntry);
+ }
+ break;
+ case ConfigureNotify:
+ Tcl_Preserve((ClientData) entryPtr);
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+ Tcl_Release((ClientData) entryPtr);
+ break;
+ case FocusIn:
+ case FocusOut:
+ if (eventPtr->xfocus.detail != NotifyInferior) {
+ EntryFocusProc(entryPtr, (eventPtr->type == FocusIn));
+ }
+ break;
}
}
@@ -2123,8 +2744,9 @@ GetEntryIndex(interp, entryPtr, string, indexPtr)
*/
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
- Tcl_AppendResult(interp, "bad entry index \"", string,
- "\"", (char *) NULL);
+ Tcl_AppendResult(interp, "bad ",
+ (entryPtr->type == TK_ENTRY) ? "entry" : "spinbox",
+ " index \"", string, "\"", (char *) NULL);
return TCL_ERROR;
}
} else if (string[0] == 'e') {
@@ -2141,7 +2763,9 @@ GetEntryIndex(interp, entryPtr, string, indexPtr)
}
} else if (string[0] == 's') {
if (entryPtr->selectFirst < 0) {
- Tcl_SetResult(interp, "selection isn't in entry", TCL_STATIC);
+ Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
+ Tcl_AppendResult(interp, "selection isn't in widget ",
+ Tk_PathName(entryPtr->tkwin), (char *) NULL);
return TCL_ERROR;
}
if (length < 5) {
@@ -2155,7 +2779,7 @@ GetEntryIndex(interp, entryPtr, string, indexPtr)
goto badIndex;
}
} else if (string[0] == '@') {
- int x, roundUp;
+ int x, roundUp, maxWidth;
if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) {
goto badIndex;
@@ -2164,8 +2788,10 @@ GetEntryIndex(interp, entryPtr, string, indexPtr)
x = entryPtr->inset;
}
roundUp = 0;
- if (x >= (Tk_Width(entryPtr->tkwin) - entryPtr->inset)) {
- x = Tk_Width(entryPtr->tkwin) - entryPtr->inset - 1;
+ maxWidth = Tk_Width(entryPtr->tkwin) - entryPtr->inset
+ - entryPtr->xWidth - 1;
+ if (x > maxWidth) {
+ x = maxWidth;
roundUp = 1;
}
*indexPtr = Tk_PointToChar(entryPtr->textLayout,
@@ -2191,8 +2817,6 @@ GetEntryIndex(interp, entryPtr, string, indexPtr)
*indexPtr = entryPtr->numChars;
}
}
- if(*indexPtr > entryPtr->numChars)
- *indexPtr = entryPtr->numChars;
return TCL_OK;
}
@@ -2348,7 +2972,8 @@ EntryFetchSelection(clientData, offset, buffer, maxBytes)
{
Entry *entryPtr = (Entry *) clientData;
int byteCount;
- char *string, *selStart, *selEnd;
+ CONST char *string;
+ CONST char *selStart, *selEnd;
if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) {
return -1;
@@ -2433,7 +3058,7 @@ static void
EventuallyRedraw(entryPtr)
Entry *entryPtr; /* Information about widget. */
{
- if ((entryPtr->tkwin == NULL) || !Tk_IsMapped(entryPtr->tkwin)) {
+ if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(entryPtr->tkwin)) {
return;
}
@@ -2484,7 +3109,7 @@ EntryVisibleRange(entryPtr, firstPtr, lastPtr)
} else {
charsInWindow = Tk_PointToChar(entryPtr->textLayout,
Tk_Width(entryPtr->tkwin) - entryPtr->inset
- - entryPtr->layoutX - 1, 0);
+ - entryPtr->xWidth - entryPtr->layoutX - 1, 0);
if (charsInWindow < entryPtr->numChars) {
charsInWindow++;
}
@@ -2539,7 +3164,9 @@ EntryUpdateScrollbar(entryPtr)
code = Tcl_VarEval(interp, entryPtr->scrollCmd, args, (char *) NULL);
if (code != TCL_OK) {
Tcl_AddErrorInfo(interp,
- "\n (horizontal scrolling command executed by entry)");
+ "\n (horizontal scrolling command executed by ");
+ Tcl_AddErrorInfo(interp, Tk_PathName(entryPtr->tkwin));
+ Tcl_AddErrorInfo(interp, ")");
Tcl_BackgroundError(interp);
}
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
@@ -2571,17 +3198,18 @@ EntryBlinkProc(clientData)
Entry *entryPtr = (Entry *) clientData;
if ((entryPtr->state == STATE_DISABLED) ||
+ (entryPtr->state == STATE_READONLY) ||
!(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);
+ entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr);
} else {
entryPtr->flags |= CURSOR_ON;
entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
- entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
+ entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr);
}
EventuallyRedraw(entryPtr);
}
@@ -2660,12 +3288,12 @@ 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. */
+ CONST char *name1; /* Not used. */
+ CONST char *name2; /* Not used. */
int flags; /* Information about what happened. */
{
Entry *entryPtr = (Entry *) clientData;
- char *value;
+ CONST char *value;
/*
* If the variable is unset, then immediately recreate it unless
@@ -2729,13 +3357,21 @@ EntryValidate(entryPtr, cmd)
code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
+ /*
+ * We accept TCL_OK and TCL_RETURN as valid return codes from the
+ * command callback.
+ */
if (code != TCL_OK && code != TCL_RETURN) {
- Tcl_AddErrorInfo(interp,
- "\n\t(in validation command executed by entry)");
+ Tcl_AddErrorInfo(interp, "\n\t(in validation command executed by ");
+ Tcl_AddErrorInfo(interp, Tk_PathName(entryPtr->tkwin));
+ Tcl_AddErrorInfo(interp, ")");
Tcl_BackgroundError(interp);
return TCL_ERROR;
}
+ /*
+ * The command callback should return an acceptable Tcl boolean.
+ */
if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp),
&bool) != TCL_OK) {
Tcl_AddErrorInfo(interp,
@@ -2774,7 +3410,7 @@ EntryValidateChange(entryPtr, change, new, index, type)
register Entry *entryPtr; /* Entry that needs validation. */
char *change; /* Characters to be added/deleted
* (NULL-terminated string). */
- char *new; /* Potential new value of entry string */
+ CONST char *new; /* Potential new value of entry string */
int index; /* index of insert/delete, -1 otherwise */
int type; /* forced, delete, insert,
* focusin or focusout */
@@ -2819,15 +3455,27 @@ EntryValidateChange(entryPtr, change, new, index, type)
* it means that a loop condition almost occured. Do not allow
* this validation result to finish.
*/
+
if (entryPtr->validate == VALIDATE_NONE
|| (!varValidate && (entryPtr->flags & VALIDATE_VAR))) {
code = TCL_ERROR;
}
+
+ /*
+ * It's possible that the user deleted the entry during validation.
+ * In that case, abort future validation and return an error.
+ */
+
+ if (entryPtr->flags & ENTRY_DELETED) {
+ return TCL_ERROR;
+ }
+
/*
* If validate will return ERROR, then disallow further validations
* Otherwise, if it didn't accept the new string (returned TCL_BREAK)
* then eval the invalidCmd (if it's set)
*/
+
if (code == TCL_ERROR) {
entryPtr->validate = VALIDATE_NONE;
} else if (code == TCL_BREAK) {
@@ -2839,6 +3487,7 @@ EntryValidateChange(entryPtr, change, new, index, type)
* may want to do entry manipulation which the setting of the
* var will later wipe anyway.
*/
+
if (varValidate) {
entryPtr->validate = VALIDATE_NONE;
} else if (entryPtr->invalidCmd != NULL) {
@@ -2856,6 +3505,15 @@ EntryValidateChange(entryPtr, change, new, index, type)
entryPtr->validate = VALIDATE_NONE;
}
Tcl_DStringFree(&script);
+
+ /*
+ * It's possible that the user deleted the entry during validation.
+ * In that case, abort future validation and return an error.
+ */
+
+ if (entryPtr->flags & ENTRY_DELETED) {
+ return TCL_ERROR;
+ }
}
}
@@ -2886,11 +3544,12 @@ EntryValidateChange(entryPtr, change, new, index, type)
static void
ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
register Entry *entryPtr; /* Entry that needs validation. */
- register char *before; /* Command containing percent
+ register CONST char *before;
+ /* Command containing percent
* expressions to be replaced. */
char *change; /* Characters to added/deleted
* (NULL-terminated string). */
- char *new; /* Potential new value of entry string */
+ CONST char *new; /* Potential new value of entry string */
int index; /* index of insert/delete */
int type; /* INSERT or DELETE */
Tcl_DString *dsPtr; /* Dynamic string in which to append
@@ -2899,7 +3558,7 @@ ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl
* list element. */
int number, length;
- register char *string;
+ register CONST char *string;
Tcl_UniChar ch;
char numStorage[2*TCL_INTEGER_SPACE];
@@ -2933,69 +3592,1029 @@ ExpandPercents(entryPtr, before, change, new, index, type, dsPtr)
} else {
ch = '%';
}
- switch (ch) {
- case 'd': /* Type of call that caused validation */
- switch (type) {
- case VALIDATE_INSERT:
- number = 1;
- break;
- case VALIDATE_DELETE:
- number = 0;
- break;
- default:
- number = -1;
- break;
- }
- sprintf(numStorage, "%d", number);
- string = numStorage;
+ if (type == VALIDATE_BUTTON) {
+ /*
+ * -command %-substitution
+ */
+ switch (ch) {
+ case 's': /* Current string value of spinbox */
+ string = entryPtr->string;
+ break;
+ case 'd': /* direction, up or down */
+ string = change;
+ break;
+ case 'W': /* widget name */
+ string = Tk_PathName(entryPtr->tkwin);
+ break;
+ default:
+ length = Tcl_UniCharToUtf(ch, numStorage);
+ numStorage[length] = '\0';
+ string = numStorage;
+ break;
+ }
+ } else {
+ /*
+ * -validatecommand / -invalidcommand %-substitution
+ */
+ switch (ch) {
+ case 'd': /* Type of call that caused validation */
+ switch (type) {
+ case VALIDATE_INSERT:
+ number = 1;
+ break;
+ case VALIDATE_DELETE:
+ number = 0;
+ break;
+ default:
+ number = -1;
+ break;
+ }
+ sprintf(numStorage, "%d", number);
+ string = numStorage;
+ break;
+ case 'i': /* index of insert/delete */
+ sprintf(numStorage, "%d", index);
+ string = numStorage;
+ break;
+ case 'P': /* 'Peeked' new value of the string */
+ string = new;
+ break;
+ case 's': /* Current string value of spinbox */
+ string = entryPtr->string;
+ break;
+ case 'S': /* string to be inserted/deleted, if any */
+ string = change;
+ break;
+ case 'v': /* type of validation currently set */
+ string = validateStrings[entryPtr->validate];
+ break;
+ case 'V': /* type of validation in effect */
+ switch (type) {
+ case VALIDATE_INSERT:
+ case VALIDATE_DELETE:
+ string = validateStrings[VALIDATE_KEY];
+ break;
+ case VALIDATE_FORCED:
+ string = "forced";
+ break;
+ default:
+ string = validateStrings[type];
+ break;
+ }
+ break;
+ case 'W': /* widget name */
+ string = Tk_PathName(entryPtr->tkwin);
+ break;
+ default:
+ length = Tcl_UniCharToUtf(ch, numStorage);
+ numStorage[length] = '\0';
+ string = numStorage;
+ break;
+ }
+ }
+
+ spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);
+ length = Tcl_DStringLength(dsPtr);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ spaceNeeded = Tcl_ConvertCountedElement(string, -1,
+ Tcl_DStringValue(dsPtr) + length,
+ cvtFlags | TCL_DONT_USE_BRACES);
+ Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_SpinboxObjCmd --
+ *
+ * This procedure is invoked to process the "spinbox" 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_SpinboxObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* NULL. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ register Entry *entryPtr;
+ register Spinbox *sbPtr;
+ Tk_OptionTable optionTable;
+ Tk_Window tkwin;
+ char *tmp;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
+ return TCL_ERROR;
+ }
+
+ tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
+ Tcl_GetString(objv[1]), (char *) NULL);
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Create the option table for this widget class. If it has already
+ * been created, Tk will return the cached value.
+ */
+
+ optionTable = Tk_CreateOptionTable(interp, sbOptSpec);
+
+ /*
+ * 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). Only the non-NULL/0
+ * data must be initialized as memset covers the rest.
+ */
+
+ sbPtr = (Spinbox *) ckalloc(sizeof(Spinbox));
+ entryPtr = (Entry *) sbPtr;
+ memset((VOID *) sbPtr, 0, sizeof(Spinbox));
+
+ entryPtr->tkwin = tkwin;
+ entryPtr->display = Tk_Display(tkwin);
+ entryPtr->interp = interp;
+ entryPtr->widgetCmd = Tcl_CreateObjCommand(interp,
+ Tk_PathName(entryPtr->tkwin), SpinboxWidgetObjCmd,
+ (ClientData) sbPtr, EntryCmdDeletedProc);
+ entryPtr->optionTable = optionTable;
+ entryPtr->type = TK_SPINBOX;
+ tmp = (char *) ckalloc(1);
+ tmp[0] = '\0';
+ entryPtr->string = tmp;
+ entryPtr->selectFirst = -1;
+ entryPtr->selectLast = -1;
+
+ entryPtr->cursor = None;
+ entryPtr->exportSelection = 1;
+ entryPtr->justify = TK_JUSTIFY_LEFT;
+ entryPtr->relief = TK_RELIEF_FLAT;
+ entryPtr->state = STATE_NORMAL;
+ entryPtr->displayString = entryPtr->string;
+ entryPtr->inset = XPAD;
+ entryPtr->textGC = None;
+ entryPtr->selTextGC = None;
+ entryPtr->highlightGC = None;
+ entryPtr->avgWidth = 1;
+ entryPtr->validate = VALIDATE_NONE;
+
+ sbPtr->selElement = SEL_NONE;
+ sbPtr->curElement = SEL_NONE;
+ sbPtr->bCursor = None;
+ sbPtr->repeatDelay = 400;
+ sbPtr->repeatInterval = 100;
+ sbPtr->fromValue = 0.0;
+ sbPtr->toValue = 100.0;
+ sbPtr->increment = 1.0;
+ sbPtr->formatBuf = (char *) ckalloc(TCL_DOUBLE_SPACE);
+ sbPtr->bdRelief = TK_RELIEF_FLAT;
+ sbPtr->buRelief = TK_RELIEF_FLAT;
+
+ /*
+ * Keep a hold of the associated tkwin until we destroy the listbox,
+ * otherwise Tk might free it while we still need it.
+ */
+
+ Tcl_Preserve((ClientData) entryPtr->tkwin);
+
+ Tk_SetClass(entryPtr->tkwin, "Spinbox");
+ Tk_SetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr);
+ Tk_CreateEventHandler(entryPtr->tkwin,
+ PointerMotionMask|ExposureMask|StructureNotifyMask|FocusChangeMask,
+ EntryEventProc, (ClientData) entryPtr);
+ Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING,
+ EntryFetchSelection, (ClientData) entryPtr, XA_STRING);
+
+ if (Tk_InitOptions(interp, (char *) sbPtr, optionTable, tkwin)
+ != TCL_OK) {
+ Tk_DestroyWindow(entryPtr->tkwin);
+ return TCL_ERROR;
+ }
+ if (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK) {
+ goto error;
+ }
+
+ Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC);
+ return TCL_OK;
+
+ error:
+ Tk_DestroyWindow(entryPtr->tkwin);
+ return TCL_ERROR;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SpinboxWidgetObjCmd --
+ *
+ * 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
+SpinboxWidgetObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Information about spinbox widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Entry *entryPtr = (Entry *) clientData;
+ Spinbox *sbPtr = (Spinbox *) clientData;
+ int cmdIndex, selIndex, result;
+ Tcl_Obj *objPtr;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Parse the widget command by looking up the second token in
+ * the list of valid command names.
+ */
+
+ result = Tcl_GetIndexFromObj(interp, objv[1], sbCmdNames,
+ "option", 0, &cmdIndex);
+ if (result != TCL_OK) {
+ return result;
+ }
+
+ Tcl_Preserve((ClientData) entryPtr);
+ switch ((enum sbCmd) cmdIndex) {
+ case SB_CMD_BBOX: {
+ int index, x, y, width, height;
+ char buf[TCL_INTEGER_SPACE * 4];
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &index) != TCL_OK) {
+ goto error;
+ }
+ if ((index == entryPtr->numChars) && (index > 0)) {
+ index--;
+ }
+ Tk_CharBbox(entryPtr->textLayout, index, &x, &y,
+ &width, &height);
+ sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX,
+ y + entryPtr->layoutY, width, height);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ break;
+ }
+
+ case SB_CMD_CGET: {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option");
+ goto error;
+ }
+
+ objPtr = Tk_GetOptionValue(interp, (char *) entryPtr,
+ entryPtr->optionTable, objv[2], entryPtr->tkwin);
+ if (objPtr == NULL) {
+ goto error;
+ } else {
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ break;
+ }
+
+ case SB_CMD_CONFIGURE: {
+ if (objc <= 3) {
+ objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr,
+ entryPtr->optionTable,
+ (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
+ entryPtr->tkwin);
+ if (objPtr == NULL) {
+ goto error;
+ } else {
+ Tcl_SetObjResult(interp, objPtr);
+ }
+ } else {
+ result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0);
+ }
break;
- case 'i': /* index of insert/delete */
- sprintf(numStorage, "%d", index);
- string = numStorage;
+ }
+
+ case SB_CMD_DELETE: {
+ int first, last;
+
+ if ((objc < 3) || (objc > 4)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &first) != TCL_OK) {
+ goto error;
+ }
+ if (objc == 3) {
+ last = first + 1;
+ } else {
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]),
+ &last) != TCL_OK) {
+ goto error;
+ }
+ }
+ if ((last >= first) && (entryPtr->state == STATE_NORMAL)) {
+ DeleteChars(entryPtr, first, last - first);
+ }
break;
- case 'P': /* 'Peeked' new value of the string */
- string = new;
+ }
+
+ case SB_CMD_GET: {
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
+ goto error;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1);
break;
- case 's': /* Current string value of entry */
- string = entryPtr->string;
+ }
+
+ case SB_CMD_ICURSOR: {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "pos");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &entryPtr->insertPos) != TCL_OK) {
+ goto error;
+ }
+ EventuallyRedraw(entryPtr);
break;
- case 'S': /* string to be inserted/deleted, if any */
- string = change;
+ }
+
+ case SB_CMD_IDENTIFY: {
+ int x, y, elem;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "x y");
+ goto error;
+ }
+ if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) ||
+ (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
+ goto error;
+ }
+ elem = GetSpinboxElement(sbPtr, x, y);
+ if (elem != SEL_NONE) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ selElementNames[elem], -1);
+ }
break;
- case 'v': /* type of validation currently set */
- string = validateStrings[entryPtr->validate];
+ }
+
+ case SB_CMD_INDEX: {
+ int index;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "string");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &index) != TCL_OK) {
+ goto error;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
break;
- case 'V': /* type of validation in effect */
- switch (type) {
- case VALIDATE_INSERT:
- case VALIDATE_DELETE:
- string = validateStrings[VALIDATE_KEY];
- break;
- case VALIDATE_FORCED:
- string = "forced";
- break;
- default:
- string = validateStrings[type];
- break;
+ }
+
+ case SB_CMD_INSERT: {
+ int index;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "index text");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &index) != TCL_OK) {
+ goto error;
+ }
+ if (entryPtr->state == STATE_NORMAL) {
+ InsertChars(entryPtr, index, Tcl_GetString(objv[3]));
}
break;
- case 'W': /* widget name */
- string = Tk_PathName(entryPtr->tkwin);
+ }
+
+ case SB_CMD_INVOKE: {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "elemName");
+ goto error;
+ }
+ result = Tcl_GetIndexFromObj(interp, objv[2],
+ selElementNames, "element", 0, &cmdIndex);
+ if (result != TCL_OK) {
+ goto error;
+ }
+ if (entryPtr->state != STATE_DISABLED) {
+ if (SpinboxInvoke(interp, sbPtr, cmdIndex) != TCL_OK) {
+ goto error;
+ }
+ }
break;
- default:
- length = Tcl_UniCharToUtf(ch, numStorage);
- numStorage[length] = '\0';
- string = numStorage;
+ }
+
+ case SB_CMD_SCAN: {
+ int x;
+ char *minorCmd;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x");
+ goto error;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) {
+ goto error;
+ }
+
+ minorCmd = Tcl_GetString(objv[2]);
+ if (minorCmd[0] == 'm'
+ && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) {
+ entryPtr->scanMarkX = x;
+ entryPtr->scanMarkIndex = entryPtr->leftIndex;
+ } else if ((minorCmd[0] == 'd')
+ && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) {
+ EntryScanTo(entryPtr, x);
+ } else {
+ Tcl_AppendResult(interp, "bad scan option \"",
+ Tcl_GetString(objv[2]), "\": must be mark or dragto",
+ (char *) NULL);
+ goto error;
+ }
break;
}
- spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags);
- length = Tcl_DStringLength(dsPtr);
- Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
- spaceNeeded = Tcl_ConvertCountedElement(string, -1,
- Tcl_DStringValue(dsPtr) + length,
- cvtFlags | TCL_DONT_USE_BRACES);
- Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
+ case SB_CMD_SELECTION: {
+ int index, index2;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "option ?index?");
+ goto error;
+ }
+
+ /*
+ * Parse the selection sub-command, using the command
+ * table "sbSelCmdNames" defined above.
+ */
+
+ result = Tcl_GetIndexFromObj(interp, objv[2], sbSelCmdNames,
+ "selection option", 0, &selIndex);
+ if (result != TCL_OK) {
+ goto error;
+ }
+
+ /*
+ * Disabled entries don't allow the selection to be modified.
+ */
+
+ if (entryPtr->state == STATE_DISABLED) {
+ goto done;
+ }
+
+ switch(selIndex) {
+ case SB_SEL_ADJUST: {
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr,
+ Tcl_GetString(objv[3]), &index) != TCL_OK) {
+ goto error;
+ }
+ if (entryPtr->selectFirst >= 0) {
+ int half1, half2;
+
+ half1 = (entryPtr->selectFirst
+ + entryPtr->selectLast)/2;
+ half2 = (entryPtr->selectFirst
+ + entryPtr->selectLast + 1)/2;
+ if (index < half1) {
+ entryPtr->selectAnchor = entryPtr->selectLast;
+ } else if (index > half2) {
+ entryPtr->selectAnchor = entryPtr->selectFirst;
+ } else {
+ /*
+ * We're at about the halfway point in the
+ * selection; just keep the existing anchor.
+ */
+ }
+ }
+ EntrySelectTo(entryPtr, index);
+ break;
+ }
+
+ case SB_SEL_CLEAR: {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
+ goto error;
+ }
+ if (entryPtr->selectFirst >= 0) {
+ entryPtr->selectFirst = -1;
+ entryPtr->selectLast = -1;
+ EventuallyRedraw(entryPtr);
+ }
+ goto done;
+ }
+
+ case SB_SEL_FROM: {
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr,
+ Tcl_GetString(objv[3]), &index) != TCL_OK) {
+ goto error;
+ }
+ entryPtr->selectAnchor = index;
+ break;
+ }
+
+ case SB_SEL_PRESENT: {
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
+ goto error;
+ }
+ Tcl_SetObjResult(interp,
+ Tcl_NewBooleanObj((entryPtr->selectFirst >= 0)));
+ goto done;
+ }
+
+ case SB_SEL_RANGE: {
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 3, objv, "start end");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr,
+ Tcl_GetString(objv[3]), &index) != TCL_OK) {
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr,
+ Tcl_GetString(objv[4]),& index2) != TCL_OK) {
+ goto error;
+ }
+ if (index >= index2) {
+ entryPtr->selectFirst = -1;
+ entryPtr->selectLast = -1;
+ } else {
+ entryPtr->selectFirst = index;
+ entryPtr->selectLast = index2;
+ }
+ if (!(entryPtr->flags & GOT_SELECTION)
+ && (entryPtr->exportSelection)) {
+ Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY,
+ EntryLostSelection, (ClientData) entryPtr);
+ entryPtr->flags |= GOT_SELECTION;
+ }
+ EventuallyRedraw(entryPtr);
+ break;
+ }
+
+ case SB_SEL_TO: {
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 3, objv, "index");
+ goto error;
+ }
+ if (GetEntryIndex(interp, entryPtr,
+ Tcl_GetString(objv[3]), &index) != TCL_OK) {
+ goto error;
+ }
+ EntrySelectTo(entryPtr, index);
+ break;
+ }
+
+ case SB_SEL_ELEMENT: {
+ if ((objc < 3) || (objc > 4)) {
+ Tcl_WrongNumArgs(interp, 3, objv, "?elemName?");
+ goto error;
+ }
+ if (objc == 3) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ selElementNames[sbPtr->selElement], -1);
+ } else {
+ int lastElement = sbPtr->selElement;
+
+ result = Tcl_GetIndexFromObj(interp, objv[3],
+ selElementNames, "selection element", 0,
+ &(sbPtr->selElement));
+ if (result != TCL_OK) {
+ goto error;
+ }
+ if (lastElement != sbPtr->selElement) {
+ EventuallyRedraw(entryPtr);
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case SB_CMD_SET: {
+ if (objc > 3) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?string?");
+ goto error;
+ }
+ if (objc == 3) {
+ EntryValueChanged(entryPtr, Tcl_GetString(objv[2]));
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1);
+ break;
+ }
+
+ case SB_CMD_VALIDATE: {
+ int code;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, (char *) NULL);
+ goto error;
+ }
+ selIndex = entryPtr->validate;
+ entryPtr->validate = VALIDATE_ALL;
+ code = EntryValidateChange(entryPtr, (char *) NULL,
+ entryPtr->string, -1, VALIDATE_FORCED);
+ if (entryPtr->validate != VALIDATE_NONE) {
+ entryPtr->validate = selIndex;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK)));
+ break;
+ }
+
+ case SB_CMD_XVIEW: {
+ int index;
+
+ if (objc == 2) {
+ double first, last;
+ char buf[TCL_DOUBLE_SPACE * 2];
+
+ EntryVisibleRange(entryPtr, &first, &last);
+ sprintf(buf, "%g %g", first, last);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ goto done;
+ } else if (objc == 3) {
+ if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]),
+ &index) != TCL_OK) {
+ goto error;
+ }
+ } else {
+ double fraction;
+ int count;
+
+ index = entryPtr->leftIndex;
+ switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction,
+ &count)) {
+ case TK_SCROLL_ERROR: {
+ goto error;
+ }
+ case TK_SCROLL_MOVETO: {
+ index = (int) ((fraction * entryPtr->numChars) + 0.5);
+ break;
+ }
+ case TK_SCROLL_PAGES: {
+ int charsPerPage;
+
+ charsPerPage = ((Tk_Width(entryPtr->tkwin)
+ - 2 * entryPtr->inset - entryPtr->xWidth)
+ / entryPtr->avgWidth) - 2;
+ if (charsPerPage < 1) {
+ charsPerPage = 1;
+ }
+ index += count * charsPerPage;
+ break;
+ }
+ case TK_SCROLL_UNITS: {
+ index += count;
+ break;
+ }
+ }
+ }
+ if (index >= entryPtr->numChars) {
+ index = entryPtr->numChars - 1;
+ }
+ if (index < 0) {
+ index = 0;
+ }
+ entryPtr->leftIndex = index;
+ entryPtr->flags |= UPDATE_SCROLLBAR;
+ EntryComputeGeometry(entryPtr);
+ EventuallyRedraw(entryPtr);
+ break;
+ }
+ }
+
+ done:
+ Tcl_Release((ClientData) entryPtr);
+ return result;
+
+ error:
+ Tcl_Release((ClientData) entryPtr);
+ return TCL_ERROR;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetSpinboxElement --
+ *
+ * Return the element associated with an x,y coord.
+ *
+ * Results:
+ * Element type as enum selelement.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+GetSpinboxElement(sbPtr, x, y)
+ Spinbox *sbPtr; /* Spinbox for which the index is being
+ * specified. */
+ int x; /* x coord */
+ int y; /* y coord */
+{
+ Entry *entryPtr = (Entry *) sbPtr;
+
+ if ((x < 0) || (y < 0) || (y > Tk_Height(entryPtr->tkwin))
+ || (x > Tk_Width(entryPtr->tkwin))) {
+ return SEL_NONE;
+ }
+
+ if (x > (Tk_Width(entryPtr->tkwin) - entryPtr->inset - entryPtr->xWidth)) {
+ if (y > (Tk_Height(entryPtr->tkwin) / 2)) {
+ return SEL_BUTTONDOWN;
+ } else {
+ return SEL_BUTTONUP;
+ }
+ }
+ return SEL_ENTRY;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SpinboxInvoke --
+ *
+ * This procedure is invoked when the invoke method for the
+ * widget is called.
+ *
+ * Results:
+ * TCL_OK.
+ *
+ * Side effects:
+ * An background error condition may arise when invoking the
+ * callback. The widget value may change.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+SpinboxInvoke(interp, sbPtr, element)
+ register Tcl_Interp *interp; /* Current interpreter. */
+ register Spinbox *sbPtr; /* Spinbox to invoke. */
+ int element; /* element to invoke, either the "up"
+ * or "down" button. */
+{
+ Entry *entryPtr = (Entry *) sbPtr;
+ char *type;
+ int code, up;
+ Tcl_DString script;
+
+ switch (element) {
+ case SEL_BUTTONUP:
+ type = "up";
+ up = 1;
+ break;
+ case SEL_BUTTONDOWN:
+ type = "down";
+ up = 0;
+ break;
+ default:
+ return TCL_OK;
+ }
+
+ if (fabs(sbPtr->increment) > MIN_DBL_VAL) {
+ if (sbPtr->listObj != NULL) {
+ Tcl_Obj *objPtr;
+
+ Tcl_ListObjIndex(interp, sbPtr->listObj, sbPtr->eIndex, &objPtr);
+ if (strcmp(Tcl_GetString(objPtr), entryPtr->string)) {
+ /*
+ * Somehow the string changed from what we expected,
+ * so let's do a search on the list to see if the current
+ * value is there. If not, move to the first element of
+ * the list.
+ */
+ int i, listc, elemLen, length = entryPtr->numChars;
+ char *bytes;
+ Tcl_Obj **listv;
+
+ Tcl_ListObjGetElements(interp, sbPtr->listObj, &listc, &listv);
+ for (i = 0; i < listc; i++) {
+ bytes = Tcl_GetStringFromObj(listv[i], &elemLen);
+ if ((length == elemLen) &&
+ (memcmp(bytes, entryPtr->string,
+ (size_t) length) == 0)) {
+ sbPtr->eIndex = i;
+ break;
+ }
+ }
+ }
+ if (up) {
+ if (++sbPtr->eIndex >= sbPtr->nElements) {
+ if (sbPtr->wrap) {
+ sbPtr->eIndex = 0;
+ } else {
+ sbPtr->eIndex = sbPtr->nElements-1;
+ }
+ }
+ } else {
+ if (--sbPtr->eIndex < 0) {
+ if (sbPtr->wrap) {
+ sbPtr->eIndex = sbPtr->nElements-1;
+ } else {
+ sbPtr->eIndex = 0;
+ }
+ }
+ }
+ Tcl_ListObjIndex(interp, sbPtr->listObj, sbPtr->eIndex, &objPtr);
+ EntryValueChanged(entryPtr, Tcl_GetString(objPtr));
+ } else if (!DOUBLES_EQ(sbPtr->fromValue, sbPtr->toValue)) {
+ double dvalue;
+
+ if (Tcl_GetDouble(NULL, entryPtr->string, &dvalue) != TCL_OK) {
+ /*
+ * If the string is empty, or isn't a valid double value,
+ * just use the -from value
+ */
+ dvalue = sbPtr->fromValue;
+ } else {
+ if (up) {
+ dvalue += sbPtr->increment;
+ if (dvalue > sbPtr->toValue) {
+ if (sbPtr->wrap) {
+ dvalue = sbPtr->fromValue;
+ } else {
+ dvalue = sbPtr->toValue;
+ }
+ } else if (dvalue < sbPtr->fromValue) {
+ /*
+ * It's possible that when pressing up, we are
+ * still less than the fromValue, because the
+ * user may have manipulated the value by hand.
+ */
+ dvalue = sbPtr->fromValue;
+ }
+ } else {
+ dvalue -= sbPtr->increment;
+ if (dvalue < sbPtr->fromValue) {
+ if (sbPtr->wrap) {
+ dvalue = sbPtr->toValue;
+ } else {
+ dvalue = sbPtr->fromValue;
+ }
+ } else if (dvalue > sbPtr->toValue) {
+ /*
+ * It's possible that when pressing down, we are
+ * still greater than the toValue, because the
+ * user may have manipulated the value by hand.
+ */
+ dvalue = sbPtr->toValue;
+ }
+ }
+ }
+ sprintf(sbPtr->formatBuf, sbPtr->valueFormat, dvalue);
+ EntryValueChanged(entryPtr, sbPtr->formatBuf);
+ }
}
+
+ if (sbPtr->command != NULL) {
+ Tcl_DStringInit(&script);
+ ExpandPercents(entryPtr, sbPtr->command, type, "", 0,
+ VALIDATE_BUTTON, &script);
+ Tcl_DStringAppend(&script, "", 1);
+
+ code = Tcl_EvalEx(interp, Tcl_DStringValue(&script), -1,
+ TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT);
+ Tcl_DStringFree(&script);
+
+ if (code != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n\t(in command executed by spinbox)");
+ Tcl_BackgroundError(interp);
+ /*
+ * Yes, it's an error, but a bg one, so we return OK
+ */
+ return TCL_OK;
+ }
+
+ Tcl_SetResult(interp, NULL, 0);
+ }
+
+ return TCL_OK;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeFormat --
+ *
+ * This procedure is invoked to recompute the "format" fields
+ * of a spinbox's widget record, which determines how the value
+ * of the dial is converted to a string.
+ *
+ * Results:
+ * Tcl result code.
+ *
+ * Side effects:
+ * The format fields of the spinbox are modified.
+ *
+ *----------------------------------------------------------------------
+ */
+static int
+ComputeFormat(sbPtr)
+ Spinbox *sbPtr; /* Information about dial widget. */
+{
+ double maxValue, x;
+ int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
+ int eDigits, fDigits;
+
+ /*
+ * Compute the displacement from the decimal of the most significant
+ * digit required for any number in the dial's range.
+ */
+
+ if (sbPtr->reqFormat) {
+ sbPtr->valueFormat = sbPtr->reqFormat;
+ return TCL_OK;
+ }
+
+ maxValue = fabs(sbPtr->fromValue);
+ x = fabs(sbPtr->toValue);
+ if (x > maxValue) {
+ maxValue = x;
+ }
+ if (maxValue == 0) {
+ maxValue = 1;
+ }
+ mostSigDigit = (int) floor(log10(maxValue));
+
+ if (fabs(sbPtr->increment) > MIN_DBL_VAL) {
+ /*
+ * A increment was specified, so use it.
+ */
+ leastSigDigit = (int) floor(log10(sbPtr->increment));
+ } else {
+ leastSigDigit = 0;
+ }
+ numDigits = mostSigDigit - leastSigDigit + 1;
+ if (numDigits < 1) {
+ numDigits = 1;
+ }
+
+ /*
+ * Compute the number of characters required using "e" format and
+ * "f" format, and then choose whichever one takes fewer characters.
+ */
+ eDigits = numDigits + 4;
+ if (numDigits > 1) {
+ eDigits++; /* Decimal point. */
+ }
+ afterDecimal = numDigits - mostSigDigit - 1;
+ if (afterDecimal < 0) {
+ afterDecimal = 0;
+ }
+ fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
+ if (afterDecimal > 0) {
+ fDigits++; /* Decimal point. */
+ }
+ if (mostSigDigit < 0) {
+ fDigits++; /* Zero to left of decimal point. */
+ }
+ if (fDigits <= eDigits) {
+ sprintf(sbPtr->digitFormat, "%%.%df", afterDecimal);
+ } else {
+ sprintf(sbPtr->digitFormat, "%%.%de", numDigits-1);
+ }
+ sbPtr->valueFormat = sbPtr->digitFormat;
+ return TCL_OK;
+}