1/* 2 * tkEntry.c -- 3 * 4 * This module implements entry and spinbox widgets for the Tk toolkit. 5 * An entry displays a string and allows the string to be edited. A 6 * spinbox expands on the entry by adding up/down buttons that control 7 * the value of the entry widget. 8 * 9 * Copyright (c) 1990-1994 The Regents of the University of California. 10 * Copyright (c) 1994-1997 Sun Microsystems, Inc. 11 * Copyright (c) 2000 Ajuba Solutions. 12 * Copyright (c) 2002 ActiveState Corporation. 13 * 14 * See the file "license.terms" for information on usage and redistribution of 15 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 16 * 17 * RCS: @(#) $Id$ 18 */ 19 20#include "tkInt.h" 21#include "default.h" 22#include "tkEntry.h" 23 24/* 25 * The following macro defines how many extra pixels to leave on each side of 26 * the text in the entry. 27 */ 28 29#define XPAD 1 30#define YPAD 1 31 32/* 33 * A comparison function for double values. For Spinboxes. 34 */ 35 36#define MIN_DBL_VAL 1E-9 37#define DOUBLES_EQ(d1, d2) (fabs((d1) - (d2)) < MIN_DBL_VAL) 38 39 40static char *stateStrings[] = { 41 "disabled", "normal", "readonly", NULL 42}; 43 44/* 45 * Definitions for -validate option values: 46 */ 47 48static char *validateStrings[] = { 49 "all", "key", "focus", "focusin", "focusout", "none", NULL 50}; 51enum validateType { 52 VALIDATE_ALL, VALIDATE_KEY, VALIDATE_FOCUS, 53 VALIDATE_FOCUSIN, VALIDATE_FOCUSOUT, VALIDATE_NONE, 54 /* 55 * These extra enums are for use with EntryValidateChange 56 */ 57 VALIDATE_FORCED, VALIDATE_DELETE, VALIDATE_INSERT, VALIDATE_BUTTON 58}; 59#define DEF_ENTRY_VALIDATE "none" 60#define DEF_ENTRY_INVALIDCMD "" 61 62/* 63 * Information used for Entry objv parsing. 64 */ 65 66static const Tk_OptionSpec entryOptSpec[] = { 67 {TK_OPTION_BORDER, "-background", "background", "Background", 68 DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder), 69 0, (ClientData) DEF_ENTRY_BG_MONO, 0}, 70 {TK_OPTION_SYNONYM, "-bd", NULL, NULL, 71 NULL, 0, -1, 0, (ClientData) "-borderwidth", 0}, 72 {TK_OPTION_SYNONYM, "-bg", NULL, NULL, 73 NULL, 0, -1, 0, (ClientData) "-background", 0}, 74 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 75 DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth), 0, 0, 0}, 76 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", 77 DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor), 78 TK_OPTION_NULL_OK, 0, 0}, 79 {TK_OPTION_BORDER, "-disabledbackground", "disabledBackground", 80 "DisabledBackground", DEF_ENTRY_DISABLED_BG_COLOR, -1, 81 Tk_Offset(Entry, disabledBorder), TK_OPTION_NULL_OK, 82 (ClientData) DEF_ENTRY_DISABLED_BG_MONO, 0}, 83 {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", 84 "DisabledForeground", DEF_ENTRY_DISABLED_FG, -1, 85 Tk_Offset(Entry, dfgColorPtr), TK_OPTION_NULL_OK, 0, 0}, 86 {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection", 87 "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1, 88 Tk_Offset(Entry, exportSelection), 0, 0, 0}, 89 {TK_OPTION_SYNONYM, "-fg", "foreground", NULL, 90 NULL, 0, -1, 0, (ClientData) "-foreground", 0}, 91 {TK_OPTION_FONT, "-font", "font", "Font", 92 DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0}, 93 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", 94 DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0, 0, 0}, 95 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", 96 "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG, 97 -1, Tk_Offset(Entry, highlightBgColorPtr), 0, 0, 0}, 98 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", 99 DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr), 0, 0, 0}, 100 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", 101 "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1, 102 Tk_Offset(Entry, highlightWidth), 0, 0, 0}, 103 {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground", 104 DEF_ENTRY_INSERT_BG, -1, Tk_Offset(Entry, insertBorder), 0, 0, 0}, 105 {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth", 106 "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1, 107 Tk_Offset(Entry, insertBorderWidth), 0, 108 (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0}, 109 {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime", 110 DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime), 111 0, 0, 0}, 112 {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime", 113 DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime), 0, 0, 0}, 114 {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", 115 DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth), 0, 0, 0}, 116 {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand", 117 DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd), 118 TK_OPTION_NULL_OK, 0, 0}, 119 {TK_OPTION_SYNONYM, "-invcmd", NULL, NULL, 120 NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0}, 121 {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", 122 DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0}, 123 {TK_OPTION_BORDER, "-readonlybackground", "readonlyBackground", 124 "ReadonlyBackground", DEF_ENTRY_READONLY_BG_COLOR, -1, 125 Tk_Offset(Entry, readonlyBorder), TK_OPTION_NULL_OK, 126 (ClientData) DEF_ENTRY_READONLY_BG_MONO, 0}, 127 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 128 DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief), 0, 0, 0}, 129 {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground", 130 DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder), 131 0, (ClientData) DEF_ENTRY_SELECT_MONO, 0}, 132 {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth", 133 "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1, 134 Tk_Offset(Entry, selBorderWidth), 135 0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0}, 136 {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background", 137 DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr), 138 TK_CONFIG_NULL_OK, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0}, 139 {TK_OPTION_STRING, "-show", "show", "Show", 140 DEF_ENTRY_SHOW, -1, Tk_Offset(Entry, showChar), 141 TK_OPTION_NULL_OK, 0, 0}, 142 {TK_OPTION_STRING_TABLE, "-state", "state", "State", 143 DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state), 144 0, (ClientData) stateStrings, 0}, 145 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", 146 DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus), 147 TK_OPTION_NULL_OK, 0, 0}, 148 {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", 149 DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName), 150 TK_OPTION_NULL_OK, 0, 0}, 151 {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate", 152 DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate), 153 0, (ClientData) validateStrings, 0}, 154 {TK_OPTION_STRING, "-validatecommand", "validateCommand","ValidateCommand", 155 NULL, -1, Tk_Offset(Entry, validateCmd), TK_OPTION_NULL_OK, 0, 0}, 156 {TK_OPTION_SYNONYM, "-vcmd", NULL, NULL, 157 NULL, 0, -1, 0, (ClientData) "-validatecommand", 0}, 158 {TK_OPTION_INT, "-width", "width", "Width", 159 DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0}, 160 {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", 161 DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd), 162 TK_OPTION_NULL_OK, 0, 0}, 163 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} 164}; 165 166/* 167 * Information used for Spinbox objv parsing. 168 */ 169 170#define DEF_SPINBOX_REPEAT_DELAY "400" 171#define DEF_SPINBOX_REPEAT_INTERVAL "100" 172 173#define DEF_SPINBOX_CMD "" 174 175#define DEF_SPINBOX_FROM "0" 176#define DEF_SPINBOX_TO "0" 177#define DEF_SPINBOX_INCREMENT "1" 178#define DEF_SPINBOX_FORMAT "" 179 180#define DEF_SPINBOX_VALUES "" 181#define DEF_SPINBOX_WRAP "0" 182 183static const Tk_OptionSpec sbOptSpec[] = { 184 {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Background", 185 DEF_BUTTON_ACTIVE_BG_COLOR, -1, Tk_Offset(Spinbox, activeBorder), 186 0, (ClientData) DEF_BUTTON_ACTIVE_BG_MONO, 0}, 187 {TK_OPTION_BORDER, "-background", "background", "Background", 188 DEF_ENTRY_BG_COLOR, -1, Tk_Offset(Entry, normalBorder), 189 0, (ClientData) DEF_ENTRY_BG_MONO, 0}, 190 {TK_OPTION_SYNONYM, "-bd", NULL, NULL, 191 NULL, 0, -1, 0, (ClientData) "-borderwidth", 0}, 192 {TK_OPTION_SYNONYM, "-bg", NULL, NULL, 193 NULL, 0, -1, 0, (ClientData) "-background", 0}, 194 {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 195 DEF_ENTRY_BORDER_WIDTH, -1, Tk_Offset(Entry, borderWidth), 0, 0, 0}, 196 {TK_OPTION_BORDER, "-buttonbackground", "Button.background", "Background", 197 DEF_BUTTON_BG_COLOR, -1, Tk_Offset(Spinbox, buttonBorder), 198 0, (ClientData) DEF_BUTTON_BG_MONO, 0}, 199 {TK_OPTION_CURSOR, "-buttoncursor", "Button.cursor", "Cursor", 200 DEF_BUTTON_CURSOR, -1, Tk_Offset(Spinbox, bCursor), 201 TK_OPTION_NULL_OK, 0, 0}, 202 {TK_OPTION_RELIEF, "-buttondownrelief", "Button.relief", "Relief", 203 DEF_BUTTON_RELIEF, -1, Tk_Offset(Spinbox, bdRelief), 0, 0, 0}, 204 {TK_OPTION_RELIEF, "-buttonuprelief", "Button.relief", "Relief", 205 DEF_BUTTON_RELIEF, -1, Tk_Offset(Spinbox, buRelief), 0, 0, 0}, 206 {TK_OPTION_STRING, "-command", "command", "Command", 207 DEF_SPINBOX_CMD, -1, Tk_Offset(Spinbox, command), 208 TK_OPTION_NULL_OK, 0, 0}, 209 {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor", 210 DEF_ENTRY_CURSOR, -1, Tk_Offset(Entry, cursor), 211 TK_OPTION_NULL_OK, 0, 0}, 212 {TK_OPTION_BORDER, "-disabledbackground", "disabledBackground", 213 "DisabledBackground", DEF_ENTRY_DISABLED_BG_COLOR, -1, 214 Tk_Offset(Entry, disabledBorder), TK_OPTION_NULL_OK, 215 (ClientData) DEF_ENTRY_DISABLED_BG_MONO, 0}, 216 {TK_OPTION_COLOR, "-disabledforeground", "disabledForeground", 217 "DisabledForeground", DEF_ENTRY_DISABLED_FG, -1, 218 Tk_Offset(Entry, dfgColorPtr), TK_OPTION_NULL_OK, 0, 0}, 219 {TK_OPTION_BOOLEAN, "-exportselection", "exportSelection", 220 "ExportSelection", DEF_ENTRY_EXPORT_SELECTION, -1, 221 Tk_Offset(Entry, exportSelection), 0, 0, 0}, 222 {TK_OPTION_SYNONYM, "-fg", "foreground", NULL, 223 NULL, 0, -1, 0, (ClientData) "-foreground", 0}, 224 {TK_OPTION_FONT, "-font", "font", "Font", 225 DEF_ENTRY_FONT, -1, Tk_Offset(Entry, tkfont), 0, 0, 0}, 226 {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground", 227 DEF_ENTRY_FG, -1, Tk_Offset(Entry, fgColorPtr), 0, 0, 0}, 228 {TK_OPTION_STRING, "-format", "format", "Format", 229 DEF_SPINBOX_FORMAT, -1, Tk_Offset(Spinbox, reqFormat), 230 TK_OPTION_NULL_OK, 0, 0}, 231 {TK_OPTION_DOUBLE, "-from", "from", "From", 232 DEF_SPINBOX_FROM, -1, Tk_Offset(Spinbox, fromValue), 0, 0, 0}, 233 {TK_OPTION_COLOR, "-highlightbackground", "highlightBackground", 234 "HighlightBackground", DEF_ENTRY_HIGHLIGHT_BG, 235 -1, Tk_Offset(Entry, highlightBgColorPtr), 0, 0, 0}, 236 {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", 237 DEF_ENTRY_HIGHLIGHT, -1, Tk_Offset(Entry, highlightColorPtr), 0, 0, 0}, 238 {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness", 239 "HighlightThickness", DEF_ENTRY_HIGHLIGHT_WIDTH, -1, 240 Tk_Offset(Entry, highlightWidth), 0, 0, 0}, 241 {TK_OPTION_DOUBLE, "-increment", "increment", "Increment", 242 DEF_SPINBOX_INCREMENT, -1, Tk_Offset(Spinbox, increment), 0, 0, 0}, 243 {TK_OPTION_BORDER, "-insertbackground", "insertBackground", "Foreground", 244 DEF_ENTRY_INSERT_BG, -1, Tk_Offset(Entry, insertBorder), 0, 0, 0}, 245 {TK_OPTION_PIXELS, "-insertborderwidth", "insertBorderWidth", 246 "BorderWidth", DEF_ENTRY_INSERT_BD_COLOR, -1, 247 Tk_Offset(Entry, insertBorderWidth), 0, 248 (ClientData) DEF_ENTRY_INSERT_BD_MONO, 0}, 249 {TK_OPTION_INT, "-insertofftime", "insertOffTime", "OffTime", 250 DEF_ENTRY_INSERT_OFF_TIME, -1, Tk_Offset(Entry, insertOffTime), 251 0, 0, 0}, 252 {TK_OPTION_INT, "-insertontime", "insertOnTime", "OnTime", 253 DEF_ENTRY_INSERT_ON_TIME, -1, Tk_Offset(Entry, insertOnTime), 0, 0, 0}, 254 {TK_OPTION_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", 255 DEF_ENTRY_INSERT_WIDTH, -1, Tk_Offset(Entry, insertWidth), 0, 0, 0}, 256 {TK_OPTION_STRING, "-invalidcommand", "invalidCommand", "InvalidCommand", 257 DEF_ENTRY_INVALIDCMD, -1, Tk_Offset(Entry, invalidCmd), 258 TK_OPTION_NULL_OK, 0, 0}, 259 {TK_OPTION_SYNONYM, "-invcmd", NULL, NULL, 260 NULL, 0, -1, 0, (ClientData) "-invalidcommand", 0}, 261 {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", 262 DEF_ENTRY_JUSTIFY, -1, Tk_Offset(Entry, justify), 0, 0, 0}, 263 {TK_OPTION_RELIEF, "-relief", "relief", "Relief", 264 DEF_ENTRY_RELIEF, -1, Tk_Offset(Entry, relief), 0, 0, 0}, 265 {TK_OPTION_BORDER, "-readonlybackground", "readonlyBackground", 266 "ReadonlyBackground", DEF_ENTRY_READONLY_BG_COLOR, -1, 267 Tk_Offset(Entry, readonlyBorder), TK_OPTION_NULL_OK, 268 (ClientData) DEF_ENTRY_READONLY_BG_MONO, 0}, 269 {TK_OPTION_INT, "-repeatdelay", "repeatDelay", "RepeatDelay", 270 DEF_SPINBOX_REPEAT_DELAY, -1, Tk_Offset(Spinbox, repeatDelay), 271 0, 0, 0}, 272 {TK_OPTION_INT, "-repeatinterval", "repeatInterval", "RepeatInterval", 273 DEF_SPINBOX_REPEAT_INTERVAL, -1, Tk_Offset(Spinbox, repeatInterval), 274 0, 0, 0}, 275 {TK_OPTION_BORDER, "-selectbackground", "selectBackground", "Foreground", 276 DEF_ENTRY_SELECT_COLOR, -1, Tk_Offset(Entry, selBorder), 277 0, (ClientData) DEF_ENTRY_SELECT_MONO, 0}, 278 {TK_OPTION_PIXELS, "-selectborderwidth", "selectBorderWidth", 279 "BorderWidth", DEF_ENTRY_SELECT_BD_COLOR, -1, 280 Tk_Offset(Entry, selBorderWidth), 281 0, (ClientData) DEF_ENTRY_SELECT_BD_MONO, 0}, 282 {TK_OPTION_COLOR, "-selectforeground", "selectForeground", "Background", 283 DEF_ENTRY_SELECT_FG_COLOR, -1, Tk_Offset(Entry, selFgColorPtr), 284 TK_CONFIG_NULL_OK, (ClientData) DEF_ENTRY_SELECT_FG_MONO, 0}, 285 {TK_OPTION_STRING_TABLE, "-state", "state", "State", 286 DEF_ENTRY_STATE, -1, Tk_Offset(Entry, state), 287 0, (ClientData) stateStrings, 0}, 288 {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus", 289 DEF_ENTRY_TAKE_FOCUS, -1, Tk_Offset(Entry, takeFocus), 290 TK_CONFIG_NULL_OK, 0, 0}, 291 {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", 292 DEF_ENTRY_TEXT_VARIABLE, -1, Tk_Offset(Entry, textVarName), 293 TK_CONFIG_NULL_OK, 0, 0}, 294 {TK_OPTION_DOUBLE, "-to", "to", "To", 295 DEF_SPINBOX_TO, -1, Tk_Offset(Spinbox, toValue), 0, 0, 0}, 296 {TK_OPTION_STRING_TABLE, "-validate", "validate", "Validate", 297 DEF_ENTRY_VALIDATE, -1, Tk_Offset(Entry, validate), 298 0, (ClientData) validateStrings, 0}, 299 {TK_OPTION_STRING, "-validatecommand", "validateCommand","ValidateCommand", 300 NULL, -1, Tk_Offset(Entry, validateCmd), TK_CONFIG_NULL_OK, 0, 0}, 301 {TK_OPTION_STRING, "-values", "values", "Values", 302 DEF_SPINBOX_VALUES, -1, Tk_Offset(Spinbox, valueStr), 303 TK_OPTION_NULL_OK, 0, 0}, 304 {TK_OPTION_SYNONYM, "-vcmd", NULL, NULL, 305 NULL, 0, -1, 0, (ClientData) "-validatecommand", 0}, 306 {TK_OPTION_INT, "-width", "width", "Width", 307 DEF_ENTRY_WIDTH, -1, Tk_Offset(Entry, prefWidth), 0, 0, 0}, 308 {TK_OPTION_BOOLEAN, "-wrap", "wrap", "Wrap", 309 DEF_SPINBOX_WRAP, -1, Tk_Offset(Spinbox, wrap), 0, 0, 0}, 310 {TK_OPTION_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", 311 DEF_ENTRY_SCROLL_COMMAND, -1, Tk_Offset(Entry, scrollCmd), 312 TK_CONFIG_NULL_OK, 0, 0}, 313 {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} 314}; 315 316/* 317 * The following tables define the entry widget commands (and sub-commands) 318 * and map the indexes into the string tables into enumerated types used to 319 * dispatch the entry widget command. 320 */ 321 322static CONST char *entryCmdNames[] = { 323 "bbox", "cget", "configure", "delete", "get", "icursor", "index", 324 "insert", "scan", "selection", "validate", "xview", NULL 325}; 326 327enum entryCmd { 328 COMMAND_BBOX, COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_DELETE, 329 COMMAND_GET, COMMAND_ICURSOR, COMMAND_INDEX, COMMAND_INSERT, 330 COMMAND_SCAN, COMMAND_SELECTION, COMMAND_VALIDATE, COMMAND_XVIEW 331}; 332 333static CONST char *selCmdNames[] = { 334 "adjust", "clear", "from", "present", "range", "to", NULL 335}; 336 337enum selCmd { 338 SELECTION_ADJUST, SELECTION_CLEAR, SELECTION_FROM, 339 SELECTION_PRESENT, SELECTION_RANGE, SELECTION_TO 340}; 341 342/* 343 * The following tables define the spinbox widget commands (and sub-commands) 344 * and map the indexes into the string tables into enumerated types used to 345 * dispatch the spinbox widget command. 346 */ 347 348static CONST char *sbCmdNames[] = { 349 "bbox", "cget", "configure", "delete", "get", "icursor", "identify", 350 "index", "insert", "invoke", "scan", "selection", "set", 351 "validate", "xview", NULL 352}; 353 354enum sbCmd { 355 SB_CMD_BBOX, SB_CMD_CGET, SB_CMD_CONFIGURE, SB_CMD_DELETE, 356 SB_CMD_GET, SB_CMD_ICURSOR, SB_CMD_IDENTIFY, SB_CMD_INDEX, 357 SB_CMD_INSERT, SB_CMD_INVOKE, SB_CMD_SCAN, SB_CMD_SELECTION, 358 SB_CMD_SET, SB_CMD_VALIDATE, SB_CMD_XVIEW 359}; 360 361static CONST char *sbSelCmdNames[] = { 362 "adjust", "clear", "element", "from", "present", "range", "to", NULL 363}; 364 365enum sbselCmd { 366 SB_SEL_ADJUST, SB_SEL_CLEAR, SB_SEL_ELEMENT, SB_SEL_FROM, 367 SB_SEL_PRESENT, SB_SEL_RANGE, SB_SEL_TO 368}; 369 370/* 371 * Extra for selection of elements 372 */ 373 374/* 375 * This is the string array corresponding to the enum in selelement. If you 376 * modify them, you must modify the strings here. 377 */ 378 379static CONST char *selElementNames[] = { 380 "none", "buttondown", "buttonup", NULL, "entry" 381}; 382 383/* 384 * Flags for GetEntryIndex function: 385 */ 386 387#define ZERO_OK 1 388#define LAST_PLUS_ONE_OK 2 389 390/* 391 * Forward declarations for functions defined later in this file: 392 */ 393 394static int ConfigureEntry(Tcl_Interp *interp, Entry *entryPtr, 395 int objc, Tcl_Obj *CONST objv[], int flags); 396static void DeleteChars(Entry *entryPtr, int index, int count); 397static void DestroyEntry(char *memPtr); 398static void DisplayEntry(ClientData clientData); 399static void EntryBlinkProc(ClientData clientData); 400static void EntryCmdDeletedProc(ClientData clientData); 401static void EntryComputeGeometry(Entry *entryPtr); 402static void EntryEventProc(ClientData clientData, 403 XEvent *eventPtr); 404static void EntryFocusProc(Entry *entryPtr, int gotFocus); 405static int EntryFetchSelection(ClientData clientData, int offset, 406 char *buffer, int maxBytes); 407static void EntryLostSelection(ClientData clientData); 408static void EventuallyRedraw(Entry *entryPtr); 409static void EntryScanTo(Entry *entryPtr, int y); 410static void EntrySetValue(Entry *entryPtr, CONST char *value); 411static void EntrySelectTo(Entry *entryPtr, int index); 412static char * EntryTextVarProc(ClientData clientData, 413 Tcl_Interp *interp, CONST char *name1, 414 CONST char *name2, int flags); 415static void EntryUpdateScrollbar(Entry *entryPtr); 416static int EntryValidate(Entry *entryPtr, char *cmd); 417static int EntryValidateChange(Entry *entryPtr, char *change, 418 CONST char *newStr, int index, int type); 419static void ExpandPercents(Entry *entryPtr, CONST char *before, 420 CONST char *change, CONST char *newStr, int index, 421 int type, Tcl_DString *dsPtr); 422static void EntryValueChanged(Entry *entryPtr, 423 CONST char *newValue); 424static void EntryVisibleRange(Entry *entryPtr, 425 double *firstPtr, double *lastPtr); 426static int EntryWidgetObjCmd(ClientData clientData, 427 Tcl_Interp *interp, int objc, 428 Tcl_Obj *CONST objv[]); 429static void EntryWorldChanged(ClientData instanceData); 430static int GetEntryIndex(Tcl_Interp *interp, Entry *entryPtr, 431 char *string, int *indexPtr); 432static void InsertChars(Entry *entryPtr, int index, char *string); 433 434/* 435 * These forward declarations are the spinbox specific ones: 436 */ 437 438static int SpinboxWidgetObjCmd(ClientData clientData, 439 Tcl_Interp *interp, int objc, 440 Tcl_Obj *CONST objv[]); 441static int GetSpinboxElement(Spinbox *sbPtr, int x, int y); 442static int SpinboxInvoke(Tcl_Interp *interp, Spinbox *sbPtr, 443 int element); 444static int ComputeFormat(Spinbox *sbPtr); 445 446/* 447 * The structure below defines widget class behavior by means of functions 448 * that can be invoked from generic window code. 449 */ 450 451static Tk_ClassProcs entryClass = { 452 sizeof(Tk_ClassProcs), /* size */ 453 EntryWorldChanged, /* worldChangedProc */ 454}; 455 456 457/* 458 *-------------------------------------------------------------- 459 * 460 * Tk_EntryObjCmd -- 461 * 462 * This function is invoked to process the "entry" Tcl command. See the 463 * user documentation for details on what it does. 464 * 465 * Results: 466 * A standard Tcl result. 467 * 468 * Side effects: 469 * See the user documentation. 470 * 471 *-------------------------------------------------------------- 472 */ 473 474int 475Tk_EntryObjCmd( 476 ClientData clientData, /* NULL. */ 477 Tcl_Interp *interp, /* Current interpreter. */ 478 int objc, /* Number of arguments. */ 479 Tcl_Obj *CONST objv[]) /* Argument objects. */ 480{ 481 register Entry *entryPtr; 482 Tk_OptionTable optionTable; 483 Tk_Window tkwin; 484 char *tmp; 485 486 if (objc < 2) { 487 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); 488 return TCL_ERROR; 489 } 490 491 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), 492 Tcl_GetString(objv[1]), NULL); 493 if (tkwin == NULL) { 494 return TCL_ERROR; 495 } 496 497 /* 498 * Create the option table for this widget class. If it has already been 499 * created, Tk will return the cached value. 500 */ 501 502 optionTable = Tk_CreateOptionTable(interp, entryOptSpec); 503 504 /* 505 * Initialize the fields of the structure that won't be initialized by 506 * ConfigureEntry, or that ConfigureEntry requires to be initialized 507 * already (e.g. resource pointers). Only the non-NULL/0 data must be 508 * initialized as memset covers the rest. 509 */ 510 511 entryPtr = (Entry *) ckalloc(sizeof(Entry)); 512 memset(entryPtr, 0, sizeof(Entry)); 513 514 entryPtr->tkwin = tkwin; 515 entryPtr->display = Tk_Display(tkwin); 516 entryPtr->interp = interp; 517 entryPtr->widgetCmd = Tcl_CreateObjCommand(interp, 518 Tk_PathName(entryPtr->tkwin), EntryWidgetObjCmd, 519 (ClientData) entryPtr, EntryCmdDeletedProc); 520 entryPtr->optionTable = optionTable; 521 entryPtr->type = TK_ENTRY; 522 tmp = (char *) ckalloc(1); 523 tmp[0] = '\0'; 524 entryPtr->string = tmp; 525 entryPtr->selectFirst = -1; 526 entryPtr->selectLast = -1; 527 528 entryPtr->cursor = None; 529 entryPtr->exportSelection = 1; 530 entryPtr->justify = TK_JUSTIFY_LEFT; 531 entryPtr->relief = TK_RELIEF_FLAT; 532 entryPtr->state = STATE_NORMAL; 533 entryPtr->displayString = entryPtr->string; 534 entryPtr->inset = XPAD; 535 entryPtr->textGC = None; 536 entryPtr->selTextGC = None; 537 entryPtr->highlightGC = None; 538 entryPtr->avgWidth = 1; 539 entryPtr->validate = VALIDATE_NONE; 540 541 /* 542 * Keep a hold of the associated tkwin until we destroy the entry, 543 * otherwise Tk might free it while we still need it. 544 */ 545 546 Tcl_Preserve((ClientData) entryPtr->tkwin); 547 548 Tk_SetClass(entryPtr->tkwin, "Entry"); 549 Tk_SetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr); 550 Tk_CreateEventHandler(entryPtr->tkwin, 551 ExposureMask|StructureNotifyMask|FocusChangeMask, 552 EntryEventProc, (ClientData) entryPtr); 553 Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING, 554 EntryFetchSelection, (ClientData) entryPtr, XA_STRING); 555 556 if ((Tk_InitOptions(interp, (char *) entryPtr, optionTable, tkwin) 557 != TCL_OK) || 558 (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK)) { 559 Tk_DestroyWindow(entryPtr->tkwin); 560 return TCL_ERROR; 561 } 562 563 Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC); 564 return TCL_OK; 565} 566 567/* 568 *-------------------------------------------------------------- 569 * 570 * EntryWidgetObjCmd -- 571 * 572 * This function is invoked to process the Tcl command that corresponds 573 * to a widget managed by this module. See the user documentation for 574 * details on what it does. 575 * 576 * Results: 577 * A standard Tcl result. 578 * 579 * Side effects: 580 * See the user documentation. 581 * 582 *-------------------------------------------------------------- 583 */ 584 585static int 586EntryWidgetObjCmd( 587 ClientData clientData, /* Information about entry widget. */ 588 Tcl_Interp *interp, /* Current interpreter. */ 589 int objc, /* Number of arguments. */ 590 Tcl_Obj *CONST objv[]) /* Argument objects. */ 591{ 592 Entry *entryPtr = (Entry *) clientData; 593 int cmdIndex, selIndex, result; 594 Tcl_Obj *objPtr; 595 596 if (objc < 2) { 597 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 598 return TCL_ERROR; 599 } 600 601 /* 602 * Parse the widget command by looking up the second token in the list of 603 * valid command names. 604 */ 605 606 result = Tcl_GetIndexFromObj(interp, objv[1], entryCmdNames, 607 "option", 0, &cmdIndex); 608 if (result != TCL_OK) { 609 return result; 610 } 611 612 Tcl_Preserve((ClientData) entryPtr); 613 switch ((enum entryCmd) cmdIndex) { 614 case COMMAND_BBOX: { 615 int index, x, y, width, height; 616 char buf[TCL_INTEGER_SPACE * 4]; 617 618 if (objc != 3) { 619 Tcl_WrongNumArgs(interp, 2, objv, "index"); 620 goto error; 621 } 622 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 623 &index) != TCL_OK) { 624 goto error; 625 } 626 if ((index == entryPtr->numChars) && (index > 0)) { 627 index--; 628 } 629 Tk_CharBbox(entryPtr->textLayout, index, &x, &y, &width, &height); 630 sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX, 631 y + entryPtr->layoutY, width, height); 632 Tcl_SetResult(interp, buf, TCL_VOLATILE); 633 break; 634 } 635 636 case COMMAND_CGET: 637 if (objc != 3) { 638 Tcl_WrongNumArgs(interp, 2, objv, "option"); 639 goto error; 640 } 641 642 objPtr = Tk_GetOptionValue(interp, (char *) entryPtr, 643 entryPtr->optionTable, objv[2], entryPtr->tkwin); 644 if (objPtr == NULL) { 645 goto error; 646 } else { 647 Tcl_SetObjResult(interp, objPtr); 648 } 649 break; 650 651 case COMMAND_CONFIGURE: 652 if (objc <= 3) { 653 objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr, 654 entryPtr->optionTable, 655 (objc == 3) ? objv[2] : NULL, 656 entryPtr->tkwin); 657 if (objPtr == NULL) { 658 goto error; 659 } else { 660 Tcl_SetObjResult(interp, objPtr); 661 } 662 } else { 663 result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0); 664 } 665 break; 666 667 case COMMAND_DELETE: { 668 int first, last; 669 670 if ((objc < 3) || (objc > 4)) { 671 Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?"); 672 goto error; 673 } 674 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 675 &first) != TCL_OK) { 676 goto error; 677 } 678 if (objc == 3) { 679 last = first + 1; 680 } else if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]), 681 &last) != TCL_OK) { 682 goto error; 683 } 684 if ((last >= first) && (entryPtr->state == STATE_NORMAL)) { 685 DeleteChars(entryPtr, first, last - first); 686 } 687 break; 688 } 689 690 case COMMAND_GET: 691 if (objc != 2) { 692 Tcl_WrongNumArgs(interp, 2, objv, NULL); 693 goto error; 694 } 695 Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1); 696 break; 697 698 case COMMAND_ICURSOR: 699 if (objc != 3) { 700 Tcl_WrongNumArgs(interp, 2, objv, "pos"); 701 goto error; 702 } 703 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 704 &entryPtr->insertPos) != TCL_OK) { 705 goto error; 706 } 707 EventuallyRedraw(entryPtr); 708 break; 709 710 case COMMAND_INDEX: { 711 int index; 712 713 if (objc != 3) { 714 Tcl_WrongNumArgs(interp, 2, objv, "string"); 715 goto error; 716 } 717 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 718 &index) != TCL_OK) { 719 goto error; 720 } 721 Tcl_SetObjResult(interp, Tcl_NewIntObj(index)); 722 break; 723 } 724 725 case COMMAND_INSERT: { 726 int index; 727 728 if (objc != 4) { 729 Tcl_WrongNumArgs(interp, 2, objv, "index text"); 730 goto error; 731 } 732 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 733 &index) != TCL_OK) { 734 goto error; 735 } 736 if (entryPtr->state == STATE_NORMAL) { 737 InsertChars(entryPtr, index, Tcl_GetString(objv[3])); 738 } 739 break; 740 } 741 742 case COMMAND_SCAN: { 743 int x; 744 char *minorCmd; 745 746 if (objc != 4) { 747 Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x"); 748 goto error; 749 } 750 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { 751 goto error; 752 } 753 754 minorCmd = Tcl_GetString(objv[2]); 755 if (minorCmd[0] == 'm' 756 && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) { 757 entryPtr->scanMarkX = x; 758 entryPtr->scanMarkIndex = entryPtr->leftIndex; 759 } else if ((minorCmd[0] == 'd') 760 && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) { 761 EntryScanTo(entryPtr, x); 762 } else { 763 Tcl_AppendResult(interp, "bad scan option \"", 764 Tcl_GetString(objv[2]), "\": must be mark or dragto", 765 NULL); 766 goto error; 767 } 768 break; 769 } 770 771 case COMMAND_SELECTION: { 772 int index, index2; 773 774 if (objc < 3) { 775 Tcl_WrongNumArgs(interp, 2, objv, "option ?index?"); 776 goto error; 777 } 778 779 /* 780 * Parse the selection sub-command, using the command table 781 * "selCmdNames" defined above. 782 */ 783 784 result = Tcl_GetIndexFromObj(interp, objv[2], selCmdNames, 785 "selection option", 0, &selIndex); 786 if (result != TCL_OK) { 787 goto error; 788 } 789 790 /* 791 * Disabled entries don't allow the selection to be modified, but 792 * 'selection present' must return a boolean. 793 */ 794 795 if ((entryPtr->state == STATE_DISABLED) 796 && (selIndex != SELECTION_PRESENT)) { 797 goto done; 798 } 799 800 switch (selIndex) { 801 case SELECTION_ADJUST: 802 if (objc != 4) { 803 Tcl_WrongNumArgs(interp, 3, objv, "index"); 804 goto error; 805 } 806 if (GetEntryIndex(interp, entryPtr, 807 Tcl_GetString(objv[3]), &index) != TCL_OK) { 808 goto error; 809 } 810 if (entryPtr->selectFirst >= 0) { 811 int half1, half2; 812 813 half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2; 814 half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2; 815 if (index < half1) { 816 entryPtr->selectAnchor = entryPtr->selectLast; 817 } else if (index > half2) { 818 entryPtr->selectAnchor = entryPtr->selectFirst; 819 } else { 820 /* 821 * We're at about the halfway point in the selection; just 822 * keep the existing anchor. 823 */ 824 } 825 } 826 EntrySelectTo(entryPtr, index); 827 break; 828 829 case SELECTION_CLEAR: 830 if (objc != 3) { 831 Tcl_WrongNumArgs(interp, 3, objv, NULL); 832 goto error; 833 } 834 if (entryPtr->selectFirst >= 0) { 835 entryPtr->selectFirst = -1; 836 entryPtr->selectLast = -1; 837 EventuallyRedraw(entryPtr); 838 } 839 goto done; 840 841 case SELECTION_FROM: 842 if (objc != 4) { 843 Tcl_WrongNumArgs(interp, 3, objv, "index"); 844 goto error; 845 } 846 if (GetEntryIndex(interp, entryPtr, 847 Tcl_GetString(objv[3]), &index) != TCL_OK) { 848 goto error; 849 } 850 entryPtr->selectAnchor = index; 851 break; 852 853 case SELECTION_PRESENT: 854 if (objc != 3) { 855 Tcl_WrongNumArgs(interp, 3, objv, NULL); 856 goto error; 857 } 858 Tcl_SetObjResult(interp, 859 Tcl_NewBooleanObj((entryPtr->selectFirst >= 0))); 860 goto done; 861 862 case SELECTION_RANGE: 863 if (objc != 5) { 864 Tcl_WrongNumArgs(interp, 3, objv, "start end"); 865 goto error; 866 } 867 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]), 868 &index) != TCL_OK) { 869 goto error; 870 } 871 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[4]), 872 &index2) != TCL_OK) { 873 goto error; 874 } 875 if (index >= index2) { 876 entryPtr->selectFirst = -1; 877 entryPtr->selectLast = -1; 878 } else { 879 entryPtr->selectFirst = index; 880 entryPtr->selectLast = index2; 881 } 882 if (!(entryPtr->flags & GOT_SELECTION) 883 && (entryPtr->exportSelection)) { 884 Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, 885 EntryLostSelection, (ClientData) entryPtr); 886 entryPtr->flags |= GOT_SELECTION; 887 } 888 EventuallyRedraw(entryPtr); 889 break; 890 891 case SELECTION_TO: 892 if (objc != 4) { 893 Tcl_WrongNumArgs(interp, 3, objv, "index"); 894 goto error; 895 } 896 if (GetEntryIndex(interp, entryPtr, 897 Tcl_GetString(objv[3]), &index) != TCL_OK) { 898 goto error; 899 } 900 EntrySelectTo(entryPtr, index); 901 break; 902 } 903 break; 904 } 905 906 case COMMAND_VALIDATE: { 907 int code; 908 909 if (objc != 2) { 910 Tcl_WrongNumArgs(interp, 2, objv, NULL); 911 goto error; 912 } 913 selIndex = entryPtr->validate; 914 entryPtr->validate = VALIDATE_ALL; 915 code = EntryValidateChange(entryPtr, NULL, entryPtr->string, 916 -1, VALIDATE_FORCED); 917 if (entryPtr->validate != VALIDATE_NONE) { 918 entryPtr->validate = selIndex; 919 } 920 Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK))); 921 break; 922 } 923 924 case COMMAND_XVIEW: { 925 int index; 926 927 if (objc == 2) { 928 double first, last; 929 char buf[TCL_DOUBLE_SPACE]; 930 931 EntryVisibleRange(entryPtr, &first, &last); 932 Tcl_PrintDouble(NULL, first, buf); 933 Tcl_SetResult(interp, buf, TCL_VOLATILE); 934 Tcl_PrintDouble(NULL, last, buf); 935 Tcl_AppendResult(interp, " ", buf, NULL); 936 goto done; 937 } else if (objc == 3) { 938 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 939 &index) != TCL_OK) { 940 goto error; 941 } 942 } else { 943 double fraction; 944 int count; 945 946 index = entryPtr->leftIndex; 947 switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction, 948 &count)) { 949 case TK_SCROLL_ERROR: 950 goto error; 951 case TK_SCROLL_MOVETO: 952 index = (int) ((fraction * entryPtr->numChars) + 0.5); 953 break; 954 case TK_SCROLL_PAGES: { 955 int charsPerPage; 956 957 charsPerPage = ((Tk_Width(entryPtr->tkwin) 958 - 2 * entryPtr->inset) / entryPtr->avgWidth) - 2; 959 if (charsPerPage < 1) { 960 charsPerPage = 1; 961 } 962 index += count * charsPerPage; 963 break; 964 } 965 case TK_SCROLL_UNITS: 966 index += count; 967 break; 968 } 969 } 970 if (index >= entryPtr->numChars) { 971 index = entryPtr->numChars - 1; 972 } 973 if (index < 0) { 974 index = 0; 975 } 976 entryPtr->leftIndex = index; 977 entryPtr->flags |= UPDATE_SCROLLBAR; 978 EntryComputeGeometry(entryPtr); 979 EventuallyRedraw(entryPtr); 980 break; 981 } 982 } 983 984 done: 985 Tcl_Release((ClientData) entryPtr); 986 return result; 987 988 error: 989 Tcl_Release((ClientData) entryPtr); 990 return TCL_ERROR; 991} 992 993/* 994 *---------------------------------------------------------------------- 995 * 996 * DestroyEntry -- 997 * 998 * This function is invoked by Tcl_EventuallyFree or Tcl_Release to clean 999 * up the internal structure of an entry at a safe time (when no-one is 1000 * using it anymore). 1001 * 1002 * Results: 1003 * None. 1004 * 1005 * Side effects: 1006 * Everything associated with the entry is freed up. 1007 * 1008 *---------------------------------------------------------------------- 1009 */ 1010 1011static void 1012DestroyEntry( 1013 char *memPtr) /* Info about entry widget. */ 1014{ 1015 Entry *entryPtr = (Entry *) memPtr; 1016 1017 /* 1018 * Free up all the stuff that requires special handling, then let 1019 * Tk_FreeOptions handle all the standard option-related stuff. 1020 */ 1021 1022 ckfree((char *)entryPtr->string); 1023 if (entryPtr->textVarName != NULL) { 1024 Tcl_UntraceVar(entryPtr->interp, entryPtr->textVarName, 1025 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 1026 EntryTextVarProc, (ClientData) entryPtr); 1027 entryPtr->flags &= ~ENTRY_VAR_TRACED; 1028 } 1029 if (entryPtr->textGC != None) { 1030 Tk_FreeGC(entryPtr->display, entryPtr->textGC); 1031 } 1032 if (entryPtr->selTextGC != None) { 1033 Tk_FreeGC(entryPtr->display, entryPtr->selTextGC); 1034 } 1035 Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler); 1036 if (entryPtr->displayString != entryPtr->string) { 1037 ckfree((char *)entryPtr->displayString); 1038 } 1039 if (entryPtr->type == TK_SPINBOX) { 1040 Spinbox *sbPtr = (Spinbox *) entryPtr; 1041 1042 if (sbPtr->listObj != NULL) { 1043 Tcl_DecrRefCount(sbPtr->listObj); 1044 sbPtr->listObj = NULL; 1045 } 1046 if (sbPtr->formatBuf) { 1047 ckfree(sbPtr->formatBuf); 1048 } 1049 } 1050 Tk_FreeTextLayout(entryPtr->textLayout); 1051 Tk_FreeConfigOptions((char *) entryPtr, entryPtr->optionTable, 1052 entryPtr->tkwin); 1053 Tcl_Release((ClientData) entryPtr->tkwin); 1054 entryPtr->tkwin = NULL; 1055 1056 ckfree((char *) entryPtr); 1057} 1058 1059/* 1060 *---------------------------------------------------------------------- 1061 * 1062 * ConfigureEntry -- 1063 * 1064 * This function is called to process an argv/argc list, plus the Tk 1065 * option database, in order to configure (or reconfigure) an entry 1066 * widget. 1067 * 1068 * Results: 1069 * The return value is a standard Tcl result. If TCL_ERROR is returned, 1070 * then the interp's result contains an error message. 1071 * 1072 * Side effects: 1073 * Configuration information, such as colors, border width, etc. get set 1074 * for entryPtr; old resources get freed, if there were any. 1075 * 1076 *---------------------------------------------------------------------- 1077 */ 1078 1079static int 1080ConfigureEntry( 1081 Tcl_Interp *interp, /* Used for error reporting. */ 1082 Entry *entryPtr, /* Information about widget; may or may not 1083 * already have values for some fields. */ 1084 int objc, /* Number of valid entries in argv. */ 1085 Tcl_Obj *CONST objv[], /* Argument objects. */ 1086 int flags) /* Flags to pass to Tk_ConfigureWidget. */ 1087{ 1088 Tk_SavedOptions savedOptions; 1089 Tk_3DBorder border; 1090 Tcl_Obj *errorResult = NULL; 1091 Spinbox *sbPtr = (Spinbox *) entryPtr; 1092 /* Only used when this widget is of type 1093 * TK_SPINBOX */ 1094 char *oldValues = NULL; /* lint initialization */ 1095 char *oldFormat = NULL; /* lint initialization */ 1096 int error; 1097 int oldExport = 0; /* lint initialization */ 1098 int valuesChanged = 0; /* lint initialization */ 1099 double oldFrom = 0.0; /* lint initialization */ 1100 double oldTo = 0.0; /* lint initialization */ 1101 1102 /* 1103 * Eliminate any existing trace on a variable monitored by the entry. 1104 */ 1105 1106 if ((entryPtr->textVarName != NULL) 1107 && (entryPtr->flags & ENTRY_VAR_TRACED)) { 1108 Tcl_UntraceVar(interp, entryPtr->textVarName, 1109 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 1110 EntryTextVarProc, (ClientData) entryPtr); 1111 entryPtr->flags &= ~ENTRY_VAR_TRACED; 1112 } 1113 1114 /* 1115 * Store old values that we need to effect certain behavior if they change 1116 * value. 1117 */ 1118 1119 oldExport = entryPtr->exportSelection; 1120 if (entryPtr->type == TK_SPINBOX) { 1121 oldValues = sbPtr->valueStr; 1122 oldFormat = sbPtr->reqFormat; 1123 oldFrom = sbPtr->fromValue; 1124 oldTo = sbPtr->toValue; 1125 } 1126 1127 for (error = 0; error <= 1; error++) { 1128 if (!error) { 1129 /* 1130 * First pass: set options to new values. 1131 */ 1132 1133 if (Tk_SetOptions(interp, (char *) entryPtr, 1134 entryPtr->optionTable, objc, objv, 1135 entryPtr->tkwin, &savedOptions, NULL) != TCL_OK) { 1136 continue; 1137 } 1138 } else { 1139 /* 1140 * Second pass: restore options to old values. 1141 */ 1142 1143 errorResult = Tcl_GetObjResult(interp); 1144 Tcl_IncrRefCount(errorResult); 1145 Tk_RestoreSavedOptions(&savedOptions); 1146 } 1147 1148 /* 1149 * A few other options also need special processing, such as parsing 1150 * the geometry and setting the background from a 3-D border. 1151 */ 1152 1153 if ((entryPtr->state == STATE_DISABLED) && 1154 (entryPtr->disabledBorder != NULL)) { 1155 border = entryPtr->disabledBorder; 1156 } else if ((entryPtr->state == STATE_READONLY) && 1157 (entryPtr->readonlyBorder != NULL)) { 1158 border = entryPtr->readonlyBorder; 1159 } else { 1160 border = entryPtr->normalBorder; 1161 } 1162 Tk_SetBackgroundFromBorder(entryPtr->tkwin, border); 1163 1164 if (entryPtr->insertWidth <= 0) { 1165 entryPtr->insertWidth = 2; 1166 } 1167 if (entryPtr->insertBorderWidth > entryPtr->insertWidth/2) { 1168 entryPtr->insertBorderWidth = entryPtr->insertWidth/2; 1169 } 1170 1171 if (entryPtr->type == TK_SPINBOX) { 1172 if (sbPtr->fromValue > sbPtr->toValue) { 1173 Tcl_SetResult(interp, 1174 "-to value must be greater than -from value", 1175 TCL_VOLATILE); 1176 continue; 1177 } 1178 1179 if (sbPtr->reqFormat && (oldFormat != sbPtr->reqFormat)) { 1180 /* 1181 * Make sure that the given format is somewhat correct, and 1182 * calculate the minimum space we'll need for the values as 1183 * strings. 1184 */ 1185 1186 int min, max; 1187 size_t formatLen, formatSpace = TCL_DOUBLE_SPACE; 1188 char fbuf[4], *fmt = sbPtr->reqFormat; 1189 1190 formatLen = strlen(fmt); 1191 if ((fmt[0] != '%') || (fmt[formatLen-1] != 'f')) { 1192 badFormatOpt: 1193 Tcl_AppendResult(interp, "bad spinbox format specifier \"", 1194 sbPtr->reqFormat, "\"", NULL); 1195 continue; 1196 } 1197 if ((sscanf(fmt, "%%%d.%d%[f]", &min, &max, fbuf) == 3) 1198 && (max >= 0)) { 1199 formatSpace = min + max + 1; 1200 } else if (((sscanf(fmt, "%%.%d%[f]", &min, fbuf) == 2) 1201 || (sscanf(fmt, "%%%d%[f]", &min, fbuf) == 2) 1202 || (sscanf(fmt, "%%%d.%[f]", &min, fbuf) == 2)) 1203 && (min >= 0)) { 1204 formatSpace = min + 1; 1205 } else { 1206 goto badFormatOpt; 1207 } 1208 if (formatSpace < TCL_DOUBLE_SPACE) { 1209 formatSpace = TCL_DOUBLE_SPACE; 1210 } 1211 sbPtr->formatBuf = ckrealloc(sbPtr->formatBuf, formatSpace); 1212 1213 /* 1214 * We perturb the value of oldFrom to allow us to go into the 1215 * branch below that will reformat the displayed value. 1216 */ 1217 1218 oldFrom = sbPtr->fromValue - 1; 1219 } 1220 1221 /* 1222 * See if we have to rearrange our listObj data. 1223 */ 1224 1225 if (oldValues != sbPtr->valueStr) { 1226 if (sbPtr->listObj != NULL) { 1227 Tcl_DecrRefCount(sbPtr->listObj); 1228 } 1229 sbPtr->listObj = NULL; 1230 if (sbPtr->valueStr != NULL) { 1231 Tcl_Obj *newObjPtr; 1232 int nelems; 1233 1234 newObjPtr = Tcl_NewStringObj(sbPtr->valueStr, -1); 1235 if (Tcl_ListObjLength(interp, newObjPtr, &nelems) 1236 != TCL_OK) { 1237 valuesChanged = -1; 1238 continue; 1239 } 1240 sbPtr->listObj = newObjPtr; 1241 Tcl_IncrRefCount(sbPtr->listObj); 1242 sbPtr->nElements = nelems; 1243 sbPtr->eIndex = 0; 1244 valuesChanged++; 1245 } 1246 } 1247 } 1248 1249 /* 1250 * Restart the cursor timing sequence in case the on-time or off-time 1251 * just changed. Set validate temporarily to none, so the configure 1252 * doesn't cause it to be triggered. 1253 */ 1254 1255 if (entryPtr->flags & GOT_FOCUS) { 1256 int validate = entryPtr->validate; 1257 1258 entryPtr->validate = VALIDATE_NONE; 1259 EntryFocusProc(entryPtr, 1); 1260 entryPtr->validate = validate; 1261 } 1262 1263 /* 1264 * Claim the selection if we've suddenly started exporting it. 1265 */ 1266 1267 if (entryPtr->exportSelection && (!oldExport) 1268 && (entryPtr->selectFirst != -1) 1269 && !(entryPtr->flags & GOT_SELECTION)) { 1270 Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection, 1271 (ClientData) entryPtr); 1272 entryPtr->flags |= GOT_SELECTION; 1273 } 1274 1275 /* 1276 * Recompute the window's geometry and arrange for it to be 1277 * redisplayed. 1278 */ 1279 1280 Tk_SetInternalBorder(entryPtr->tkwin, 1281 entryPtr->borderWidth + entryPtr->highlightWidth); 1282 if (entryPtr->highlightWidth <= 0) { 1283 entryPtr->highlightWidth = 0; 1284 } 1285 entryPtr->inset = entryPtr->highlightWidth 1286 + entryPtr->borderWidth + XPAD; 1287 break; 1288 } 1289 if (!error) { 1290 Tk_FreeSavedOptions(&savedOptions); 1291 } 1292 1293 /* 1294 * If the entry is tied to the value of a variable, create the variable if 1295 * it doesn't exist, and set the entry's value from the variable's value. 1296 */ 1297 1298 if (entryPtr->textVarName != NULL) { 1299 CONST char *value; 1300 1301 value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY); 1302 if (value == NULL) { 1303 EntryValueChanged(entryPtr, NULL); 1304 } else { 1305 EntrySetValue(entryPtr, value); 1306 } 1307 } 1308 1309 if (entryPtr->type == TK_SPINBOX) { 1310 ComputeFormat(sbPtr); 1311 1312 if (valuesChanged > 0) { 1313 Tcl_Obj *objPtr; 1314 1315 /* 1316 * No check for error return, because there shouldn't be one given 1317 * the check for valid list above. 1318 */ 1319 1320 Tcl_ListObjIndex(interp, sbPtr->listObj, 0, &objPtr); 1321 EntryValueChanged(entryPtr, Tcl_GetString(objPtr)); 1322 } else if ((sbPtr->valueStr == NULL) 1323 && !DOUBLES_EQ(sbPtr->fromValue, sbPtr->toValue) 1324 && (!DOUBLES_EQ(sbPtr->fromValue, oldFrom) 1325 || !DOUBLES_EQ(sbPtr->toValue, oldTo))) { 1326 /* 1327 * If the valueStr is empty and -from && -to are specified, check 1328 * to see if the current string is within the range. If not, it 1329 * will be constrained to the nearest edge. If the current string 1330 * isn't a double value, we set it to -from. 1331 */ 1332 1333 int code; 1334 double dvalue; 1335 1336 code = Tcl_GetDouble(NULL, entryPtr->string, &dvalue); 1337 if (code != TCL_OK) { 1338 dvalue = sbPtr->fromValue; 1339 } else { 1340 if (dvalue > sbPtr->toValue) { 1341 dvalue = sbPtr->toValue; 1342 } else if (dvalue < sbPtr->fromValue) { 1343 dvalue = sbPtr->fromValue; 1344 } 1345 } 1346 sprintf(sbPtr->formatBuf, sbPtr->valueFormat, dvalue); 1347 EntryValueChanged(entryPtr, sbPtr->formatBuf); 1348 } 1349 } 1350 1351 /* 1352 * Set up a trace on the variable's value after we've possibly constrained 1353 * the value according to new -from/-to values. 1354 */ 1355 1356 if ((entryPtr->textVarName != NULL) 1357 && !(entryPtr->flags & ENTRY_VAR_TRACED)) { 1358 Tcl_TraceVar(interp, entryPtr->textVarName, 1359 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 1360 EntryTextVarProc, (ClientData) entryPtr); 1361 entryPtr->flags |= ENTRY_VAR_TRACED; 1362 } 1363 1364 EntryWorldChanged((ClientData) entryPtr); 1365 if (error) { 1366 Tcl_SetObjResult(interp, errorResult); 1367 Tcl_DecrRefCount(errorResult); 1368 return TCL_ERROR; 1369 } else { 1370 return TCL_OK; 1371 } 1372} 1373 1374/* 1375 *--------------------------------------------------------------------------- 1376 * 1377 * EntryWorldChanged -- 1378 * 1379 * This function is called when the world has changed in some way and the 1380 * widget needs to recompute all its graphics contexts and determine its 1381 * new geometry. 1382 * 1383 * Results: 1384 * None. 1385 * 1386 * Side effects: 1387 * Entry will be relayed out and redisplayed. 1388 * 1389 *--------------------------------------------------------------------------- 1390 */ 1391 1392static void 1393EntryWorldChanged( 1394 ClientData instanceData) /* Information about widget. */ 1395{ 1396 XGCValues gcValues; 1397 GC gc = None; 1398 unsigned long mask; 1399 Tk_3DBorder border; 1400 XColor *colorPtr; 1401 Entry *entryPtr = (Entry *) instanceData; 1402 1403 entryPtr->avgWidth = Tk_TextWidth(entryPtr->tkfont, "0", 1); 1404 if (entryPtr->avgWidth == 0) { 1405 entryPtr->avgWidth = 1; 1406 } 1407 1408 if (entryPtr->type == TK_SPINBOX) { 1409 /* 1410 * Compute the button width for a spinbox 1411 */ 1412 1413 entryPtr->xWidth = entryPtr->avgWidth + 2 * (1+XPAD); 1414 if (entryPtr->xWidth < 11) { 1415 entryPtr->xWidth = 11; /* we want a min visible size */ 1416 } 1417 } 1418 1419 /* 1420 * Default background and foreground are from the normal state. In a 1421 * disabled state, both of those may be overridden; in the readonly state, 1422 * the background may be overridden. 1423 */ 1424 1425 border = entryPtr->normalBorder; 1426 colorPtr = entryPtr->fgColorPtr; 1427 switch (entryPtr->state) { 1428 case STATE_DISABLED: 1429 if (entryPtr->disabledBorder != NULL) { 1430 border = entryPtr->disabledBorder; 1431 } 1432 if (entryPtr->dfgColorPtr != NULL) { 1433 colorPtr = entryPtr->dfgColorPtr; 1434 } 1435 break; 1436 case STATE_READONLY: 1437 if (entryPtr->readonlyBorder != NULL) { 1438 border = entryPtr->readonlyBorder; 1439 } 1440 break; 1441 } 1442 1443 Tk_SetBackgroundFromBorder(entryPtr->tkwin, border); 1444 gcValues.foreground = colorPtr->pixel; 1445 gcValues.font = Tk_FontId(entryPtr->tkfont); 1446 gcValues.graphics_exposures = False; 1447 mask = GCForeground | GCFont | GCGraphicsExposures; 1448 gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues); 1449 if (entryPtr->textGC != None) { 1450 Tk_FreeGC(entryPtr->display, entryPtr->textGC); 1451 } 1452 entryPtr->textGC = gc; 1453 1454 if (entryPtr->selFgColorPtr != NULL) { 1455 gcValues.foreground = entryPtr->selFgColorPtr->pixel; 1456 } 1457 gcValues.font = Tk_FontId(entryPtr->tkfont); 1458 mask = GCForeground | GCFont; 1459 gc = Tk_GetGC(entryPtr->tkwin, mask, &gcValues); 1460 if (entryPtr->selTextGC != None) { 1461 Tk_FreeGC(entryPtr->display, entryPtr->selTextGC); 1462 } 1463 entryPtr->selTextGC = gc; 1464 1465 /* 1466 * Recompute the window's geometry and arrange for it to be redisplayed. 1467 */ 1468 1469 EntryComputeGeometry(entryPtr); 1470 entryPtr->flags |= UPDATE_SCROLLBAR; 1471 EventuallyRedraw(entryPtr); 1472} 1473 1474#ifndef MAC_OSX_TK 1475/* 1476 *-------------------------------------------------------------- 1477 * 1478 * TkpDrawEntryBorderAndFocus -- 1479 * 1480 * This function redraws the border of an entry widget. It overrides the 1481 * generic border drawing code if the entry widget parameters are such 1482 * that the native widget drawing is a good fit. This version just 1483 * returns 0, so platforms that don't do special native drawing don't 1484 * have to implement it. 1485 * 1486 * Results: 1487 * 1 if it has drawn the border, 0 if not. 1488 * 1489 * Side effects: 1490 * May draw the entry border into pixmap. 1491 * 1492 *-------------------------------------------------------------- 1493 */ 1494 1495int 1496TkpDrawEntryBorderAndFocus( 1497 Entry *entryPtr, 1498 Drawable pixmap, 1499 int isSpinbox) 1500{ 1501 return 0; 1502} 1503 1504/* 1505 *-------------------------------------------------------------- 1506 * 1507 * TkpDrawSpinboxButtons -- 1508 * 1509 * This function redraws the buttons of an spinbox widget. It overrides 1510 * the generic button drawing code if the spinbox widget parameters are 1511 * such that the native widget drawing is a good fit. This version just 1512 * returns 0, so platforms that don't do special native drawing don't 1513 * have to implement it. 1514 * 1515 * Results: 1516 * 1 if it has drawn the border, 0 if not. 1517 * 1518 * Side effects: 1519 * May draw the entry border into pixmap. 1520 * 1521 *-------------------------------------------------------------- 1522 */ 1523 1524int 1525TkpDrawSpinboxButtons( 1526 Spinbox *sbPtr, 1527 Pixmap pixmap) 1528{ 1529 return 0; 1530} 1531#endif /* Not MAC_OSX_TK */ 1532 1533/* 1534 *-------------------------------------------------------------- 1535 * 1536 * DisplayEntry -- 1537 * 1538 * This function redraws the contents of an entry window. 1539 * 1540 * Results: 1541 * None. 1542 * 1543 * Side effects: 1544 * Information appears on the screen. 1545 * 1546 *-------------------------------------------------------------- 1547 */ 1548 1549static void 1550DisplayEntry( 1551 ClientData clientData) /* Information about window. */ 1552{ 1553 Entry *entryPtr = (Entry *) clientData; 1554 Tk_Window tkwin = entryPtr->tkwin; 1555 int baseY, selStartX, selEndX, cursorX; 1556 int showSelection, xBound; 1557 Tk_FontMetrics fm; 1558 Pixmap pixmap; 1559 Tk_3DBorder border; 1560 1561 entryPtr->flags &= ~REDRAW_PENDING; 1562 if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) { 1563 return; 1564 } 1565 1566 Tk_GetFontMetrics(entryPtr->tkfont, &fm); 1567 1568 /* 1569 * Update the scrollbar if that's needed. 1570 */ 1571 1572 if (entryPtr->flags & UPDATE_SCROLLBAR) { 1573 entryPtr->flags &= ~UPDATE_SCROLLBAR; 1574 1575 /* 1576 * Preserve/Release because updating the scrollbar can have the 1577 * side-effect of destroying or unmapping the entry widget. 1578 */ 1579 1580 Tcl_Preserve((ClientData) entryPtr); 1581 EntryUpdateScrollbar(entryPtr); 1582 1583 if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(tkwin)) { 1584 Tcl_Release((ClientData) entryPtr); 1585 return; 1586 } 1587 Tcl_Release((ClientData) entryPtr); 1588 } 1589 1590#ifndef TK_NO_DOUBLE_BUFFERING 1591 /* 1592 * In order to avoid screen flashes, this function redraws the textual 1593 * area of the entry into off-screen memory, then copies it back on-screen 1594 * in a single operation. This means there's no point in time where the 1595 * on-screen image has been cleared. 1596 */ 1597 1598 pixmap = Tk_GetPixmap(entryPtr->display, Tk_WindowId(tkwin), 1599 Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); 1600#else 1601 pixmap = Tk_WindowId(tkwin); 1602#endif /* TK_NO_DOUBLE_BUFFERING */ 1603 1604 /* 1605 * Compute x-coordinate of the pixel just after last visible one, plus 1606 * vertical position of baseline of text. 1607 */ 1608 1609 xBound = Tk_Width(tkwin) - entryPtr->inset - entryPtr->xWidth; 1610 baseY = (Tk_Height(tkwin) + fm.ascent - fm.descent) / 2; 1611 1612 /* 1613 * Hide the selection whenever we don't have the focus, unless we 1614 * always want to show selection. 1615 */ 1616 if (TkpAlwaysShowSelection(entryPtr->tkwin)) { 1617 showSelection = 1; 1618 } else { 1619 showSelection = (entryPtr->flags & GOT_FOCUS); 1620 } 1621 1622 /* 1623 * Draw the background in three layers. From bottom to top the layers are: 1624 * normal background, selection background, and insertion cursor 1625 * background. 1626 */ 1627 1628 if ((entryPtr->state == STATE_DISABLED) && 1629 (entryPtr->disabledBorder != NULL)) { 1630 border = entryPtr->disabledBorder; 1631 } else if ((entryPtr->state == STATE_READONLY) && 1632 (entryPtr->readonlyBorder != NULL)) { 1633 border = entryPtr->readonlyBorder; 1634 } else { 1635 border = entryPtr->normalBorder; 1636 } 1637 Tk_Fill3DRectangle(tkwin, pixmap, border, 1638 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, TK_RELIEF_FLAT); 1639 1640 if (showSelection && (entryPtr->state != STATE_DISABLED) 1641 && (entryPtr->selectLast > entryPtr->leftIndex)) { 1642 if (entryPtr->selectFirst <= entryPtr->leftIndex) { 1643 selStartX = entryPtr->leftX; 1644 } else { 1645 Tk_CharBbox(entryPtr->textLayout, entryPtr->selectFirst, 1646 &selStartX, NULL, NULL, NULL); 1647 selStartX += entryPtr->layoutX; 1648 } 1649 if ((selStartX - entryPtr->selBorderWidth) < xBound) { 1650 Tk_CharBbox(entryPtr->textLayout, entryPtr->selectLast, 1651 &selEndX, NULL, NULL, NULL); 1652 selEndX += entryPtr->layoutX; 1653 Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->selBorder, 1654 selStartX - entryPtr->selBorderWidth, 1655 baseY - fm.ascent - entryPtr->selBorderWidth, 1656 (selEndX - selStartX) + 2*entryPtr->selBorderWidth, 1657 (fm.ascent + fm.descent) + 2*entryPtr->selBorderWidth, 1658 entryPtr->selBorderWidth, 1659#ifndef MAC_OSX_TK 1660 TK_RELIEF_RAISED 1661#else 1662 MAC_OSX_ENTRY_SELECT_RELIEF 1663#endif 1664 ); 1665 } 1666 } 1667 1668 /* 1669 * Draw a special background for the insertion cursor, overriding even the 1670 * selection background. As a special hack to keep the cursor visible when 1671 * the insertion cursor color is the same as the color for selected text 1672 * (e.g., on mono displays), write background in the cursor area (instead 1673 * of nothing) when the cursor isn't on. Otherwise the selection would 1674 * hide the cursor. 1675 */ 1676 1677 if ((entryPtr->state == STATE_NORMAL) && (entryPtr->flags & GOT_FOCUS)) { 1678 Tk_CharBbox(entryPtr->textLayout, entryPtr->insertPos, &cursorX, NULL, 1679 NULL, NULL); 1680 cursorX += entryPtr->layoutX; 1681 cursorX -= (entryPtr->insertWidth)/2; 1682 Tk_SetCaretPos(entryPtr->tkwin, cursorX, baseY - fm.ascent, 1683 fm.ascent + fm.descent); 1684 if (entryPtr->insertPos >= entryPtr->leftIndex && cursorX < xBound) { 1685 if (entryPtr->flags & CURSOR_ON) { 1686 Tk_Fill3DRectangle(tkwin, pixmap, entryPtr->insertBorder, 1687 cursorX, baseY - fm.ascent, entryPtr->insertWidth, 1688 fm.ascent + fm.descent, entryPtr->insertBorderWidth, 1689 TK_RELIEF_RAISED); 1690 } else if (entryPtr->insertBorder == entryPtr->selBorder) { 1691 Tk_Fill3DRectangle(tkwin, pixmap, border, cursorX, 1692 baseY - fm.ascent, entryPtr->insertWidth, 1693 fm.ascent + fm.descent, 0, TK_RELIEF_FLAT); 1694 } 1695 } 1696 } 1697 1698 /* 1699 * Draw the text in two pieces: first the unselected portion, then the 1700 * selected portion on top of it. 1701 */ 1702 1703 Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->textGC, 1704 entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY, 1705 entryPtr->leftIndex, entryPtr->numChars); 1706 1707 if (showSelection && (entryPtr->state != STATE_DISABLED) 1708 && (entryPtr->selTextGC != entryPtr->textGC) 1709 && (entryPtr->selectFirst < entryPtr->selectLast)) { 1710 int selFirst; 1711 1712 if (entryPtr->selectFirst < entryPtr->leftIndex) { 1713 selFirst = entryPtr->leftIndex; 1714 } else { 1715 selFirst = entryPtr->selectFirst; 1716 } 1717 Tk_DrawTextLayout(entryPtr->display, pixmap, entryPtr->selTextGC, 1718 entryPtr->textLayout, entryPtr->layoutX, entryPtr->layoutY, 1719 selFirst, entryPtr->selectLast); 1720 } 1721 1722 if (entryPtr->type == TK_SPINBOX) { 1723 int startx, height, inset, pad, tHeight, xWidth; 1724 Spinbox *sbPtr = (Spinbox *) entryPtr; 1725 1726 /* 1727 * Draw the spin button controls. 1728 */ 1729 1730 if (TkpDrawSpinboxButtons(sbPtr, pixmap) == 0) { 1731 xWidth = entryPtr->xWidth; 1732 pad = XPAD + 1; 1733 inset = entryPtr->inset - XPAD; 1734 startx = Tk_Width(tkwin) - (xWidth + inset); 1735 height = (Tk_Height(tkwin) - 2*inset)/2; 1736#if 0 1737 Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder, 1738 startx, inset, xWidth, height, 1, sbPtr->buRelief); 1739 Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder, 1740 startx, inset+height, xWidth, height, 1, sbPtr->bdRelief); 1741#else 1742 Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder, 1743 startx, inset, xWidth, height, 1, 1744 (sbPtr->selElement == SEL_BUTTONUP) ? 1745 TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); 1746 Tk_Fill3DRectangle(tkwin, pixmap, sbPtr->buttonBorder, 1747 startx, inset+height, xWidth, height, 1, 1748 (sbPtr->selElement == SEL_BUTTONDOWN) ? 1749 TK_RELIEF_SUNKEN : TK_RELIEF_RAISED); 1750#endif 1751 1752 xWidth -= 2*pad; 1753 1754 /* 1755 * Only draw the triangles if we have enough display space 1756 */ 1757 1758 if ((xWidth > 1)) { 1759 XPoint points[3]; 1760 int starty, space, offset; 1761 1762 space = height - 2*pad; 1763 1764 /* 1765 * Ensure width of triangle is odd to guarantee a sharp tip 1766 */ 1767 1768 if (!(xWidth % 2)) { 1769 xWidth++; 1770 } 1771 tHeight = (xWidth + 1) / 2; 1772 if (tHeight > space) { 1773 tHeight = space; 1774 } 1775 space = (space - tHeight) / 2; 1776 startx += pad; 1777 starty = inset + height - pad - space; 1778 offset = (sbPtr->selElement == SEL_BUTTONUP); 1779 1780 /* 1781 * The points are slightly different for the up and down 1782 * arrows because (for *.x), we need to account for a bug in 1783 * the way XFillPolygon draws triangles, and we want to shift 1784 * the arrows differently when allowing for depressed 1785 * behavior. 1786 */ 1787 1788 points[0].x = startx + offset; 1789 points[0].y = starty + (offset ? 0 : -1); 1790 points[1].x = startx + xWidth/2 + offset; 1791 points[1].y = starty - tHeight + (offset ? 0 : -1); 1792 points[2].x = startx + xWidth + offset; 1793 points[2].y = points[0].y; 1794 XFillPolygon(entryPtr->display, pixmap, entryPtr->textGC, 1795 points, 3, Convex, CoordModeOrigin); 1796 1797 starty = inset + height + pad + space; 1798 offset = (sbPtr->selElement == SEL_BUTTONDOWN); 1799 points[0].x = startx + 1 + offset; 1800 points[0].y = starty + (offset ? 1 : 0); 1801 points[1].x = startx + xWidth/2 + offset; 1802 points[1].y = starty + tHeight + (offset ? 0 : -1); 1803 points[2].x = startx - 1 + xWidth + offset; 1804 points[2].y = points[0].y; 1805 XFillPolygon(entryPtr->display, pixmap, entryPtr->textGC, 1806 points, 3, Convex, CoordModeOrigin); 1807 } 1808 } 1809 } 1810 1811 /* 1812 * Draw the border and focus highlight last, so they will overwrite any 1813 * text that extends past the viewable part of the window. 1814 */ 1815 1816 if (!TkpDrawEntryBorderAndFocus(entryPtr, pixmap, 1817 (entryPtr->type == TK_SPINBOX))) { 1818 xBound = entryPtr->highlightWidth; 1819 if (entryPtr->relief != TK_RELIEF_FLAT) { 1820 Tk_Draw3DRectangle(tkwin, pixmap, border, xBound, xBound, 1821 Tk_Width(tkwin) - 2 * xBound, 1822 Tk_Height(tkwin) - 2 * xBound, 1823 entryPtr->borderWidth, entryPtr->relief); 1824 } 1825 if (xBound > 0) { 1826 GC fgGC, bgGC; 1827 1828 bgGC = Tk_GCForColor(entryPtr->highlightBgColorPtr, pixmap); 1829 if (entryPtr->flags & GOT_FOCUS) { 1830 fgGC = Tk_GCForColor(entryPtr->highlightColorPtr, pixmap); 1831 TkpDrawHighlightBorder(tkwin, fgGC, bgGC, xBound, pixmap); 1832 } else { 1833 TkpDrawHighlightBorder(tkwin, bgGC, bgGC, xBound, pixmap); 1834 } 1835 } 1836 } 1837 1838#ifndef TK_NO_DOUBLE_BUFFERING 1839 /* 1840 * Everything's been redisplayed; now copy the pixmap onto the screen and 1841 * free up the pixmap. 1842 */ 1843 1844 XCopyArea(entryPtr->display, pixmap, Tk_WindowId(tkwin), entryPtr->textGC, 1845 0, 0, (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 1846 0, 0); 1847 Tk_FreePixmap(entryPtr->display, pixmap); 1848#endif /* TK_NO_DOUBLE_BUFFERING */ 1849 entryPtr->flags &= ~BORDER_NEEDED; 1850} 1851 1852/* 1853 *---------------------------------------------------------------------- 1854 * 1855 * EntryComputeGeometry -- 1856 * 1857 * This function is invoked to recompute information about where in its 1858 * window an entry's string will be displayed. It also computes the 1859 * requested size for the window. 1860 * 1861 * Results: 1862 * None. 1863 * 1864 * Side effects: 1865 * The leftX and tabOrigin fields are recomputed for entryPtr, and 1866 * leftIndex may be adjusted. Tk_GeometryRequest is called to register 1867 * the desired dimensions for the window. 1868 * 1869 *---------------------------------------------------------------------- 1870 */ 1871 1872static void 1873EntryComputeGeometry( 1874 Entry *entryPtr) /* Widget record for entry. */ 1875{ 1876 int totalLength, overflow, maxOffScreen, rightX; 1877 int height, width, i; 1878 Tk_FontMetrics fm; 1879 char *p; 1880 1881 if (entryPtr->displayString != entryPtr->string) { 1882 ckfree((char *)entryPtr->displayString); 1883 entryPtr->displayString = entryPtr->string; 1884 entryPtr->numDisplayBytes = entryPtr->numBytes; 1885 } 1886 1887 /* 1888 * If we're displaying a special character instead of the value of the 1889 * entry, recompute the displayString. 1890 */ 1891 1892 if (entryPtr->showChar != NULL) { 1893 Tcl_UniChar ch; 1894 char buf[TCL_UTF_MAX]; 1895 int size; 1896 1897 /* 1898 * Normalize the special character so we can safely duplicate it in 1899 * the display string. If we didn't do this, then two malformed 1900 * characters might end up looking like one valid UTF character in the 1901 * resulting string. 1902 */ 1903 1904 Tcl_UtfToUniChar(entryPtr->showChar, &ch); 1905 size = Tcl_UniCharToUtf(ch, buf); 1906 1907 entryPtr->numDisplayBytes = entryPtr->numChars * size; 1908 p = (char *) ckalloc((unsigned) (entryPtr->numDisplayBytes + 1)); 1909 entryPtr->displayString = p; 1910 1911 for (i = entryPtr->numChars; --i >= 0; ) { 1912 p += Tcl_UniCharToUtf(ch, p); 1913 } 1914 *p = '\0'; 1915 } 1916 1917 Tk_FreeTextLayout(entryPtr->textLayout); 1918 entryPtr->textLayout = Tk_ComputeTextLayout(entryPtr->tkfont, 1919 entryPtr->displayString, entryPtr->numChars, 0, 1920 entryPtr->justify, TK_IGNORE_NEWLINES, &totalLength, &height); 1921 1922 entryPtr->layoutY = (Tk_Height(entryPtr->tkwin) - height) / 2; 1923 1924 /* 1925 * Recompute where the leftmost character on the display will be drawn 1926 * (entryPtr->leftX) and adjust leftIndex if necessary so that we don't 1927 * let characters hang off the edge of the window unless the entire window 1928 * is full. 1929 */ 1930 1931 overflow = totalLength - 1932 (Tk_Width(entryPtr->tkwin) - 2*entryPtr->inset - entryPtr->xWidth); 1933 if (overflow <= 0) { 1934 entryPtr->leftIndex = 0; 1935 if (entryPtr->justify == TK_JUSTIFY_LEFT) { 1936 entryPtr->leftX = entryPtr->inset; 1937 } else if (entryPtr->justify == TK_JUSTIFY_RIGHT) { 1938 entryPtr->leftX = Tk_Width(entryPtr->tkwin) - entryPtr->inset 1939 - entryPtr->xWidth - totalLength; 1940 } else { 1941 entryPtr->leftX = (Tk_Width(entryPtr->tkwin) 1942 - entryPtr->xWidth - totalLength)/2; 1943 } 1944 entryPtr->layoutX = entryPtr->leftX; 1945 } else { 1946 /* 1947 * The whole string can't fit in the window. Compute the maximum 1948 * number of characters that may be off-screen to the left without 1949 * leaving empty space on the right of the window, then don't let 1950 * leftIndex be any greater than that. 1951 */ 1952 1953 maxOffScreen = Tk_PointToChar(entryPtr->textLayout, overflow, 0); 1954 Tk_CharBbox(entryPtr->textLayout, maxOffScreen, 1955 &rightX, NULL, NULL, NULL); 1956 if (rightX < overflow) { 1957 maxOffScreen++; 1958 } 1959 if (entryPtr->leftIndex > maxOffScreen) { 1960 entryPtr->leftIndex = maxOffScreen; 1961 } 1962 Tk_CharBbox(entryPtr->textLayout, entryPtr->leftIndex, &rightX, 1963 NULL, NULL, NULL); 1964 entryPtr->leftX = entryPtr->inset; 1965 entryPtr->layoutX = entryPtr->leftX - rightX; 1966 } 1967 1968 Tk_GetFontMetrics(entryPtr->tkfont, &fm); 1969 height = fm.linespace + 2*entryPtr->inset + 2*(YPAD-XPAD); 1970 if (entryPtr->prefWidth > 0) { 1971 width = entryPtr->prefWidth*entryPtr->avgWidth + 2*entryPtr->inset; 1972 } else { 1973 if (totalLength == 0) { 1974 width = entryPtr->avgWidth + 2*entryPtr->inset; 1975 } else { 1976 width = totalLength + 2*entryPtr->inset; 1977 } 1978 } 1979 1980 /* 1981 * Add one extra length for the spin buttons 1982 */ 1983 width += entryPtr->xWidth; 1984 1985 Tk_GeometryRequest(entryPtr->tkwin, width, height); 1986} 1987 1988/* 1989 *---------------------------------------------------------------------- 1990 * 1991 * InsertChars -- 1992 * 1993 * Add new characters to an entry widget. 1994 * 1995 * Results: 1996 * None. 1997 * 1998 * Side effects: 1999 * New information gets added to entryPtr; it will be redisplayed soon, 2000 * but not necessarily immediately. 2001 * 2002 *---------------------------------------------------------------------- 2003 */ 2004 2005static void 2006InsertChars( 2007 Entry *entryPtr, /* Entry that is to get the new elements. */ 2008 int index, /* Add the new elements before this character 2009 * index. */ 2010 char *value) /* New characters to add (NULL-terminated 2011 * string). */ 2012{ 2013 ptrdiff_t byteIndex; 2014 size_t byteCount, newByteCount; 2015 int oldChars, charsAdded; 2016 CONST char *string; 2017 char *newStr; 2018 2019 string = entryPtr->string; 2020 byteIndex = Tcl_UtfAtIndex(string, index) - string; 2021 byteCount = strlen(value); 2022 if (byteCount == 0) { 2023 return; 2024 } 2025 2026 newByteCount = entryPtr->numBytes + byteCount + 1; 2027 newStr = (char *) ckalloc((unsigned) newByteCount); 2028 memcpy(newStr, string, byteIndex); 2029 strcpy(newStr + byteIndex, value); 2030 strcpy(newStr + byteIndex + byteCount, string + byteIndex); 2031 2032 if ((entryPtr->validate == VALIDATE_KEY || 2033 entryPtr->validate == VALIDATE_ALL) && 2034 EntryValidateChange(entryPtr, value, newStr, index, 2035 VALIDATE_INSERT) != TCL_OK) { 2036 ckfree(newStr); 2037 return; 2038 } 2039 2040 ckfree((char *)string); 2041 entryPtr->string = newStr; 2042 2043 /* 2044 * The following construction is used because inserting improperly formed 2045 * UTF-8 sequences between other improperly formed UTF-8 sequences could 2046 * result in actually forming valid UTF-8 sequences; the number of 2047 * characters added may not be Tcl_NumUtfChars(string, -1), because of 2048 * context. The actual number of characters added is how many characters 2049 * are in the string now minus the number that used to be there. 2050 */ 2051 2052 oldChars = entryPtr->numChars; 2053 entryPtr->numChars = Tcl_NumUtfChars(newStr, -1); 2054 charsAdded = entryPtr->numChars - oldChars; 2055 entryPtr->numBytes += byteCount; 2056 2057 if (entryPtr->displayString == string) { 2058 entryPtr->displayString = newStr; 2059 entryPtr->numDisplayBytes = entryPtr->numBytes; 2060 } 2061 2062 /* 2063 * Inserting characters invalidates all indexes into the string. Touch up 2064 * the indexes so that they still refer to the same characters (at new 2065 * positions). When updating the selection end-points, don't include the 2066 * new text in the selection unless it was completely surrounded by the 2067 * selection. 2068 */ 2069 2070 if (entryPtr->selectFirst >= index) { 2071 entryPtr->selectFirst += charsAdded; 2072 } 2073 if (entryPtr->selectLast > index) { 2074 entryPtr->selectLast += charsAdded; 2075 } 2076 if ((entryPtr->selectAnchor > index) || (entryPtr->selectFirst >= index)) { 2077 entryPtr->selectAnchor += charsAdded; 2078 } 2079 if (entryPtr->leftIndex > index) { 2080 entryPtr->leftIndex += charsAdded; 2081 } 2082 if (entryPtr->insertPos >= index) { 2083 entryPtr->insertPos += charsAdded; 2084 } 2085 EntryValueChanged(entryPtr, NULL); 2086} 2087 2088/* 2089 *---------------------------------------------------------------------- 2090 * 2091 * DeleteChars -- 2092 * 2093 * Remove one or more characters from an entry widget. 2094 * 2095 * Results: 2096 * None. 2097 * 2098 * Side effects: 2099 * Memory gets freed, the entry gets modified and (eventually) 2100 * redisplayed. 2101 * 2102 *---------------------------------------------------------------------- 2103 */ 2104 2105static void 2106DeleteChars( 2107 Entry *entryPtr, /* Entry widget to modify. */ 2108 int index, /* Index of first character to delete. */ 2109 int count) /* How many characters to delete. */ 2110{ 2111 int byteIndex, byteCount, newByteCount; 2112 CONST char *string; 2113 char *newStr, *toDelete; 2114 2115 if ((index + count) > entryPtr->numChars) { 2116 count = entryPtr->numChars - index; 2117 } 2118 if (count <= 0) { 2119 return; 2120 } 2121 2122 string = entryPtr->string; 2123 byteIndex = Tcl_UtfAtIndex(string, index) - string; 2124 byteCount = Tcl_UtfAtIndex(string + byteIndex, count) - (string+byteIndex); 2125 2126 newByteCount = entryPtr->numBytes + 1 - byteCount; 2127 newStr = (char *) ckalloc((unsigned) newByteCount); 2128 memcpy(newStr, string, (size_t) byteIndex); 2129 strcpy(newStr + byteIndex, string + byteIndex + byteCount); 2130 2131 toDelete = (char *) ckalloc((unsigned) (byteCount + 1)); 2132 memcpy(toDelete, string + byteIndex, (size_t) byteCount); 2133 toDelete[byteCount] = '\0'; 2134 2135 if ((entryPtr->validate == VALIDATE_KEY || 2136 entryPtr->validate == VALIDATE_ALL) && 2137 EntryValidateChange(entryPtr, toDelete, newStr, index, 2138 VALIDATE_DELETE) != TCL_OK) { 2139 ckfree(newStr); 2140 ckfree(toDelete); 2141 return; 2142 } 2143 2144 ckfree(toDelete); 2145 ckfree((char *)entryPtr->string); 2146 entryPtr->string = newStr; 2147 entryPtr->numChars -= count; 2148 entryPtr->numBytes -= byteCount; 2149 2150 if (entryPtr->displayString == string) { 2151 entryPtr->displayString = newStr; 2152 entryPtr->numDisplayBytes = entryPtr->numBytes; 2153 } 2154 2155 /* 2156 * Deleting characters results in the remaining characters being 2157 * renumbered. Update the various indexes into the string to reflect this 2158 * change. 2159 */ 2160 2161 if (entryPtr->selectFirst >= index) { 2162 if (entryPtr->selectFirst >= (index + count)) { 2163 entryPtr->selectFirst -= count; 2164 } else { 2165 entryPtr->selectFirst = index; 2166 } 2167 } 2168 if (entryPtr->selectLast >= index) { 2169 if (entryPtr->selectLast >= (index + count)) { 2170 entryPtr->selectLast -= count; 2171 } else { 2172 entryPtr->selectLast = index; 2173 } 2174 } 2175 if (entryPtr->selectLast <= entryPtr->selectFirst) { 2176 entryPtr->selectFirst = -1; 2177 entryPtr->selectLast = -1; 2178 } 2179 if (entryPtr->selectAnchor >= index) { 2180 if (entryPtr->selectAnchor >= (index+count)) { 2181 entryPtr->selectAnchor -= count; 2182 } else { 2183 entryPtr->selectAnchor = index; 2184 } 2185 } 2186 if (entryPtr->leftIndex > index) { 2187 if (entryPtr->leftIndex >= (index + count)) { 2188 entryPtr->leftIndex -= count; 2189 } else { 2190 entryPtr->leftIndex = index; 2191 } 2192 } 2193 if (entryPtr->insertPos >= index) { 2194 if (entryPtr->insertPos >= (index + count)) { 2195 entryPtr->insertPos -= count; 2196 } else { 2197 entryPtr->insertPos = index; 2198 } 2199 } 2200 EntryValueChanged(entryPtr, NULL); 2201} 2202 2203/* 2204 *---------------------------------------------------------------------- 2205 * 2206 * EntryValueChanged -- 2207 * 2208 * This function is invoked when characters are inserted into an entry or 2209 * deleted from it. It updates the entry's associated variable, if there 2210 * is one, and does other bookkeeping such as arranging for redisplay. 2211 * 2212 * Results: 2213 * None. 2214 * 2215 * Side effects: 2216 * None. 2217 * 2218 *---------------------------------------------------------------------- 2219 */ 2220 2221static void 2222EntryValueChanged( 2223 Entry *entryPtr, /* Entry whose value just changed. */ 2224 CONST char *newValue) /* If this value is not NULL, we first force 2225 * the value of the entry to this. */ 2226{ 2227 if (newValue != NULL) { 2228 EntrySetValue(entryPtr, newValue); 2229 } 2230 2231 if (entryPtr->textVarName == NULL) { 2232 newValue = NULL; 2233 } else { 2234 newValue = Tcl_SetVar(entryPtr->interp, entryPtr->textVarName, 2235 entryPtr->string, TCL_GLOBAL_ONLY); 2236 } 2237 2238 if ((newValue != NULL) && (strcmp(newValue, entryPtr->string) != 0)) { 2239 /* 2240 * The value of the variable is different than what we asked for. 2241 * This means that a trace on the variable modified it. In this case 2242 * our trace function wasn't invoked since the modification came while 2243 * a trace was already active on the variable. So, update our value to 2244 * reflect the variable's latest value. 2245 */ 2246 2247 EntrySetValue(entryPtr, newValue); 2248 } else { 2249 /* 2250 * Arrange for redisplay. 2251 */ 2252 2253 entryPtr->flags |= UPDATE_SCROLLBAR; 2254 EntryComputeGeometry(entryPtr); 2255 EventuallyRedraw(entryPtr); 2256 } 2257} 2258 2259/* 2260 *---------------------------------------------------------------------- 2261 * 2262 * EntrySetValue -- 2263 * 2264 * Replace the contents of a text entry with a given value. This function 2265 * is invoked when updating the entry from the entry's associated 2266 * variable. 2267 * 2268 * Results: 2269 * None. 2270 * 2271 * Side effects: 2272 * The string displayed in the entry will change. The selection, 2273 * insertion point, and view may have to be adjusted to keep them within 2274 * the bounds of the new string. Note: this function does *not* update 2275 * the entry's associated variable, since that could result in an 2276 * infinite loop. 2277 * 2278 *---------------------------------------------------------------------- 2279 */ 2280 2281static void 2282EntrySetValue( 2283 Entry *entryPtr, /* Entry whose value is to be changed. */ 2284 CONST char *value) /* New text to display in entry. */ 2285{ 2286 CONST char *oldSource; 2287 int valueLen, malloced = 0; 2288 2289 if (strcmp(value, entryPtr->string) == 0) { 2290 return; 2291 } 2292 valueLen = strlen(value); 2293 2294 if (entryPtr->flags & VALIDATE_VAR) { 2295 entryPtr->flags |= VALIDATE_ABORT; 2296 } else { 2297 /* 2298 * If we validate, we create a copy of the value, as it may point to 2299 * volatile memory, like the value of the -textvar which may get freed 2300 * during validation 2301 */ 2302 2303 char *tmp = (char *) ckalloc((unsigned) (valueLen + 1)); 2304 2305 strcpy(tmp, value); 2306 value = tmp; 2307 malloced = 1; 2308 2309 entryPtr->flags |= VALIDATE_VAR; 2310 (void) EntryValidateChange(entryPtr, NULL, value, -1, 2311 VALIDATE_FORCED); 2312 entryPtr->flags &= ~VALIDATE_VAR; 2313 2314 /* 2315 * If VALIDATE_ABORT has been set, then this operation should be 2316 * aborted because the validatecommand did something else instead 2317 */ 2318 2319 if (entryPtr->flags & VALIDATE_ABORT) { 2320 entryPtr->flags &= ~VALIDATE_ABORT; 2321 ckfree((char *)value); 2322 return; 2323 } 2324 } 2325 2326 oldSource = entryPtr->string; 2327 ckfree((char *)entryPtr->string); 2328 2329 if (malloced) { 2330 entryPtr->string = value; 2331 } else { 2332 char *tmp = (char *) ckalloc((unsigned) (valueLen + 1)); 2333 2334 strcpy(tmp, value); 2335 entryPtr->string = tmp; 2336 } 2337 entryPtr->numBytes = valueLen; 2338 entryPtr->numChars = Tcl_NumUtfChars(value, valueLen); 2339 2340 if (entryPtr->displayString == oldSource) { 2341 entryPtr->displayString = entryPtr->string; 2342 entryPtr->numDisplayBytes = entryPtr->numBytes; 2343 } 2344 2345 if (entryPtr->selectFirst >= 0) { 2346 if (entryPtr->selectFirst >= entryPtr->numChars) { 2347 entryPtr->selectFirst = -1; 2348 entryPtr->selectLast = -1; 2349 } else if (entryPtr->selectLast > entryPtr->numChars) { 2350 entryPtr->selectLast = entryPtr->numChars; 2351 } 2352 } 2353 if (entryPtr->leftIndex >= entryPtr->numChars) { 2354 if (entryPtr->numChars > 0) { 2355 entryPtr->leftIndex = entryPtr->numChars - 1; 2356 } else { 2357 entryPtr->leftIndex = 0; 2358 } 2359 } 2360 if (entryPtr->insertPos > entryPtr->numChars) { 2361 entryPtr->insertPos = entryPtr->numChars; 2362 } 2363 2364 entryPtr->flags |= UPDATE_SCROLLBAR; 2365 EntryComputeGeometry(entryPtr); 2366 EventuallyRedraw(entryPtr); 2367} 2368 2369/* 2370 *-------------------------------------------------------------- 2371 * 2372 * EntryEventProc -- 2373 * 2374 * This function is invoked by the Tk dispatcher for various events on 2375 * entries. 2376 * 2377 * Results: 2378 * None. 2379 * 2380 * Side effects: 2381 * When the window gets deleted, internal structures get cleaned up. 2382 * When it gets exposed, it is redisplayed. 2383 * 2384 *-------------------------------------------------------------- 2385 */ 2386 2387static void 2388EntryEventProc( 2389 ClientData clientData, /* Information about window. */ 2390 XEvent *eventPtr) /* Information about event. */ 2391{ 2392 Entry *entryPtr = (Entry *) clientData; 2393 2394 if ((entryPtr->type == TK_SPINBOX) && (eventPtr->type == MotionNotify)) { 2395 Spinbox *sbPtr = (Spinbox *) clientData; 2396 int elem; 2397 2398 elem = GetSpinboxElement(sbPtr, eventPtr->xmotion.x, 2399 eventPtr->xmotion.y); 2400 if (elem != sbPtr->curElement) { 2401 Tk_Cursor cursor; 2402 2403 sbPtr->curElement = elem; 2404 if (elem == SEL_ENTRY) { 2405 cursor = entryPtr->cursor; 2406 } else if ((elem == SEL_BUTTONDOWN) || (elem == SEL_BUTTONUP)) { 2407 cursor = sbPtr->bCursor; 2408 } else { 2409 cursor = None; 2410 } 2411 if (cursor != None) { 2412 Tk_DefineCursor(entryPtr->tkwin, cursor); 2413 } else { 2414 Tk_UndefineCursor(entryPtr->tkwin); 2415 } 2416 } 2417 return; 2418 } 2419 2420 switch (eventPtr->type) { 2421 case Expose: 2422 EventuallyRedraw(entryPtr); 2423 entryPtr->flags |= BORDER_NEEDED; 2424 break; 2425 case DestroyNotify: 2426 if (!(entryPtr->flags & ENTRY_DELETED)) { 2427 entryPtr->flags |= (ENTRY_DELETED | VALIDATE_ABORT); 2428 Tcl_DeleteCommandFromToken(entryPtr->interp, entryPtr->widgetCmd); 2429 if (entryPtr->flags & REDRAW_PENDING) { 2430 Tcl_CancelIdleCall(DisplayEntry, clientData); 2431 } 2432 Tcl_EventuallyFree(clientData, DestroyEntry); 2433 } 2434 break; 2435 case ConfigureNotify: 2436 Tcl_Preserve((ClientData) entryPtr); 2437 entryPtr->flags |= UPDATE_SCROLLBAR; 2438 EntryComputeGeometry(entryPtr); 2439 EventuallyRedraw(entryPtr); 2440 Tcl_Release((ClientData) entryPtr); 2441 break; 2442 case FocusIn: 2443 case FocusOut: 2444 if (eventPtr->xfocus.detail != NotifyInferior) { 2445 EntryFocusProc(entryPtr, (eventPtr->type == FocusIn)); 2446 } 2447 break; 2448 } 2449} 2450 2451/* 2452 *---------------------------------------------------------------------- 2453 * 2454 * EntryCmdDeletedProc -- 2455 * 2456 * This function is invoked when a widget command is deleted. If the 2457 * widget isn't already in the process of being destroyed, this command 2458 * destroys it. 2459 * 2460 * Results: 2461 * None. 2462 * 2463 * Side effects: 2464 * The widget is destroyed. 2465 * 2466 *---------------------------------------------------------------------- 2467 */ 2468 2469static void 2470EntryCmdDeletedProc( 2471 ClientData clientData) /* Pointer to widget record for widget. */ 2472{ 2473 Entry *entryPtr = (Entry *) clientData; 2474 2475 /* 2476 * This function could be invoked either because the window was destroyed 2477 * and the command was then deleted (in which case tkwin is NULL) or 2478 * because the command was deleted, and then this function destroys the 2479 * widget. 2480 */ 2481 2482 if (!(entryPtr->flags & ENTRY_DELETED)) { 2483 Tk_DestroyWindow(entryPtr->tkwin); 2484 } 2485} 2486 2487/* 2488 *--------------------------------------------------------------------------- 2489 * 2490 * GetEntryIndex -- 2491 * 2492 * Parse an index into an entry and return either its value or an error. 2493 * 2494 * Results: 2495 * A standard Tcl result. If all went well, then *indexPtr is filled in 2496 * with the character index (into entryPtr) corresponding to string. The 2497 * index value is guaranteed to lie between 0 and the number of 2498 * characters in the string, inclusive. If an error occurs then an error 2499 * message is left in the interp's result. 2500 * 2501 * Side effects: 2502 * None. 2503 * 2504 *--------------------------------------------------------------------------- 2505 */ 2506 2507static int 2508GetEntryIndex( 2509 Tcl_Interp *interp, /* For error messages. */ 2510 Entry *entryPtr, /* Entry for which the index is being 2511 * specified. */ 2512 char *string, /* Specifies character in entryPtr. */ 2513 int *indexPtr) /* Where to store converted character index */ 2514{ 2515 size_t length; 2516 2517 length = strlen(string); 2518 2519 if (string[0] == 'a') { 2520 if (strncmp(string, "anchor", length) == 0) { 2521 *indexPtr = entryPtr->selectAnchor; 2522 } else { 2523 badIndex: 2524 2525 /* 2526 * Some of the paths here leave messages in the interp's result, 2527 * so we have to clear it out before storing our own message. 2528 */ 2529 2530 Tcl_SetResult(interp, NULL, TCL_STATIC); 2531 Tcl_AppendResult(interp, "bad ", 2532 (entryPtr->type == TK_ENTRY) ? "entry" : "spinbox", 2533 " index \"", string, "\"", NULL); 2534 return TCL_ERROR; 2535 } 2536 } else if (string[0] == 'e') { 2537 if (strncmp(string, "end", length) == 0) { 2538 *indexPtr = entryPtr->numChars; 2539 } else { 2540 goto badIndex; 2541 } 2542 } else if (string[0] == 'i') { 2543 if (strncmp(string, "insert", length) == 0) { 2544 *indexPtr = entryPtr->insertPos; 2545 } else { 2546 goto badIndex; 2547 } 2548 } else if (string[0] == 's') { 2549 if (entryPtr->selectFirst < 0) { 2550 Tcl_SetResult(interp, NULL, TCL_STATIC); 2551 Tcl_AppendResult(interp, "selection isn't in widget ", 2552 Tk_PathName(entryPtr->tkwin), NULL); 2553 return TCL_ERROR; 2554 } 2555 if (length < 5) { 2556 goto badIndex; 2557 } 2558 if (strncmp(string, "sel.first", length) == 0) { 2559 *indexPtr = entryPtr->selectFirst; 2560 } else if (strncmp(string, "sel.last", length) == 0) { 2561 *indexPtr = entryPtr->selectLast; 2562 } else { 2563 goto badIndex; 2564 } 2565 } else if (string[0] == '@') { 2566 int x, roundUp, maxWidth; 2567 2568 if (Tcl_GetInt(interp, string + 1, &x) != TCL_OK) { 2569 goto badIndex; 2570 } 2571 if (x < entryPtr->inset) { 2572 x = entryPtr->inset; 2573 } 2574 roundUp = 0; 2575 maxWidth = Tk_Width(entryPtr->tkwin) - entryPtr->inset 2576 - entryPtr->xWidth - 1; 2577 if (x > maxWidth) { 2578 x = maxWidth; 2579 roundUp = 1; 2580 } 2581 *indexPtr = Tk_PointToChar(entryPtr->textLayout, 2582 x - entryPtr->layoutX, 0); 2583 2584 /* 2585 * Special trick: if the x-position was off-screen to the right, round 2586 * the index up to refer to the character just after the last visible 2587 * one on the screen. This is needed to enable the last character to 2588 * be selected, for example. 2589 */ 2590 2591 if (roundUp && (*indexPtr < entryPtr->numChars)) { 2592 *indexPtr += 1; 2593 } 2594 } else { 2595 if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) { 2596 goto badIndex; 2597 } 2598 if (*indexPtr < 0){ 2599 *indexPtr = 0; 2600 } else if (*indexPtr > entryPtr->numChars) { 2601 *indexPtr = entryPtr->numChars; 2602 } 2603 } 2604 return TCL_OK; 2605} 2606 2607/* 2608 *---------------------------------------------------------------------- 2609 * 2610 * EntryScanTo -- 2611 * 2612 * Given a y-coordinate (presumably of the curent mouse location) drag 2613 * the view in the window to implement the scan operation. 2614 * 2615 * Results: 2616 * None. 2617 * 2618 * Side effects: 2619 * The view in the window may change. 2620 * 2621 *---------------------------------------------------------------------- 2622 */ 2623 2624static void 2625EntryScanTo( 2626 Entry *entryPtr, /* Information about widget. */ 2627 int x) /* X-coordinate to use for scan operation. */ 2628{ 2629 int newLeftIndex; 2630 2631 /* 2632 * Compute new leftIndex for entry by amplifying the difference between 2633 * the current position and the place where the scan started (the "mark" 2634 * position). If we run off the left or right side of the entry, then 2635 * reset the mark point so that the current position continues to 2636 * correspond to the edge of the window. This means that the picture will 2637 * start dragging as soon as the mouse reverses direction (without this 2638 * reset, might have to slide mouse a long ways back before the picture 2639 * starts moving again). 2640 */ 2641 2642 newLeftIndex = entryPtr->scanMarkIndex 2643 - (10 * (x - entryPtr->scanMarkX)) / entryPtr->avgWidth; 2644 if (newLeftIndex >= entryPtr->numChars) { 2645 newLeftIndex = entryPtr->scanMarkIndex = entryPtr->numChars - 1; 2646 entryPtr->scanMarkX = x; 2647 } 2648 if (newLeftIndex < 0) { 2649 newLeftIndex = entryPtr->scanMarkIndex = 0; 2650 entryPtr->scanMarkX = x; 2651 } 2652 2653 if (newLeftIndex != entryPtr->leftIndex) { 2654 entryPtr->leftIndex = newLeftIndex; 2655 entryPtr->flags |= UPDATE_SCROLLBAR; 2656 EntryComputeGeometry(entryPtr); 2657 if (newLeftIndex != entryPtr->leftIndex) { 2658 entryPtr->scanMarkIndex = entryPtr->leftIndex; 2659 entryPtr->scanMarkX = x; 2660 } 2661 EventuallyRedraw(entryPtr); 2662 } 2663} 2664 2665/* 2666 *---------------------------------------------------------------------- 2667 * 2668 * EntrySelectTo -- 2669 * 2670 * Modify the selection by moving its un-anchored end. This could make 2671 * the selection either larger or smaller. 2672 * 2673 * Results: 2674 * None. 2675 * 2676 * Side effects: 2677 * The selection changes. 2678 * 2679 *---------------------------------------------------------------------- 2680 */ 2681 2682static void 2683EntrySelectTo( 2684 Entry *entryPtr, /* Information about widget. */ 2685 int index) /* Character index of element that is to 2686 * become the "other" end of the selection. */ 2687{ 2688 int newFirst, newLast; 2689 2690 /* 2691 * Grab the selection if we don't own it already. 2692 */ 2693 2694 if (!(entryPtr->flags & GOT_SELECTION) && (entryPtr->exportSelection)) { 2695 Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, EntryLostSelection, 2696 (ClientData) entryPtr); 2697 entryPtr->flags |= GOT_SELECTION; 2698 } 2699 2700 /* 2701 * Pick new starting and ending points for the selection. 2702 */ 2703 2704 if (entryPtr->selectAnchor > entryPtr->numChars) { 2705 entryPtr->selectAnchor = entryPtr->numChars; 2706 } 2707 if (entryPtr->selectAnchor <= index) { 2708 newFirst = entryPtr->selectAnchor; 2709 newLast = index; 2710 } else { 2711 newFirst = index; 2712 newLast = entryPtr->selectAnchor; 2713 if (newLast < 0) { 2714 newFirst = newLast = -1; 2715 } 2716 } 2717 if ((entryPtr->selectFirst == newFirst) 2718 && (entryPtr->selectLast == newLast)) { 2719 return; 2720 } 2721 entryPtr->selectFirst = newFirst; 2722 entryPtr->selectLast = newLast; 2723 EventuallyRedraw(entryPtr); 2724} 2725 2726/* 2727 *---------------------------------------------------------------------- 2728 * 2729 * EntryFetchSelection -- 2730 * 2731 * This function is called back by Tk when the selection is requested by 2732 * someone. It returns part or all of the selection in a buffer provided 2733 * by the caller. 2734 * 2735 * Results: 2736 * The return value is the number of non-NULL bytes stored at buffer. 2737 * Buffer is filled (or partially filled) with a NULL-terminated string 2738 * containing part or all of the selection, as given by offset and 2739 * maxBytes. 2740 * 2741 * Side effects: 2742 * None. 2743 * 2744 *---------------------------------------------------------------------- 2745 */ 2746 2747static int 2748EntryFetchSelection( 2749 ClientData clientData, /* Information about entry widget. */ 2750 int offset, /* Byte offset within selection of first 2751 * character to be returned. */ 2752 char *buffer, /* Location in which to place selection. */ 2753 int maxBytes) /* Maximum number of bytes to place at buffer, 2754 * not including terminating NUL character. */ 2755{ 2756 Entry *entryPtr = (Entry *) clientData; 2757 int byteCount; 2758 CONST char *string; 2759 CONST char *selStart, *selEnd; 2760 2761 if ((entryPtr->selectFirst < 0) || !(entryPtr->exportSelection)) { 2762 return -1; 2763 } 2764 string = entryPtr->displayString; 2765 selStart = Tcl_UtfAtIndex(string, entryPtr->selectFirst); 2766 selEnd = Tcl_UtfAtIndex(selStart, 2767 entryPtr->selectLast - entryPtr->selectFirst); 2768 byteCount = selEnd - selStart - offset; 2769 if (byteCount > maxBytes) { 2770 byteCount = maxBytes; 2771 } 2772 if (byteCount <= 0) { 2773 return 0; 2774 } 2775 memcpy(buffer, selStart + offset, (size_t) byteCount); 2776 buffer[byteCount] = '\0'; 2777 return byteCount; 2778} 2779 2780/* 2781 *---------------------------------------------------------------------- 2782 * 2783 * EntryLostSelection -- 2784 * 2785 * This function is called back by Tk when the selection is grabbed away 2786 * from an entry widget. 2787 * 2788 * Results: 2789 * None. 2790 * 2791 * Side effects: 2792 * The existing selection is unhighlighted, and the window is marked as 2793 * not containing a selection. 2794 * 2795 *---------------------------------------------------------------------- 2796 */ 2797 2798static void 2799EntryLostSelection( 2800 ClientData clientData) /* Information about entry widget. */ 2801{ 2802 Entry *entryPtr = (Entry *) clientData; 2803 2804 entryPtr->flags &= ~GOT_SELECTION; 2805 2806 /* 2807 * On Windows and Mac systems, we want to remember the selection for the 2808 * next time the focus enters the window. On Unix, we need to clear the 2809 * selection since it is always visible. 2810 * This is controlled by ::tk::AlwaysShowSelection. 2811 */ 2812 2813 if (TkpAlwaysShowSelection(entryPtr->tkwin) 2814 && (entryPtr->selectFirst >= 0) && entryPtr->exportSelection) { 2815 entryPtr->selectFirst = -1; 2816 entryPtr->selectLast = -1; 2817 EventuallyRedraw(entryPtr); 2818 } 2819} 2820 2821/* 2822 *---------------------------------------------------------------------- 2823 * 2824 * EventuallyRedraw -- 2825 * 2826 * Ensure that an entry is eventually redrawn on the display. 2827 * 2828 * Results: 2829 * None. 2830 * 2831 * Side effects: 2832 * Information gets redisplayed. Right now we don't do selective 2833 * redisplays: the whole window will be redrawn. This doesn't seem to 2834 * hurt performance noticeably, but if it does then this could be 2835 * changed. 2836 * 2837 *---------------------------------------------------------------------- 2838 */ 2839 2840static void 2841EventuallyRedraw( 2842 Entry *entryPtr) /* Information about widget. */ 2843{ 2844 if ((entryPtr->flags & ENTRY_DELETED) || !Tk_IsMapped(entryPtr->tkwin)) { 2845 return; 2846 } 2847 2848 /* 2849 * Right now we don't do selective redisplays: the whole window will be 2850 * redrawn. This doesn't seem to hurt performance noticeably, but if it 2851 * does then this could be changed. 2852 */ 2853 2854 if (!(entryPtr->flags & REDRAW_PENDING)) { 2855 entryPtr->flags |= REDRAW_PENDING; 2856 Tcl_DoWhenIdle(DisplayEntry, (ClientData) entryPtr); 2857 } 2858} 2859 2860/* 2861 *---------------------------------------------------------------------- 2862 * 2863 * EntryVisibleRange -- 2864 * 2865 * Return information about the range of the entry that is currently 2866 * visible. 2867 * 2868 * Results: 2869 * *firstPtr and *lastPtr are modified to hold fractions between 0 and 1 2870 * identifying the range of characters visible in the entry. 2871 * 2872 * Side effects: 2873 * None. 2874 * 2875 *---------------------------------------------------------------------- 2876 */ 2877 2878static void 2879EntryVisibleRange( 2880 Entry *entryPtr, /* Information about widget. */ 2881 double *firstPtr, /* Return position of first visible character 2882 * in widget. */ 2883 double *lastPtr) /* Return position of char just after last 2884 * visible one. */ 2885{ 2886 int charsInWindow; 2887 2888 if (entryPtr->numChars == 0) { 2889 *firstPtr = 0.0; 2890 *lastPtr = 1.0; 2891 } else { 2892 charsInWindow = Tk_PointToChar(entryPtr->textLayout, 2893 Tk_Width(entryPtr->tkwin) - entryPtr->inset 2894 - entryPtr->xWidth - entryPtr->layoutX - 1, 0); 2895 if (charsInWindow < entryPtr->numChars) { 2896 charsInWindow++; 2897 } 2898 charsInWindow -= entryPtr->leftIndex; 2899 if (charsInWindow == 0) { 2900 charsInWindow = 1; 2901 } 2902 2903 *firstPtr = (double) entryPtr->leftIndex / entryPtr->numChars; 2904 *lastPtr = (double) (entryPtr->leftIndex + charsInWindow) 2905 / entryPtr->numChars; 2906 } 2907} 2908 2909/* 2910 *---------------------------------------------------------------------- 2911 * 2912 * EntryUpdateScrollbar -- 2913 * 2914 * This function is invoked whenever information has changed in an entry 2915 * in a way that would invalidate a scrollbar display. If there is an 2916 * associated scrollbar, then this function updates it by invoking a Tcl 2917 * command. 2918 * 2919 * Results: 2920 * None. 2921 * 2922 * Side effects: 2923 * A Tcl command is invoked, and an additional command may be 2924 * invoked to process errors in the command. 2925 * 2926 *---------------------------------------------------------------------- 2927 */ 2928 2929static void 2930EntryUpdateScrollbar( 2931 Entry *entryPtr) /* Information about widget. */ 2932{ 2933 char firstStr[TCL_DOUBLE_SPACE], lastStr[TCL_DOUBLE_SPACE]; 2934 int code; 2935 double first, last; 2936 Tcl_Interp *interp; 2937 2938 if (entryPtr->scrollCmd == NULL) { 2939 return; 2940 } 2941 2942 interp = entryPtr->interp; 2943 Tcl_Preserve((ClientData) interp); 2944 EntryVisibleRange(entryPtr, &first, &last); 2945 Tcl_PrintDouble(NULL, first, firstStr); 2946 Tcl_PrintDouble(NULL, last, lastStr); 2947 code = Tcl_VarEval(interp, entryPtr->scrollCmd, " ", firstStr, " ", 2948 lastStr, NULL); 2949 if (code != TCL_OK) { 2950 Tcl_AddErrorInfo(interp, 2951 "\n (horizontal scrolling command executed by "); 2952 Tcl_AddErrorInfo(interp, Tk_PathName(entryPtr->tkwin)); 2953 Tcl_AddErrorInfo(interp, ")"); 2954 Tcl_BackgroundError(interp); 2955 } 2956 Tcl_SetResult(interp, NULL, TCL_STATIC); 2957 Tcl_Release((ClientData) interp); 2958} 2959 2960/* 2961 *---------------------------------------------------------------------- 2962 * 2963 * EntryBlinkProc -- 2964 * 2965 * This function is called as a timer handler to blink the insertion 2966 * cursor off and on. 2967 * 2968 * Results: 2969 * None. 2970 * 2971 * Side effects: 2972 * The cursor gets turned on or off, redisplay gets invoked, and this 2973 * function reschedules itself. 2974 * 2975 *---------------------------------------------------------------------- 2976 */ 2977 2978static void 2979EntryBlinkProc( 2980 ClientData clientData) /* Pointer to record describing entry. */ 2981{ 2982 Entry *entryPtr = (Entry *) clientData; 2983 2984 if ((entryPtr->state == STATE_DISABLED) || 2985 (entryPtr->state == STATE_READONLY) || 2986 !(entryPtr->flags & GOT_FOCUS) || (entryPtr->insertOffTime == 0)) { 2987 return; 2988 } 2989 if (entryPtr->flags & CURSOR_ON) { 2990 entryPtr->flags &= ~CURSOR_ON; 2991 entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 2992 entryPtr->insertOffTime, EntryBlinkProc, (ClientData) entryPtr); 2993 } else { 2994 entryPtr->flags |= CURSOR_ON; 2995 entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 2996 entryPtr->insertOnTime, EntryBlinkProc, (ClientData) entryPtr); 2997 } 2998 EventuallyRedraw(entryPtr); 2999} 3000 3001/* 3002 *---------------------------------------------------------------------- 3003 * 3004 * EntryFocusProc -- 3005 * 3006 * This function is called whenever the entry gets or loses the input 3007 * focus. It's also called whenever the window is reconfigured while it 3008 * has the focus. 3009 * 3010 * Results: 3011 * None. 3012 * 3013 * Side effects: 3014 * The cursor gets turned on or off. 3015 * 3016 *---------------------------------------------------------------------- 3017 */ 3018 3019static void 3020EntryFocusProc( 3021 Entry *entryPtr, /* Entry that got or lost focus. */ 3022 int gotFocus) /* 1 means window is getting focus, 0 means 3023 * it's losing it. */ 3024{ 3025 Tcl_DeleteTimerHandler(entryPtr->insertBlinkHandler); 3026 if (gotFocus) { 3027 entryPtr->flags |= GOT_FOCUS | CURSOR_ON; 3028 if (entryPtr->insertOffTime != 0) { 3029 entryPtr->insertBlinkHandler = Tcl_CreateTimerHandler( 3030 entryPtr->insertOnTime, EntryBlinkProc, 3031 (ClientData) entryPtr); 3032 } 3033 if (entryPtr->validate == VALIDATE_ALL || 3034 entryPtr->validate == VALIDATE_FOCUS || 3035 entryPtr->validate == VALIDATE_FOCUSIN) { 3036 EntryValidateChange(entryPtr, NULL, 3037 entryPtr->string, -1, VALIDATE_FOCUSIN); 3038 } 3039 } else { 3040 entryPtr->flags &= ~(GOT_FOCUS | CURSOR_ON); 3041 entryPtr->insertBlinkHandler = (Tcl_TimerToken) NULL; 3042 if (entryPtr->validate == VALIDATE_ALL || 3043 entryPtr->validate == VALIDATE_FOCUS || 3044 entryPtr->validate == VALIDATE_FOCUSOUT) { 3045 EntryValidateChange(entryPtr, NULL, 3046 entryPtr->string, -1, VALIDATE_FOCUSOUT); 3047 } 3048 } 3049 EventuallyRedraw(entryPtr); 3050} 3051 3052/* 3053 *-------------------------------------------------------------- 3054 * 3055 * EntryTextVarProc -- 3056 * 3057 * This function is invoked when someone changes the variable whose 3058 * contents are to be displayed in an entry. 3059 * 3060 * Results: 3061 * NULL is always returned. 3062 * 3063 * Side effects: 3064 * The text displayed in the entry will change to match the variable. 3065 * 3066 *-------------------------------------------------------------- 3067 */ 3068 3069 /* ARGSUSED */ 3070static char * 3071EntryTextVarProc( 3072 ClientData clientData, /* Information about button. */ 3073 Tcl_Interp *interp, /* Interpreter containing variable. */ 3074 CONST char *name1, /* Not used. */ 3075 CONST char *name2, /* Not used. */ 3076 int flags) /* Information about what happened. */ 3077{ 3078 Entry *entryPtr = (Entry *) clientData; 3079 CONST char *value; 3080 3081 if (entryPtr->flags & ENTRY_DELETED) { 3082 /* 3083 * Just abort early if we entered here while being deleted. 3084 */ 3085 return NULL; 3086 } 3087 3088 /* 3089 * If the variable is unset, then immediately recreate it unless the whole 3090 * interpreter is going away. 3091 */ 3092 3093 if (flags & TCL_TRACE_UNSETS) { 3094 if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) { 3095 Tcl_SetVar(interp, entryPtr->textVarName, entryPtr->string, 3096 TCL_GLOBAL_ONLY); 3097 Tcl_TraceVar(interp, entryPtr->textVarName, 3098 TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, 3099 EntryTextVarProc, clientData); 3100 entryPtr->flags |= ENTRY_VAR_TRACED; 3101 } 3102 return NULL; 3103 } 3104 3105 /* 3106 * Update the entry's text with the value of the variable, unless the 3107 * entry already has that value (this happens when the variable changes 3108 * value because we changed it because someone typed in the entry). 3109 */ 3110 3111 value = Tcl_GetVar(interp, entryPtr->textVarName, TCL_GLOBAL_ONLY); 3112 if (value == NULL) { 3113 value = ""; 3114 } 3115 EntrySetValue(entryPtr, value); 3116 return NULL; 3117} 3118 3119/* 3120 *-------------------------------------------------------------- 3121 * 3122 * EntryValidate -- 3123 * 3124 * This function is invoked when any character is added or removed from 3125 * the entry widget, or a focus has trigerred validation. 3126 * 3127 * Results: 3128 3129 * TCL_OK if the validatecommand passes the new string. TCL_BREAK if the 3130 * vcmd executed OK, but rejects the string. TCL_ERROR if an error 3131 * occurred while executing the vcmd or a valid Tcl_Bool is not returned. 3132 * 3133 * Side effects: 3134 * An error condition may arise 3135 * 3136 *-------------------------------------------------------------- 3137 */ 3138 3139static int 3140EntryValidate( 3141 register Entry *entryPtr, /* Entry that needs validation. */ 3142 register char *cmd) /* Validation command (NULL-terminated 3143 * string). */ 3144{ 3145 register Tcl_Interp *interp = entryPtr->interp; 3146 int code, bool; 3147 3148 code = Tcl_EvalEx(interp, cmd, -1, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT); 3149 3150 /* 3151 * We accept TCL_OK and TCL_RETURN as valid return codes from the command 3152 * callback. 3153 */ 3154 3155 if (code != TCL_OK && code != TCL_RETURN) { 3156 Tcl_AddErrorInfo(interp, "\n\t(in validation command executed by "); 3157 Tcl_AddErrorInfo(interp, Tk_PathName(entryPtr->tkwin)); 3158 Tcl_AddErrorInfo(interp, ")"); 3159 Tcl_BackgroundError(interp); 3160 return TCL_ERROR; 3161 } 3162 3163 /* 3164 * The command callback should return an acceptable Tcl boolean. 3165 */ 3166 3167 if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), 3168 &bool) != TCL_OK) { 3169 Tcl_AddErrorInfo(interp, 3170 "\nvalid boolean not returned by validation command"); 3171 Tcl_BackgroundError(interp); 3172 Tcl_SetResult(interp, NULL, 0); 3173 return TCL_ERROR; 3174 } 3175 3176 Tcl_SetResult(interp, NULL, 0); 3177 return (bool ? TCL_OK : TCL_BREAK); 3178} 3179 3180/* 3181 *-------------------------------------------------------------- 3182 * 3183 * EntryValidateChange -- 3184 * 3185 * This function is invoked when any character is added or removed from 3186 * the entry widget, or a focus has trigerred validation. 3187 * 3188 * Results: 3189 * TCL_OK if the validatecommand accepts the new string, TCL_ERROR if any 3190 * problems occured with validatecommand. 3191 * 3192 * Side effects: 3193 * The insertion/deletion may be aborted, and the validatecommand might 3194 * turn itself off (if an error or loop condition arises). 3195 * 3196 *-------------------------------------------------------------- 3197 */ 3198 3199static int 3200EntryValidateChange( 3201 register Entry *entryPtr, /* Entry that needs validation. */ 3202 char *change, /* Characters to be added/deleted 3203 * (NUL-terminated string). */ 3204 CONST char *newValue, /* Potential new value of entry string */ 3205 int index, /* index of insert/delete, -1 otherwise */ 3206 int type) /* forced, delete, insert, focusin or 3207 * focusout */ 3208{ 3209 int code, varValidate = (entryPtr->flags & VALIDATE_VAR); 3210 char *p; 3211 Tcl_DString script; 3212 3213 if (entryPtr->validateCmd == NULL || 3214 entryPtr->validate == VALIDATE_NONE) { 3215 return (varValidate ? TCL_ERROR : TCL_OK); 3216 } 3217 3218 /* 3219 * If we're already validating, then we're hitting a loop condition Return 3220 * and set validate to 0 to disallow further validations and prevent 3221 * current validation from finishing 3222 */ 3223 3224 if (entryPtr->flags & VALIDATING) { 3225 entryPtr->validate = VALIDATE_NONE; 3226 return (varValidate ? TCL_ERROR : TCL_OK); 3227 } 3228 3229 entryPtr->flags |= VALIDATING; 3230 3231 /* 3232 * Now form command string and run through the -validatecommand 3233 */ 3234 3235 Tcl_DStringInit(&script); 3236 ExpandPercents(entryPtr, entryPtr->validateCmd, 3237 change, newValue, index, type, &script); 3238 Tcl_DStringAppend(&script, "", 1); 3239 3240 p = Tcl_DStringValue(&script); 3241 code = EntryValidate(entryPtr, p); 3242 Tcl_DStringFree(&script); 3243 3244 /* 3245 * If e->validate has become VALIDATE_NONE during the validation, or we 3246 * now have VALIDATE_VAR set (from EntrySetValue) and didn't before, it 3247 * means that a loop condition almost occured. Do not allow this 3248 * validation result to finish. 3249 */ 3250 3251 if (entryPtr->validate == VALIDATE_NONE 3252 || (!varValidate && (entryPtr->flags & VALIDATE_VAR))) { 3253 code = TCL_ERROR; 3254 } 3255 3256 /* 3257 * It's possible that the user deleted the entry during validation. In 3258 * that case, abort future validation and return an error. 3259 */ 3260 3261 if (entryPtr->flags & ENTRY_DELETED) { 3262 return TCL_ERROR; 3263 } 3264 3265 /* 3266 * If validate will return ERROR, then disallow further validations 3267 * Otherwise, if it didn't accept the new string (returned TCL_BREAK) then 3268 * eval the invalidCmd (if it's set) 3269 */ 3270 3271 if (code == TCL_ERROR) { 3272 entryPtr->validate = VALIDATE_NONE; 3273 } else if (code == TCL_BREAK) { 3274 /* 3275 * If we were doing forced validation (like via a variable trace) and 3276 * the command returned 0, the we turn off validation because we 3277 * assume that textvariables have precedence in managing the value. 3278 * We also don't call the invcmd, as it may want to do entry 3279 * manipulation which the setting of the var will later wipe anyway. 3280 */ 3281 3282 if (varValidate) { 3283 entryPtr->validate = VALIDATE_NONE; 3284 } else if (entryPtr->invalidCmd != NULL) { 3285 Tcl_DStringInit(&script); 3286 ExpandPercents(entryPtr, entryPtr->invalidCmd, 3287 change, newValue, index, type, &script); 3288 Tcl_DStringAppend(&script, "", 1); 3289 p = Tcl_DStringValue(&script); 3290 if (Tcl_EvalEx(entryPtr->interp, p, -1, 3291 TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT) != TCL_OK) { 3292 Tcl_AddErrorInfo(entryPtr->interp, 3293 "\n\t(in invalidcommand executed by entry)"); 3294 Tcl_BackgroundError(entryPtr->interp); 3295 code = TCL_ERROR; 3296 entryPtr->validate = VALIDATE_NONE; 3297 } 3298 Tcl_DStringFree(&script); 3299 3300 /* 3301 * It's possible that the user deleted the entry during 3302 * validation. In that case, abort future validation and return an 3303 * error. 3304 */ 3305 3306 if (entryPtr->flags & ENTRY_DELETED) { 3307 return TCL_ERROR; 3308 } 3309 } 3310 } 3311 3312 entryPtr->flags &= ~VALIDATING; 3313 3314 return code; 3315} 3316 3317/* 3318 *-------------------------------------------------------------- 3319 * 3320 * ExpandPercents -- 3321 * 3322 * Given a command and an event, produce a new command by replacing % 3323 * constructs in the original command with information from the X event. 3324 * 3325 * Results: 3326 * The new expanded command is appended to the dynamic string given by 3327 * dsPtr. 3328 * 3329 * Side effects: 3330 * None. 3331 * 3332 *-------------------------------------------------------------- 3333 */ 3334 3335static void 3336ExpandPercents( 3337 register Entry *entryPtr, /* Entry that needs validation. */ 3338 register CONST char *before, 3339 /* Command containing percent expressions to 3340 * be replaced. */ 3341 CONST char *change, /* Characters to added/deleted (NUL-terminated 3342 * string). */ 3343 CONST char *newValue, /* Potential new value of entry string */ 3344 int index, /* index of insert/delete */ 3345 int type, /* INSERT or DELETE */ 3346 Tcl_DString *dsPtr) /* Dynamic string in which to append new 3347 * command. */ 3348{ 3349 int spaceNeeded, cvtFlags; /* Used to substitute string as proper Tcl 3350 * list element. */ 3351 int number, length; 3352 register CONST char *string; 3353 Tcl_UniChar ch; 3354 char numStorage[2*TCL_INTEGER_SPACE]; 3355 3356 while (1) { 3357 if (*before == '\0') { 3358 break; 3359 } 3360 /* 3361 * Find everything up to the next % character and append it to the 3362 * result string. 3363 */ 3364 3365 string = before; 3366 3367 /* 3368 * No need to convert '%', as it is in ascii range. 3369 */ 3370 3371 string = Tcl_UtfFindFirst(before, '%'); 3372 if (string == NULL) { 3373 Tcl_DStringAppend(dsPtr, before, -1); 3374 break; 3375 } else if (string != before) { 3376 Tcl_DStringAppend(dsPtr, before, string-before); 3377 before = string; 3378 } 3379 3380 /* 3381 * There's a percent sequence here. Process it. 3382 */ 3383 3384 before++; /* skip over % */ 3385 if (*before != '\0') { 3386 before += Tcl_UtfToUniChar(before, &ch); 3387 } else { 3388 ch = '%'; 3389 } 3390 if (type == VALIDATE_BUTTON) { 3391 /* 3392 * -command %-substitution 3393 */ 3394 3395 switch (ch) { 3396 case 's': /* Current string value of spinbox */ 3397 string = entryPtr->string; 3398 break; 3399 case 'd': /* direction, up or down */ 3400 string = change; 3401 break; 3402 case 'W': /* widget name */ 3403 string = Tk_PathName(entryPtr->tkwin); 3404 break; 3405 default: 3406 length = Tcl_UniCharToUtf(ch, numStorage); 3407 numStorage[length] = '\0'; 3408 string = numStorage; 3409 break; 3410 } 3411 } else { 3412 /* 3413 * -validatecommand / -invalidcommand %-substitution 3414 */ 3415 3416 switch (ch) { 3417 case 'd': /* Type of call that caused validation */ 3418 switch (type) { 3419 case VALIDATE_INSERT: 3420 number = 1; 3421 break; 3422 case VALIDATE_DELETE: 3423 number = 0; 3424 break; 3425 default: 3426 number = -1; 3427 break; 3428 } 3429 sprintf(numStorage, "%d", number); 3430 string = numStorage; 3431 break; 3432 case 'i': /* index of insert/delete */ 3433 sprintf(numStorage, "%d", index); 3434 string = numStorage; 3435 break; 3436 case 'P': /* 'Peeked' new value of the string */ 3437 string = newValue; 3438 break; 3439 case 's': /* Current string value of spinbox */ 3440 string = entryPtr->string; 3441 break; 3442 case 'S': /* string to be inserted/deleted, if any */ 3443 string = change; 3444 break; 3445 case 'v': /* type of validation currently set */ 3446 string = validateStrings[entryPtr->validate]; 3447 break; 3448 case 'V': /* type of validation in effect */ 3449 switch (type) { 3450 case VALIDATE_INSERT: 3451 case VALIDATE_DELETE: 3452 string = validateStrings[VALIDATE_KEY]; 3453 break; 3454 case VALIDATE_FORCED: 3455 string = "forced"; 3456 break; 3457 default: 3458 string = validateStrings[type]; 3459 break; 3460 } 3461 break; 3462 case 'W': /* widget name */ 3463 string = Tk_PathName(entryPtr->tkwin); 3464 break; 3465 default: 3466 length = Tcl_UniCharToUtf(ch, numStorage); 3467 numStorage[length] = '\0'; 3468 string = numStorage; 3469 break; 3470 } 3471 } 3472 3473 spaceNeeded = Tcl_ScanCountedElement(string, -1, &cvtFlags); 3474 length = Tcl_DStringLength(dsPtr); 3475 Tcl_DStringSetLength(dsPtr, length + spaceNeeded); 3476 spaceNeeded = Tcl_ConvertCountedElement(string, -1, 3477 Tcl_DStringValue(dsPtr) + length, 3478 cvtFlags | TCL_DONT_USE_BRACES); 3479 Tcl_DStringSetLength(dsPtr, length + spaceNeeded); 3480 } 3481} 3482 3483/* 3484 *-------------------------------------------------------------- 3485 * 3486 * Tk_SpinboxObjCmd -- 3487 * 3488 * This function is invoked to process the "spinbox" Tcl command. See the 3489 * user documentation for details on what it does. 3490 * 3491 * Results: 3492 * A standard Tcl result. 3493 * 3494 * Side effects: 3495 * See the user documentation. 3496 * 3497 *-------------------------------------------------------------- 3498 */ 3499 3500int 3501Tk_SpinboxObjCmd( 3502 ClientData clientData, /* NULL. */ 3503 Tcl_Interp *interp, /* Current interpreter. */ 3504 int objc, /* Number of arguments. */ 3505 Tcl_Obj *CONST objv[]) /* Argument objects. */ 3506{ 3507 register Entry *entryPtr; 3508 register Spinbox *sbPtr; 3509 Tk_OptionTable optionTable; 3510 Tk_Window tkwin; 3511 char *tmp; 3512 3513 if (objc < 2) { 3514 Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?"); 3515 return TCL_ERROR; 3516 } 3517 3518 tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), 3519 Tcl_GetString(objv[1]), NULL); 3520 if (tkwin == NULL) { 3521 return TCL_ERROR; 3522 } 3523 3524 /* 3525 * Create the option table for this widget class. If it has already been 3526 * created, Tk will return the cached value. 3527 */ 3528 3529 optionTable = Tk_CreateOptionTable(interp, sbOptSpec); 3530 3531 /* 3532 * Initialize the fields of the structure that won't be initialized by 3533 * ConfigureEntry, or that ConfigureEntry requires to be initialized 3534 * already (e.g. resource pointers). Only the non-NULL/0 data must be 3535 * initialized as memset covers the rest. 3536 */ 3537 3538 sbPtr = (Spinbox *) ckalloc(sizeof(Spinbox)); 3539 entryPtr = (Entry *) sbPtr; 3540 memset(sbPtr, 0, sizeof(Spinbox)); 3541 3542 entryPtr->tkwin = tkwin; 3543 entryPtr->display = Tk_Display(tkwin); 3544 entryPtr->interp = interp; 3545 entryPtr->widgetCmd = Tcl_CreateObjCommand(interp, 3546 Tk_PathName(entryPtr->tkwin), SpinboxWidgetObjCmd, 3547 (ClientData) sbPtr, EntryCmdDeletedProc); 3548 entryPtr->optionTable = optionTable; 3549 entryPtr->type = TK_SPINBOX; 3550 tmp = (char *) ckalloc(1); 3551 tmp[0] = '\0'; 3552 entryPtr->string = tmp; 3553 entryPtr->selectFirst = -1; 3554 entryPtr->selectLast = -1; 3555 3556 entryPtr->cursor = None; 3557 entryPtr->exportSelection = 1; 3558 entryPtr->justify = TK_JUSTIFY_LEFT; 3559 entryPtr->relief = TK_RELIEF_FLAT; 3560 entryPtr->state = STATE_NORMAL; 3561 entryPtr->displayString = entryPtr->string; 3562 entryPtr->inset = XPAD; 3563 entryPtr->textGC = None; 3564 entryPtr->selTextGC = None; 3565 entryPtr->highlightGC = None; 3566 entryPtr->avgWidth = 1; 3567 entryPtr->validate = VALIDATE_NONE; 3568 3569 sbPtr->selElement = SEL_NONE; 3570 sbPtr->curElement = SEL_NONE; 3571 sbPtr->bCursor = None; 3572 sbPtr->repeatDelay = 400; 3573 sbPtr->repeatInterval = 100; 3574 sbPtr->fromValue = 0.0; 3575 sbPtr->toValue = 100.0; 3576 sbPtr->increment = 1.0; 3577 sbPtr->formatBuf = (char *) ckalloc(TCL_DOUBLE_SPACE); 3578 sbPtr->bdRelief = TK_RELIEF_FLAT; 3579 sbPtr->buRelief = TK_RELIEF_FLAT; 3580 3581 /* 3582 * Keep a hold of the associated tkwin until we destroy the spinbox, 3583 * otherwise Tk might free it while we still need it. 3584 */ 3585 3586 Tcl_Preserve((ClientData) entryPtr->tkwin); 3587 3588 Tk_SetClass(entryPtr->tkwin, "Spinbox"); 3589 Tk_SetClassProcs(entryPtr->tkwin, &entryClass, (ClientData) entryPtr); 3590 Tk_CreateEventHandler(entryPtr->tkwin, 3591 PointerMotionMask|ExposureMask|StructureNotifyMask|FocusChangeMask, 3592 EntryEventProc, (ClientData) entryPtr); 3593 Tk_CreateSelHandler(entryPtr->tkwin, XA_PRIMARY, XA_STRING, 3594 EntryFetchSelection, (ClientData) entryPtr, XA_STRING); 3595 3596 if (Tk_InitOptions(interp, (char *) sbPtr, optionTable, tkwin) 3597 != TCL_OK) { 3598 Tk_DestroyWindow(entryPtr->tkwin); 3599 return TCL_ERROR; 3600 } 3601 if (ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0) != TCL_OK) { 3602 goto error; 3603 } 3604 3605 Tcl_SetResult(interp, Tk_PathName(entryPtr->tkwin), TCL_STATIC); 3606 return TCL_OK; 3607 3608 error: 3609 Tk_DestroyWindow(entryPtr->tkwin); 3610 return TCL_ERROR; 3611} 3612 3613/* 3614 *-------------------------------------------------------------- 3615 * 3616 * SpinboxWidgetObjCmd -- 3617 * 3618 * This function is invoked to process the Tcl command that corresponds 3619 * to a widget managed by this module. See the user documentation for 3620 * details on what it does. 3621 * 3622 * Results: 3623 * A standard Tcl result. 3624 * 3625 * Side effects: 3626 * See the user documentation. 3627 * 3628 *-------------------------------------------------------------- 3629 */ 3630 3631static int 3632SpinboxWidgetObjCmd( 3633 ClientData clientData, /* Information about spinbox widget. */ 3634 Tcl_Interp *interp, /* Current interpreter. */ 3635 int objc, /* Number of arguments. */ 3636 Tcl_Obj *CONST objv[]) /* Argument objects. */ 3637{ 3638 Entry *entryPtr = (Entry *) clientData; 3639 Spinbox *sbPtr = (Spinbox *) clientData; 3640 int cmdIndex, selIndex, result; 3641 Tcl_Obj *objPtr; 3642 3643 if (objc < 2) { 3644 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 3645 return TCL_ERROR; 3646 } 3647 3648 /* 3649 * Parse the widget command by looking up the second token in the list of 3650 * valid command names. 3651 */ 3652 3653 result = Tcl_GetIndexFromObj(interp, objv[1], sbCmdNames, 3654 "option", 0, &cmdIndex); 3655 if (result != TCL_OK) { 3656 return result; 3657 } 3658 3659 Tcl_Preserve((ClientData) entryPtr); 3660 switch ((enum sbCmd) cmdIndex) { 3661 case SB_CMD_BBOX: { 3662 int index, x, y, width, height; 3663 char buf[TCL_INTEGER_SPACE * 4]; 3664 3665 if (objc != 3) { 3666 Tcl_WrongNumArgs(interp, 2, objv, "index"); 3667 goto error; 3668 } 3669 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 3670 &index) != TCL_OK) { 3671 goto error; 3672 } 3673 if ((index == entryPtr->numChars) && (index > 0)) { 3674 index--; 3675 } 3676 Tk_CharBbox(entryPtr->textLayout, index, &x, &y, 3677 &width, &height); 3678 sprintf(buf, "%d %d %d %d", x + entryPtr->layoutX, 3679 y + entryPtr->layoutY, width, height); 3680 Tcl_SetResult(interp, buf, TCL_VOLATILE); 3681 break; 3682 } 3683 3684 case SB_CMD_CGET: 3685 if (objc != 3) { 3686 Tcl_WrongNumArgs(interp, 2, objv, "option"); 3687 goto error; 3688 } 3689 3690 objPtr = Tk_GetOptionValue(interp, (char *) entryPtr, 3691 entryPtr->optionTable, objv[2], entryPtr->tkwin); 3692 if (objPtr == NULL) { 3693 goto error; 3694 } else { 3695 Tcl_SetObjResult(interp, objPtr); 3696 } 3697 break; 3698 3699 case SB_CMD_CONFIGURE: 3700 if (objc <= 3) { 3701 objPtr = Tk_GetOptionInfo(interp, (char *) entryPtr, 3702 entryPtr->optionTable, 3703 (objc == 3) ? objv[2] : NULL, 3704 entryPtr->tkwin); 3705 if (objPtr == NULL) { 3706 goto error; 3707 } else { 3708 Tcl_SetObjResult(interp, objPtr); 3709 } 3710 } else { 3711 result = ConfigureEntry(interp, entryPtr, objc-2, objv+2, 0); 3712 } 3713 break; 3714 3715 case SB_CMD_DELETE: { 3716 int first, last; 3717 3718 if ((objc < 3) || (objc > 4)) { 3719 Tcl_WrongNumArgs(interp, 2, objv, "firstIndex ?lastIndex?"); 3720 goto error; 3721 } 3722 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 3723 &first) != TCL_OK) { 3724 goto error; 3725 } 3726 if (objc == 3) { 3727 last = first + 1; 3728 } else { 3729 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[3]), 3730 &last) != TCL_OK) { 3731 goto error; 3732 } 3733 } 3734 if ((last >= first) && (entryPtr->state == STATE_NORMAL)) { 3735 DeleteChars(entryPtr, first, last - first); 3736 } 3737 break; 3738 } 3739 3740 case SB_CMD_GET: 3741 if (objc != 2) { 3742 Tcl_WrongNumArgs(interp, 2, objv, NULL); 3743 goto error; 3744 } 3745 Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1); 3746 break; 3747 3748 case SB_CMD_ICURSOR: 3749 if (objc != 3) { 3750 Tcl_WrongNumArgs(interp, 2, objv, "pos"); 3751 goto error; 3752 } 3753 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 3754 &entryPtr->insertPos) != TCL_OK) { 3755 goto error; 3756 } 3757 EventuallyRedraw(entryPtr); 3758 break; 3759 3760 case SB_CMD_IDENTIFY: { 3761 int x, y, elem; 3762 3763 if (objc != 4) { 3764 Tcl_WrongNumArgs(interp, 2, objv, "x y"); 3765 goto error; 3766 } 3767 if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK) || 3768 (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) { 3769 goto error; 3770 } 3771 elem = GetSpinboxElement(sbPtr, x, y); 3772 if (elem != SEL_NONE) { 3773 Tcl_SetStringObj(Tcl_GetObjResult(interp), 3774 selElementNames[elem], -1); 3775 } 3776 break; 3777 } 3778 3779 case SB_CMD_INDEX: { 3780 int index; 3781 3782 if (objc != 3) { 3783 Tcl_WrongNumArgs(interp, 2, objv, "string"); 3784 goto error; 3785 } 3786 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 3787 &index) != TCL_OK) { 3788 goto error; 3789 } 3790 Tcl_SetObjResult(interp, Tcl_NewIntObj(index)); 3791 break; 3792 } 3793 3794 case SB_CMD_INSERT: { 3795 int index; 3796 3797 if (objc != 4) { 3798 Tcl_WrongNumArgs(interp, 2, objv, "index text"); 3799 goto error; 3800 } 3801 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 3802 &index) != TCL_OK) { 3803 goto error; 3804 } 3805 if (entryPtr->state == STATE_NORMAL) { 3806 InsertChars(entryPtr, index, Tcl_GetString(objv[3])); 3807 } 3808 break; 3809 } 3810 3811 case SB_CMD_INVOKE: 3812 if (objc != 3) { 3813 Tcl_WrongNumArgs(interp, 2, objv, "elemName"); 3814 goto error; 3815 } 3816 result = Tcl_GetIndexFromObj(interp, objv[2], 3817 selElementNames, "element", 0, &cmdIndex); 3818 if (result != TCL_OK) { 3819 goto error; 3820 } 3821 if (entryPtr->state != STATE_DISABLED) { 3822 if (SpinboxInvoke(interp, sbPtr, cmdIndex) != TCL_OK) { 3823 goto error; 3824 } 3825 } 3826 break; 3827 3828 case SB_CMD_SCAN: { 3829 int x; 3830 char *minorCmd; 3831 3832 if (objc != 4) { 3833 Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x"); 3834 goto error; 3835 } 3836 if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) { 3837 goto error; 3838 } 3839 3840 minorCmd = Tcl_GetString(objv[2]); 3841 if (minorCmd[0] == 'm' 3842 && (strncmp(minorCmd, "mark", strlen(minorCmd)) == 0)) { 3843 entryPtr->scanMarkX = x; 3844 entryPtr->scanMarkIndex = entryPtr->leftIndex; 3845 } else if ((minorCmd[0] == 'd') 3846 && (strncmp(minorCmd, "dragto", strlen(minorCmd)) == 0)) { 3847 EntryScanTo(entryPtr, x); 3848 } else { 3849 Tcl_AppendResult(interp, "bad scan option \"", 3850 Tcl_GetString(objv[2]), "\": must be mark or dragto", 3851 NULL); 3852 goto error; 3853 } 3854 break; 3855 } 3856 3857 case SB_CMD_SELECTION: { 3858 int index, index2; 3859 3860 if (objc < 3) { 3861 Tcl_WrongNumArgs(interp, 2, objv, "option ?index?"); 3862 goto error; 3863 } 3864 3865 /* 3866 * Parse the selection sub-command, using the command table 3867 * "sbSelCmdNames" defined above. 3868 */ 3869 3870 result = Tcl_GetIndexFromObj(interp, objv[2], sbSelCmdNames, 3871 "selection option", 0, &selIndex); 3872 if (result != TCL_OK) { 3873 goto error; 3874 } 3875 3876 /* 3877 * Disabled entries don't allow the selection to be modified, but 3878 * 'selection present' must return a boolean. 3879 */ 3880 3881 if ((entryPtr->state == STATE_DISABLED) 3882 && (selIndex != SB_SEL_PRESENT)) { 3883 goto done; 3884 } 3885 3886 switch (selIndex) { 3887 case SB_SEL_ADJUST: 3888 if (objc != 4) { 3889 Tcl_WrongNumArgs(interp, 3, objv, "index"); 3890 goto error; 3891 } 3892 if (GetEntryIndex(interp, entryPtr, 3893 Tcl_GetString(objv[3]), &index) != TCL_OK) { 3894 goto error; 3895 } 3896 if (entryPtr->selectFirst >= 0) { 3897 int half1, half2; 3898 3899 half1 = (entryPtr->selectFirst + entryPtr->selectLast)/2; 3900 half2 = (entryPtr->selectFirst + entryPtr->selectLast + 1)/2; 3901 if (index < half1) { 3902 entryPtr->selectAnchor = entryPtr->selectLast; 3903 } else if (index > half2) { 3904 entryPtr->selectAnchor = entryPtr->selectFirst; 3905 } else { 3906 /* 3907 * We're at about the halfway point in the selection; just 3908 * keep the existing anchor. 3909 */ 3910 } 3911 } 3912 EntrySelectTo(entryPtr, index); 3913 break; 3914 3915 case SB_SEL_CLEAR: 3916 if (objc != 3) { 3917 Tcl_WrongNumArgs(interp, 3, objv, NULL); 3918 goto error; 3919 } 3920 if (entryPtr->selectFirst >= 0) { 3921 entryPtr->selectFirst = -1; 3922 entryPtr->selectLast = -1; 3923 EventuallyRedraw(entryPtr); 3924 } 3925 goto done; 3926 3927 case SB_SEL_FROM: 3928 if (objc != 4) { 3929 Tcl_WrongNumArgs(interp, 3, objv, "index"); 3930 goto error; 3931 } 3932 if (GetEntryIndex(interp, entryPtr, 3933 Tcl_GetString(objv[3]), &index) != TCL_OK) { 3934 goto error; 3935 } 3936 entryPtr->selectAnchor = index; 3937 break; 3938 3939 case SB_SEL_PRESENT: 3940 if (objc != 3) { 3941 Tcl_WrongNumArgs(interp, 3, objv, NULL); 3942 goto error; 3943 } 3944 Tcl_SetObjResult(interp, 3945 Tcl_NewBooleanObj((entryPtr->selectFirst >= 0))); 3946 goto done; 3947 3948 case SB_SEL_RANGE: 3949 if (objc != 5) { 3950 Tcl_WrongNumArgs(interp, 3, objv, "start end"); 3951 goto error; 3952 } 3953 if (GetEntryIndex(interp, entryPtr, 3954 Tcl_GetString(objv[3]), &index) != TCL_OK) { 3955 goto error; 3956 } 3957 if (GetEntryIndex(interp, entryPtr, 3958 Tcl_GetString(objv[4]),& index2) != TCL_OK) { 3959 goto error; 3960 } 3961 if (index >= index2) { 3962 entryPtr->selectFirst = -1; 3963 entryPtr->selectLast = -1; 3964 } else { 3965 entryPtr->selectFirst = index; 3966 entryPtr->selectLast = index2; 3967 } 3968 if (!(entryPtr->flags & GOT_SELECTION) 3969 && (entryPtr->exportSelection)) { 3970 Tk_OwnSelection(entryPtr->tkwin, XA_PRIMARY, 3971 EntryLostSelection, (ClientData) entryPtr); 3972 entryPtr->flags |= GOT_SELECTION; 3973 } 3974 EventuallyRedraw(entryPtr); 3975 break; 3976 3977 case SB_SEL_TO: 3978 if (objc != 4) { 3979 Tcl_WrongNumArgs(interp, 3, objv, "index"); 3980 goto error; 3981 } 3982 if (GetEntryIndex(interp, entryPtr, 3983 Tcl_GetString(objv[3]), &index) != TCL_OK) { 3984 goto error; 3985 } 3986 EntrySelectTo(entryPtr, index); 3987 break; 3988 3989 case SB_SEL_ELEMENT: 3990 if ((objc < 3) || (objc > 4)) { 3991 Tcl_WrongNumArgs(interp, 3, objv, "?elemName?"); 3992 goto error; 3993 } 3994 if (objc == 3) { 3995 Tcl_SetStringObj(Tcl_GetObjResult(interp), 3996 selElementNames[sbPtr->selElement], -1); 3997 } else { 3998 int lastElement = sbPtr->selElement; 3999 4000 result = Tcl_GetIndexFromObj(interp, objv[3], selElementNames, 4001 "selection element", 0, &(sbPtr->selElement)); 4002 if (result != TCL_OK) { 4003 goto error; 4004 } 4005 if (lastElement != sbPtr->selElement) { 4006 EventuallyRedraw(entryPtr); 4007 } 4008 } 4009 break; 4010 } 4011 break; 4012 } 4013 4014 case SB_CMD_SET: 4015 if (objc > 3) { 4016 Tcl_WrongNumArgs(interp, 2, objv, "?string?"); 4017 goto error; 4018 } 4019 if (objc == 3) { 4020 EntryValueChanged(entryPtr, Tcl_GetString(objv[2])); 4021 } 4022 Tcl_SetStringObj(Tcl_GetObjResult(interp), entryPtr->string, -1); 4023 break; 4024 4025 case SB_CMD_VALIDATE: { 4026 int code; 4027 4028 if (objc != 2) { 4029 Tcl_WrongNumArgs(interp, 2, objv, NULL); 4030 goto error; 4031 } 4032 selIndex = entryPtr->validate; 4033 entryPtr->validate = VALIDATE_ALL; 4034 code = EntryValidateChange(entryPtr, NULL, entryPtr->string, 4035 -1, VALIDATE_FORCED); 4036 if (entryPtr->validate != VALIDATE_NONE) { 4037 entryPtr->validate = selIndex; 4038 } 4039 Tcl_SetObjResult(interp, Tcl_NewBooleanObj((code == TCL_OK))); 4040 break; 4041 } 4042 4043 case SB_CMD_XVIEW: { 4044 int index; 4045 4046 if (objc == 2) { 4047 double first, last; 4048 char buf[TCL_DOUBLE_SPACE]; 4049 4050 EntryVisibleRange(entryPtr, &first, &last); 4051 Tcl_PrintDouble(NULL, first, buf); 4052 Tcl_SetResult(interp, buf, TCL_VOLATILE); 4053 Tcl_PrintDouble(NULL, last, buf); 4054 Tcl_AppendResult(interp, " ", buf, NULL); 4055 goto done; 4056 } else if (objc == 3) { 4057 if (GetEntryIndex(interp, entryPtr, Tcl_GetString(objv[2]), 4058 &index) != TCL_OK) { 4059 goto error; 4060 } 4061 } else { 4062 double fraction; 4063 int count; 4064 4065 index = entryPtr->leftIndex; 4066 switch (Tk_GetScrollInfoObj(interp, objc, objv, &fraction, 4067 &count)) { 4068 case TK_SCROLL_ERROR: 4069 goto error; 4070 case TK_SCROLL_MOVETO: 4071 index = (int) ((fraction * entryPtr->numChars) + 0.5); 4072 break; 4073 case TK_SCROLL_PAGES: { 4074 int charsPerPage; 4075 4076 charsPerPage = ((Tk_Width(entryPtr->tkwin) 4077 - 2 * entryPtr->inset - entryPtr->xWidth) 4078 / entryPtr->avgWidth) - 2; 4079 if (charsPerPage < 1) { 4080 charsPerPage = 1; 4081 } 4082 index += count * charsPerPage; 4083 break; 4084 } 4085 case TK_SCROLL_UNITS: 4086 index += count; 4087 break; 4088 } 4089 } 4090 if (index >= entryPtr->numChars) { 4091 index = entryPtr->numChars - 1; 4092 } 4093 if (index < 0) { 4094 index = 0; 4095 } 4096 entryPtr->leftIndex = index; 4097 entryPtr->flags |= UPDATE_SCROLLBAR; 4098 EntryComputeGeometry(entryPtr); 4099 EventuallyRedraw(entryPtr); 4100 break; 4101 } 4102 } 4103 4104 done: 4105 Tcl_Release((ClientData) entryPtr); 4106 return result; 4107 4108 error: 4109 Tcl_Release((ClientData) entryPtr); 4110 return TCL_ERROR; 4111} 4112 4113/* 4114 *--------------------------------------------------------------------------- 4115 * 4116 * GetSpinboxElement -- 4117 * 4118 * Return the element associated with an x,y coord. 4119 * 4120 * Results: 4121 * Element type as enum selelement. 4122 * 4123 * Side effects: 4124 * None. 4125 * 4126 *--------------------------------------------------------------------------- 4127 */ 4128 4129static int 4130GetSpinboxElement( 4131 Spinbox *sbPtr, /* Spinbox for which the index is being 4132 * specified. */ 4133 int x, int y) /* Widget-relative coordinates. */ 4134{ 4135 Entry *entryPtr = (Entry *) sbPtr; 4136 4137 if ((x < 0) || (y < 0) || (y > Tk_Height(entryPtr->tkwin)) 4138 || (x > Tk_Width(entryPtr->tkwin))) { 4139 return SEL_NONE; 4140 } 4141 4142 if (x > (Tk_Width(entryPtr->tkwin) - entryPtr->inset - entryPtr->xWidth)) { 4143 if (y > (Tk_Height(entryPtr->tkwin) / 2)) { 4144 return SEL_BUTTONDOWN; 4145 } else { 4146 return SEL_BUTTONUP; 4147 } 4148 } 4149 return SEL_ENTRY; 4150} 4151 4152/* 4153 *-------------------------------------------------------------- 4154 * 4155 * SpinboxInvoke -- 4156 * 4157 * This function is invoked when the invoke method for the widget is 4158 * called. 4159 * 4160 * Results: 4161 * TCL_OK. 4162 * 4163 * Side effects: 4164 * An background error condition may arise when invoking the callback. 4165 * The widget value may change. 4166 * 4167 *-------------------------------------------------------------- 4168 */ 4169 4170static int 4171SpinboxInvoke( 4172 register Tcl_Interp *interp,/* Current interpreter. */ 4173 register Spinbox *sbPtr, /* Spinbox to invoke. */ 4174 int element) /* Element to invoke, either the "up" or 4175 * "down" button. */ 4176{ 4177 Entry *entryPtr = (Entry *) sbPtr; 4178 char *type; 4179 int code, up; 4180 Tcl_DString script; 4181 4182 switch (element) { 4183 case SEL_BUTTONUP: 4184 type = "up"; 4185 up = 1; 4186 break; 4187 case SEL_BUTTONDOWN: 4188 type = "down"; 4189 up = 0; 4190 break; 4191 default: 4192 return TCL_OK; 4193 } 4194 4195 if (fabs(sbPtr->increment) > MIN_DBL_VAL) { 4196 if (sbPtr->listObj != NULL) { 4197 Tcl_Obj *objPtr; 4198 4199 Tcl_ListObjIndex(interp, sbPtr->listObj, sbPtr->eIndex, &objPtr); 4200 if (strcmp(Tcl_GetString(objPtr), entryPtr->string)) { 4201 /* 4202 * Somehow the string changed from what we expected, so let's 4203 * do a search on the list to see if the current value is 4204 * there. If not, move to the first element of the list. 4205 */ 4206 4207 int i, listc, elemLen, length = entryPtr->numChars; 4208 char *bytes; 4209 Tcl_Obj **listv; 4210 4211 Tcl_ListObjGetElements(interp, sbPtr->listObj, &listc, &listv); 4212 for (i = 0; i < listc; i++) { 4213 bytes = Tcl_GetStringFromObj(listv[i], &elemLen); 4214 if ((length == elemLen) && 4215 (memcmp(bytes, entryPtr->string, 4216 (size_t) length) == 0)) { 4217 sbPtr->eIndex = i; 4218 break; 4219 } 4220 } 4221 } 4222 if (up) { 4223 if (++sbPtr->eIndex >= sbPtr->nElements) { 4224 if (sbPtr->wrap) { 4225 sbPtr->eIndex = 0; 4226 } else { 4227 sbPtr->eIndex = sbPtr->nElements-1; 4228 } 4229 } 4230 } else { 4231 if (--sbPtr->eIndex < 0) { 4232 if (sbPtr->wrap) { 4233 sbPtr->eIndex = sbPtr->nElements-1; 4234 } else { 4235 sbPtr->eIndex = 0; 4236 } 4237 } 4238 } 4239 Tcl_ListObjIndex(interp, sbPtr->listObj, sbPtr->eIndex, &objPtr); 4240 EntryValueChanged(entryPtr, Tcl_GetString(objPtr)); 4241 } else if (!DOUBLES_EQ(sbPtr->fromValue, sbPtr->toValue)) { 4242 double dvalue; 4243 4244 if (Tcl_GetDouble(NULL, entryPtr->string, &dvalue) != TCL_OK) { 4245 /* 4246 * If the string is empty, or isn't a valid double value, just 4247 * use the -from value 4248 */ 4249 4250 dvalue = sbPtr->fromValue; 4251 } else if (up) { 4252 dvalue += sbPtr->increment; 4253 if (dvalue > sbPtr->toValue) { 4254 if (sbPtr->wrap) { 4255 dvalue = sbPtr->fromValue; 4256 } else { 4257 dvalue = sbPtr->toValue; 4258 } 4259 } else if (dvalue < sbPtr->fromValue) { 4260 /* 4261 * It's possible that when pressing up, we are still less 4262 * than the fromValue, because the user may have 4263 * manipulated the value by hand. 4264 */ 4265 4266 dvalue = sbPtr->fromValue; 4267 } 4268 } else { 4269 dvalue -= sbPtr->increment; 4270 if (dvalue < sbPtr->fromValue) { 4271 if (sbPtr->wrap) { 4272 dvalue = sbPtr->toValue; 4273 } else { 4274 dvalue = sbPtr->fromValue; 4275 } 4276 } else if (dvalue > sbPtr->toValue) { 4277 /* 4278 * It's possible that when pressing down, we are still 4279 * greater than the toValue, because the user may have 4280 * manipulated the value by hand. 4281 */ 4282 4283 dvalue = sbPtr->toValue; 4284 } 4285 } 4286 sprintf(sbPtr->formatBuf, sbPtr->valueFormat, dvalue); 4287 EntryValueChanged(entryPtr, sbPtr->formatBuf); 4288 } 4289 } 4290 4291 if (sbPtr->command != NULL) { 4292 Tcl_DStringInit(&script); 4293 ExpandPercents(entryPtr, sbPtr->command, type, "", 0, 4294 VALIDATE_BUTTON, &script); 4295 Tcl_DStringAppend(&script, "", 1); 4296 4297 code = Tcl_EvalEx(interp, Tcl_DStringValue(&script), -1, 4298 TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT); 4299 Tcl_DStringFree(&script); 4300 4301 if (code != TCL_OK) { 4302 Tcl_AddErrorInfo(interp, "\n\t(in command executed by spinbox)"); 4303 Tcl_BackgroundError(interp); 4304 4305 /* 4306 * Yes, it's an error, but a bg one, so we return OK 4307 */ 4308 4309 return TCL_OK; 4310 } 4311 4312 Tcl_SetResult(interp, NULL, 0); 4313 } 4314 4315 return TCL_OK; 4316} 4317 4318/* 4319 *---------------------------------------------------------------------- 4320 * 4321 * ComputeFormat -- 4322 * 4323 * This function is invoked to recompute the "format" fields of a 4324 * spinbox's widget record, which determines how the value of the dial is 4325 * converted to a string. 4326 * 4327 * Results: 4328 * Tcl result code. 4329 * 4330 * Side effects: 4331 * The format fields of the spinbox are modified. 4332 * 4333 *---------------------------------------------------------------------- 4334 */ 4335 4336static int 4337ComputeFormat( 4338 Spinbox *sbPtr) /* Information about dial widget. */ 4339{ 4340 double maxValue, x; 4341 int mostSigDigit, numDigits, leastSigDigit, afterDecimal; 4342 int eDigits, fDigits; 4343 4344 /* 4345 * Compute the displacement from the decimal of the most significant digit 4346 * required for any number in the dial's range. 4347 */ 4348 4349 if (sbPtr->reqFormat) { 4350 sbPtr->valueFormat = sbPtr->reqFormat; 4351 return TCL_OK; 4352 } 4353 4354 maxValue = fabs(sbPtr->fromValue); 4355 x = fabs(sbPtr->toValue); 4356 if (x > maxValue) { 4357 maxValue = x; 4358 } 4359 if (maxValue == 0) { 4360 maxValue = 1; 4361 } 4362 mostSigDigit = (int) floor(log10(maxValue)); 4363 4364 if (fabs(sbPtr->increment) > MIN_DBL_VAL) { 4365 /* 4366 * A increment was specified, so use it. 4367 */ 4368 4369 leastSigDigit = (int) floor(log10(sbPtr->increment)); 4370 } else { 4371 leastSigDigit = 0; 4372 } 4373 numDigits = mostSigDigit - leastSigDigit + 1; 4374 if (numDigits < 1) { 4375 numDigits = 1; 4376 } 4377 4378 /* 4379 * Compute the number of characters required using "e" format and "f" 4380 * format, and then choose whichever one takes fewer characters. 4381 */ 4382 4383 eDigits = numDigits + 4; 4384 if (numDigits > 1) { 4385 eDigits++; /* Decimal point. */ 4386 } 4387 afterDecimal = numDigits - mostSigDigit - 1; 4388 if (afterDecimal < 0) { 4389 afterDecimal = 0; 4390 } 4391 fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal; 4392 if (afterDecimal > 0) { 4393 fDigits++; /* Decimal point. */ 4394 } 4395 if (mostSigDigit < 0) { 4396 fDigits++; /* Zero to left of decimal point. */ 4397 } 4398 if (fDigits <= eDigits) { 4399 sprintf(sbPtr->digitFormat, "%%.%df", afterDecimal); 4400 } else { 4401 sprintf(sbPtr->digitFormat, "%%.%de", numDigits-1); 4402 } 4403 sbPtr->valueFormat = sbPtr->digitFormat; 4404 return TCL_OK; 4405} 4406 4407/* 4408 * Local Variables: 4409 * mode: c 4410 * c-basic-offset: 4 4411 * fill-column: 78 4412 * End: 4413 */ 4414