summaryrefslogtreecommitdiffstats
path: root/generic/ttk
diff options
context:
space:
mode:
authorpatthoyts <patthoyts@users.sourceforge.net>2008-11-01 15:34:23 (GMT)
committerpatthoyts <patthoyts@users.sourceforge.net>2008-11-01 15:34:23 (GMT)
commit1ed5704ca8775530ac5ef1ca6d648a4029112823 (patch)
treeb7d15a510acb56835dde346dca064c75f8284acf /generic/ttk
parent6554e94184299faab586db5a555ecf119365b845 (diff)
downloadtk-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.c418
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, &currentIndex) != 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], &currentIndex) != 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, &currentIndex) != 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], &currentIndex) != TCL_OK) {
+ }
+
+ /* Make sure -values is a valid list:
+ */
+ if (mask & VALUES_CHANGED) {
+ if (ValuesValidate(interp, recordPtr, &currentIndex) != 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*/