diff options
author | patthoyts <patthoyts@users.sourceforge.net> | 2008-11-01 15:34:23 (GMT) |
---|---|---|
committer | patthoyts <patthoyts@users.sourceforge.net> | 2008-11-01 15:34:23 (GMT) |
commit | 1ed5704ca8775530ac5ef1ca6d648a4029112823 (patch) | |
tree | b7d15a510acb56835dde346dca064c75f8284acf /generic/ttk | |
parent | 6554e94184299faab586db5a555ecf119365b845 (diff) | |
download | tk-1ed5704ca8775530ac5ef1ca6d648a4029112823.zip tk-1ed5704ca8775530ac5ef1ca6d648a4029112823.tar.gz tk-1ed5704ca8775530ac5ef1ca6d648a4029112823.tar.bz2 |
Implemented the themed spinbox widget ttk::spinbox.
Diffstat (limited to 'generic/ttk')
-rw-r--r-- | generic/ttk/ttkEntry.c | 418 |
1 files changed, 350 insertions, 68 deletions
diff --git a/generic/ttk/ttkEntry.c b/generic/ttk/ttkEntry.c index d41010d..7397dfb 100644 --- a/generic/ttk/ttkEntry.c +++ b/generic/ttk/ttkEntry.c @@ -1,5 +1,5 @@ /* - * $Id: ttkEntry.c,v 1.9 2007/05/18 21:46:11 jenglish Exp $ + * $Id: ttkEntry.c,v 1.10 2008/11/01 15:34:24 patthoyts Exp $ * * DERIVED FROM: tk/generic/tkEntry.c r1.35. * @@ -13,6 +13,7 @@ #include <string.h> #include <tk.h> #include <X11/Xatom.h> +#include <math.h> #include "ttkTheme.h" #include "ttkWidget.h" @@ -26,6 +27,12 @@ #define VALIDATION_SET_VALUE (WIDGET_USER_FLAG<<4) /* + * inline equality test for doubles + */ +#define MIN_DBL_VAL 1E-9 +#define DOUBLES_EQ(d1, d2) (fabs((d1) - (d2)) < MIN_DBL_VAL) + +/* * Definitions for -validate option values: */ typedef enum validateMode { @@ -144,9 +151,12 @@ typedef struct /* * Extra mask bits for Tk_SetOptions() */ -#define STATE_CHANGED (0x100) /* -state option changed */ -#define TEXTVAR_CHANGED (0x200) /* -textvariable option changed */ -#define SCROLLCMD_CHANGED (0x400) /* -xscrollcommand option changed */ +#define STATE_CHANGED (0x0100) /* -state option changed */ +#define TEXTVAR_CHANGED (0x0200) /* -textvariable option changed */ +#define SCROLLCMD_CHANGED (0x0400) /* -xscrollcommand option changed */ +#define VALUES_CHANGED (0x0800) /* -values option changed */ +#define FORMAT_CHANGED (0x1000) /* -format option changed (spinbox) */ +#define RANGE_CHANGED (0x2000) /* -from or -to option changed */ /* * Default option values: @@ -1679,19 +1689,127 @@ static WidgetSpec EntryWidgetSpec = }; /*------------------------------------------------------------------------ + * +++ Values entry widget widget record. + * + * This record and the command function are shared by the combobox and + * spinbox which both have support for a -values option and [current] + * command. + */ + +typedef struct { + Tcl_Obj *valuesObj; + int currentIndex; +} ValuesPart; + +typedef struct { + WidgetCore core; + EntryPart entry; + ValuesPart values; +} Values; + +#define ENTRY_VALUES_OPTION \ + {TK_OPTION_STRING, "-values", "values", "Values", \ + "", Tk_Offset(Values, values.valuesObj), -1, 0, 0, VALUES_CHANGED} + +static int +ValuesInitialize(Tcl_Interp *interp, void *recordPtr) +{ + Values *valPtr = recordPtr; + valPtr->values.currentIndex = -1; + return TCL_OK; +} + +static int +ValuesValidate(Tcl_Interp *interp, void *recordPtr, int *indexPtr) +{ + Values *valPtr = recordPtr; + int currentIndex = valPtr->values.currentIndex; + const char *currentValue = valPtr->entry.string; + int eltc; + Tcl_Obj **eltv; + + + if (Tcl_ListObjLength(interp,valPtr->values.valuesObj,&eltc) != TCL_OK) + return TCL_ERROR; + + Tcl_ListObjGetElements(interp,valPtr->values.valuesObj,&eltc,&eltv); + + if ( currentIndex < 0 + || currentIndex >= eltc + || strcmp(currentValue, Tcl_GetString(eltv[currentIndex]))) { + /* + * Not valid. Check current value against each element in -values: + */ + for (currentIndex = 0; currentIndex < eltc; ++currentIndex) { + if (!strcmp(currentValue,Tcl_GetString(eltv[currentIndex]))) { + break; + } + } + if (currentIndex >= eltc) { + /* Not found */ + currentIndex = -1; + } + } + *indexPtr = currentIndex; + return TCL_OK; +} + +/* $widget current ?newIndex? -- get or set current index. + * Setting the current index updates the combobox value, + * but the value and -values may be changed independently + * of the index. Instead of trying to keep currentIndex + * in sync at all times, [$cb current] double-checks + */ +static int ValuesCurrentCommand( + Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) +{ + Values *valPtr = recordPtr; + int currentIndex = valPtr->values.currentIndex; + const char *currentValue = valPtr->entry.string; + int nValues; + Tcl_Obj **values; + + Tcl_ListObjGetElements(interp,valPtr->values.valuesObj,&nValues,&values); + + if (objc == 2) { + if (ValuesValidate(interp, recordPtr, ¤tIndex) != TCL_OK) + return TCL_ERROR; + valPtr->values.currentIndex = currentIndex; + Tcl_SetObjResult(interp, Tcl_NewIntObj(currentIndex)); + } else if (objc == 3) { + if (Tcl_GetIntFromObj(interp, objv[2], ¤tIndex) != TCL_OK) { + return TCL_ERROR; + } + if (currentIndex < 0 || currentIndex >= nValues) { + Tcl_AppendResult(interp, + "Index ", Tcl_GetString(objv[2]), " out of range", + NULL); + return TCL_ERROR; + } + + valPtr->values.currentIndex = currentIndex; + + return EntrySetValue(recordPtr, Tcl_GetString(values[currentIndex])); + } else { + Tcl_WrongNumArgs(interp, 2, objv, "?newIndex?"); + return TCL_ERROR; + } + return TCL_OK; +} + +/*------------------------------------------------------------------------ * +++ Combobox widget record. */ typedef struct { Tcl_Obj *postCommandObj; - Tcl_Obj *valuesObj; Tcl_Obj *heightObj; - int currentIndex; } ComboboxPart; typedef struct { WidgetCore core; EntryPart entry; + ValuesPart values; ComboboxPart combobox; } Combobox; @@ -1703,9 +1821,7 @@ static Tk_OptionSpec ComboboxOptionSpecs[] = {TK_OPTION_STRING, "-postcommand", "postCommand", "PostCommand", "", Tk_Offset(Combobox, combobox.postCommandObj), -1, 0,0,0 }, - {TK_OPTION_STRING, "-values", "values", "Values", - "", Tk_Offset(Combobox, combobox.valuesObj), -1, - 0,0,0 }, + ENTRY_VALUES_OPTION, WIDGET_INHERIT_OPTIONS(EntryOptionSpecs) }; @@ -1716,8 +1832,8 @@ static int ComboboxInitialize(Tcl_Interp *interp, void *recordPtr) { Combobox *cb = recordPtr; - cb->combobox.currentIndex = -1; TtkTrackElementState(&cb->core); + ValuesInitialize(interp, recordPtr); return EntryInitialize(interp, recordPtr); } @@ -1728,86 +1844,241 @@ static int ComboboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask) { Combobox *cbPtr = recordPtr; - int unused; + int currentIndex = 0; /* Make sure -values is a valid list: */ - if (Tcl_ListObjLength(interp,cbPtr->combobox.valuesObj,&unused) != TCL_OK) - return TCL_ERROR; + if (mask & VALUES_CHANGED) { + if (ValuesValidate(interp, recordPtr, ¤tIndex) != TCL_OK) + return TCL_ERROR; + cbPtr->values.currentIndex = currentIndex; + } return EntryConfigure(interp, recordPtr, mask); } -/* $cb current ?newIndex? -- get or set current index. - * Setting the current index updates the combobox value, - * but the value and -values may be changed independently - * of the index. Instead of trying to keep currentIndex - * in sync at all times, [$cb current] double-checks +/*------------------------------------------------------------------------ + * +++ Combobox widget definition. */ -static int ComboboxCurrentCommand( - Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) +static WidgetCommandSpec ComboboxCommands[] = { - Combobox *cbPtr = recordPtr; - int currentIndex = cbPtr->combobox.currentIndex; - const char *currentValue = cbPtr->entry.string; - int nValues; - Tcl_Obj **values; + { "bbox", EntryBBoxCommand }, + { "cget", TtkWidgetCgetCommand }, + { "configure", TtkWidgetConfigureCommand }, + { "current", ValuesCurrentCommand }, + { "delete", EntryDeleteCommand }, + { "get", EntryGetCommand }, + { "icursor", EntryICursorCommand }, + { "identify", TtkWidgetIdentifyCommand }, + { "index", EntryIndexCommand }, + { "insert", EntryInsertCommand }, + { "instate", TtkWidgetInstateCommand }, + { "selection", EntrySelectionCommand }, + { "state", TtkWidgetStateCommand }, + { "set", EntrySetCommand }, + { "xview", EntryXViewCommand }, + {0,0} +}; - Tcl_ListObjGetElements(interp,cbPtr->combobox.valuesObj,&nValues,&values); +static WidgetSpec ComboboxWidgetSpec = +{ + "TCombobox", /* className */ + sizeof(Combobox), /* recordSize */ + ComboboxOptionSpecs, /* optionSpecs */ + ComboboxCommands, /* subcommands */ + ComboboxInitialize, /* initializeProc */ + EntryCleanup, /* cleanupProc */ + ComboboxConfigure, /* configureProc */ + EntryPostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + EntryDoLayout, /* layoutProc */ + EntryDisplay /* displayProc */ +}; - if (objc == 2) { - /* Check if currentIndex still valid: - */ - if ( currentIndex < 0 - || currentIndex >= nValues - || strcmp(currentValue,Tcl_GetString(values[currentIndex])) - ) - { - /* Not valid. Check current value against each element in -values: - */ - for (currentIndex = 0; currentIndex < nValues; ++currentIndex) { - if (!strcmp(currentValue,Tcl_GetString(values[currentIndex]))) { - break; - } +/*------------------------------------------------------------------------ + * +++ Spinbox widget record. + */ + +typedef struct { + Tcl_Obj *commandObj; + double fromValue; + double toValue; + int valueCount; + Tcl_Obj *formatObj; + Tcl_Obj *incrementObj; + Tcl_Obj *wrapObj; + char formatBuffer[TCL_INTEGER_SPACE + 4]; +} SpinboxPart; + +typedef struct { + WidgetCore core; + EntryPart entry; + ValuesPart values; + SpinboxPart spinbox; +} Spinbox; + +static Tk_OptionSpec SpinboxOptionSpecs[] = +{ + {TK_OPTION_STRING, "-command", "command", "Command", + "", Tk_Offset(Spinbox, spinbox.commandObj), -1, + 0,0,0 }, + {TK_OPTION_STRING, "-format", "format", "Format", "", + Tk_Offset(Spinbox, spinbox.formatObj), -1, 0, 0, FORMAT_CHANGED }, + {TK_OPTION_DOUBLE, "-from", "from", "From", "0", + -1, Tk_Offset(Spinbox,spinbox.fromValue), 0, 0, RANGE_CHANGED }, + {TK_OPTION_DOUBLE, "-to", "to", "To", "0", + -1, Tk_Offset(Spinbox,spinbox.toValue), 0, 0, RANGE_CHANGED }, + {TK_OPTION_DOUBLE, "-increment", "increment", "Increment", "1", + Tk_Offset(Spinbox,spinbox.incrementObj), -1, 0, 0, 0}, + {TK_OPTION_BOOLEAN, "-wrap", "wrap", "Wrap", "0", + Tk_Offset(Spinbox,spinbox.wrapObj), -1, 0, 0, 0}, + ENTRY_VALUES_OPTION, + WIDGET_INHERIT_OPTIONS(EntryOptionSpecs) +}; + +/* + * SpinboxInitialize -- + * Initialization hook for spinbox widgets. + */ + +static int +SpinboxInitialize(Tcl_Interp *interp, void *recordPtr) +{ + Spinbox *sbPtr = recordPtr; + sbPtr->spinbox.valueCount = 0; + TtkTrackElementState(&sbPtr->core); + ValuesInitialize(interp, recordPtr); + return EntryInitialize(interp, recordPtr); +} + +/* + * If the format option has not been set manually then we calculate + * an appropriate format here based upon the -from and -to options + * If -values has been set this is not called. + */ + +static const char * +SpinboxCalculateFormat(Spinbox *sbPtr) +{ + const char *formatString = "%f"; + if (Tcl_GetCharLength(sbPtr->spinbox.formatObj) > 0) { + formatString = Tcl_GetString(sbPtr->spinbox.formatObj); + } else { + double increment = 0; + int len; + if (Tcl_GetDoubleFromObj(NULL, sbPtr->spinbox.incrementObj, &increment) != TCL_OK) + increment = 1; + len = (int)floor(fabs(log10(fabs(increment)))); + sprintf(sbPtr->spinbox.formatBuffer, "%%.%df", len); + formatString = sbPtr->spinbox.formatBuffer; + } + return formatString; +} + +/* SpinboxConfigure -- + * Configuration hook for spinbox widgets. + */ +static int +SpinboxConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Spinbox *sbPtr = recordPtr; + int currentIndex = 0, needsUpdate = 0; + double d = 0; + + needsUpdate = (mask & FORMAT_CHANGED); + + if (mask & RANGE_CHANGED) { + if (Tcl_GetDouble(NULL, sbPtr->entry.string, &d) != TCL_OK) { + d = sbPtr->spinbox.fromValue; + needsUpdate = 1; + } else { + if (d < sbPtr->spinbox.fromValue) { + d = sbPtr->spinbox.fromValue; + needsUpdate = 1; } - if (currentIndex >= nValues) { - /* Not found */ - currentIndex = -1; + if (d > sbPtr->spinbox.toValue) { + d = sbPtr->spinbox.toValue; + needsUpdate = 1; } } - cbPtr->combobox.currentIndex = currentIndex; - Tcl_SetObjResult(interp, Tcl_NewIntObj(currentIndex)); - return TCL_OK; - } else if (objc == 3) { - if (Tcl_GetIntFromObj(interp, objv[2], ¤tIndex) != TCL_OK) { + } + + /* Make sure -values is a valid list: + */ + if (mask & VALUES_CHANGED) { + if (ValuesValidate(interp, recordPtr, ¤tIndex) != TCL_OK) return TCL_ERROR; - } - if (currentIndex < 0 || currentIndex >= nValues) { - Tcl_AppendResult(interp, - "Index ", Tcl_GetString(objv[2]), " out of range", - NULL); + if (Tcl_ListObjLength(interp,sbPtr->values.valuesObj, + &sbPtr->spinbox.valueCount) != TCL_OK) return TCL_ERROR; + if (sbPtr->spinbox.valueCount > 0) { + if (sbPtr->values.currentIndex == -1 + || sbPtr->values.currentIndex != currentIndex) { + Tcl_Obj *valueObj; + if (currentIndex == -1) currentIndex = 0; + Tcl_ListObjIndex(interp, sbPtr->values.valuesObj, + currentIndex, &valueObj); + EntrySetValue(recordPtr, Tcl_GetString(valueObj)); + } + sbPtr->values.currentIndex = currentIndex; } + } - cbPtr->combobox.currentIndex = currentIndex; + if (needsUpdate && sbPtr->spinbox.valueCount < 1) { + Tcl_Obj *strObj, *valueObj = NULL; + const char *formatString = SpinboxCalculateFormat(sbPtr); + strObj = Tcl_NewDoubleObj(d); + valueObj = Tcl_Format(interp, formatString, 1, &strObj); + if (valueObj) { + Tcl_IncrRefCount(valueObj); + EntrySetValue(recordPtr, Tcl_GetString(valueObj)); + Tcl_DecrRefCount(valueObj); + } + } + + return EntryConfigure(interp, recordPtr, mask); +} - return EntrySetValue(recordPtr, Tcl_GetString(values[currentIndex])); - } else { - Tcl_WrongNumArgs(interp, 2, objv, "?newIndex?"); +/* $spinbox set $value + * Sets the value of a spinbox widget. + * We need to take account of any -format option. + */ +static int SpinboxSetCommand( + Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], void *recordPtr) +{ + Spinbox *sbPtr = recordPtr; + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "value"); return TCL_ERROR; } + if (sbPtr->spinbox.valueCount < 1) { + Tcl_Obj *valueObj = NULL; + const char *formatString = SpinboxCalculateFormat(sbPtr); + valueObj = Tcl_Format(interp, formatString, 1, &objv[2]); + if (valueObj) { + Tcl_IncrRefCount(valueObj); + EntrySetValue(recordPtr, Tcl_GetString(valueObj)); + Tcl_DecrRefCount(valueObj); + } else { + EntrySetValue(recordPtr, Tcl_GetString(objv[2])); + } + } else { + EntrySetValue(recordPtr, Tcl_GetString(objv[2])); + } return TCL_OK; } /*------------------------------------------------------------------------ - * +++ Combobox widget definition. + * +++ Spinbox widget definition. */ -static WidgetCommandSpec ComboboxCommands[] = + +static WidgetCommandSpec SpinboxCommands[] = { { "bbox", EntryBBoxCommand }, { "cget", TtkWidgetCgetCommand }, { "configure", TtkWidgetConfigureCommand }, - { "current", ComboboxCurrentCommand }, + { "current", ValuesCurrentCommand }, { "delete", EntryDeleteCommand }, { "get", EntryGetCommand }, { "icursor", EntryICursorCommand }, @@ -1817,20 +2088,21 @@ static WidgetCommandSpec ComboboxCommands[] = { "instate", TtkWidgetInstateCommand }, { "selection", EntrySelectionCommand }, { "state", TtkWidgetStateCommand }, - { "set", EntrySetCommand }, + { "set", SpinboxSetCommand }, + { "validate", EntryValidateCommand }, { "xview", EntryXViewCommand }, {0,0} }; -static WidgetSpec ComboboxWidgetSpec = +static WidgetSpec SpinboxWidgetSpec = { - "TCombobox", /* className */ - sizeof(Combobox), /* recordSize */ - ComboboxOptionSpecs, /* optionSpecs */ - ComboboxCommands, /* subcommands */ - ComboboxInitialize, /* initializeProc */ + "TSpinbox", /* className */ + sizeof(Spinbox), /* recordSize */ + SpinboxOptionSpecs, /* optionSpecs */ + SpinboxCommands, /* subcommands */ + SpinboxInitialize, /* initializeProc */ EntryCleanup, /* cleanupProc */ - ComboboxConfigure, /* configureProc */ + SpinboxConfigure, /* configureProc */ EntryPostConfigure, /* postConfigureProc */ TtkWidgetGetLayout, /* getLayoutProc */ TtkWidgetSize, /* sizeProc */ @@ -1902,6 +2174,14 @@ TTK_BEGIN_LAYOUT(ComboboxLayout) TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))) TTK_END_LAYOUT +TTK_BEGIN_LAYOUT(SpinboxLayout) + TTK_GROUP("Spinbox.field", TTK_FILL_BOTH, + TTK_GROUP("Spinbox.padding", TTK_FILL_BOTH, + TTK_NODE("Spinbox.textarea", TTK_PACK_LEFT|TTK_EXPAND)) + TTK_NODE("Spinbox.uparrow", TTK_PACK_TOP|TTK_STICK_E) + TTK_NODE("Spinbox.downarrow", TTK_PACK_BOTTOM|TTK_STICK_E)) +TTK_END_LAYOUT + /*------------------------------------------------------------------------ * +++ Initialization. */ @@ -1915,9 +2195,11 @@ void TtkEntry_Init(Tcl_Interp *interp) Ttk_RegisterLayout(themePtr, "TEntry", EntryLayout); Ttk_RegisterLayout(themePtr, "TCombobox", ComboboxLayout); + Ttk_RegisterLayout(themePtr, "TSpinbox", SpinboxLayout); RegisterWidget(interp, "ttk::entry", &EntryWidgetSpec); RegisterWidget(interp, "ttk::combobox", &ComboboxWidgetSpec); + RegisterWidget(interp, "ttk::spinbox", &SpinboxWidgetSpec); } /*EOF*/ |