diff options
author | jenglish <jenglish@flightlab.com> | 2011-08-16 17:03:20 (GMT) |
---|---|---|
committer | jenglish <jenglish@flightlab.com> | 2011-08-16 17:03:20 (GMT) |
commit | 9a046cd669466105bb9bd4d137b8b227ff958448 (patch) | |
tree | 05b9d1df2025423ae5e6126bdb4947449db7a3fb /generic | |
parent | dff96aa27cdba340f38ae9fe05fd4ebb46f767c6 (diff) | |
download | tk-ttk_range_widget.zip tk-ttk_range_widget.tar.gz tk-ttk_range_widget.tar.bz2 |
ttk::range widget implementation, contributed by Goodwin Lawlor [SF#3391905]ttk_range_widget
Diffstat (limited to 'generic')
-rw-r--r-- | generic/ttk/ttkClamTheme.c | 2 | ||||
-rw-r--r-- | generic/ttk/ttkDefaultTheme.c | 2 | ||||
-rw-r--r-- | generic/ttk/ttkElements.c | 2 | ||||
-rw-r--r-- | generic/ttk/ttkInit.c | 2 | ||||
-rw-r--r-- | generic/ttk/ttkRange.c | 762 |
5 files changed, 770 insertions, 0 deletions
diff --git a/generic/ttk/ttkClamTheme.c b/generic/ttk/ttkClamTheme.c index 572f630..c182f01 100644 --- a/generic/ttk/ttkClamTheme.c +++ b/generic/ttk/ttkClamTheme.c @@ -955,6 +955,8 @@ TtkClamTheme_Init(Tcl_Interp *interp) Ttk_RegisterElement(interp, theme, "client", &ClientElementSpec, NULL); Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "minslider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "maxslider", &SliderElementSpec, NULL); Ttk_RegisterElement(interp, theme, "bar", &PbarElementSpec, NULL); Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL); diff --git a/generic/ttk/ttkDefaultTheme.c b/generic/ttk/ttkDefaultTheme.c index d2deee8..850a476 100644 --- a/generic/ttk/ttkDefaultTheme.c +++ b/generic/ttk/ttkDefaultTheme.c @@ -1104,6 +1104,8 @@ MODULE_SCOPE int TtkAltTheme_Init(Tcl_Interp *interp) Ttk_RegisterElement(interp, theme, "trough", &TroughElementSpec, NULL); Ttk_RegisterElement(interp, theme, "thumb", &ThumbElementSpec, NULL); Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "minslider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "maxslider", &SliderElementSpec, NULL); Ttk_RegisterElement(interp, theme, "uparrow", &ArrowElementSpec, &ArrowElements[0]); diff --git a/generic/ttk/ttkElements.c b/generic/ttk/ttkElements.c index 22af1d6..7d0497a 100644 --- a/generic/ttk/ttkElements.c +++ b/generic/ttk/ttkElements.c @@ -1258,6 +1258,8 @@ void TtkElements_Init(Tcl_Interp *interp) Ttk_RegisterElement(interp, theme, "trough", &TroughElementSpec, NULL); Ttk_RegisterElement(interp, theme, "thumb", &ThumbElementSpec, NULL); Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "minslider", &SliderElementSpec, NULL); + Ttk_RegisterElement(interp, theme, "maxslider", &SliderElementSpec, NULL); Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL); Ttk_RegisterElement(interp, theme, "separator", diff --git a/generic/ttk/ttkInit.c b/generic/ttk/ttkInit.c index be30ee7..59d8705 100644 --- a/generic/ttk/ttkInit.c +++ b/generic/ttk/ttkInit.c @@ -213,6 +213,7 @@ extern void TtkFrame_Init(Tcl_Interp *); extern void TtkNotebook_Init(Tcl_Interp *); extern void TtkPanedwindow_Init(Tcl_Interp *); extern void TtkProgressbar_Init(Tcl_Interp *); +extern void TtkRange_Init(Tcl_Interp *); extern void TtkScale_Init(Tcl_Interp *); extern void TtkScrollbar_Init(Tcl_Interp *); extern void TtkSeparator_Init(Tcl_Interp *); @@ -230,6 +231,7 @@ static void RegisterWidgets(Tcl_Interp *interp) TtkNotebook_Init(interp); TtkPanedwindow_Init(interp); TtkProgressbar_Init(interp); + TtkRange_Init(interp); TtkScale_Init(interp); TtkScrollbar_Init(interp); TtkSeparator_Init(interp); diff --git a/generic/ttk/ttkRange.c b/generic/ttk/ttkRange.c new file mode 100644 index 0000000..618b432 --- /dev/null +++ b/generic/ttk/ttkRange.c @@ -0,0 +1,762 @@ +/* + * Copyright (C) 2011 Goodwin Lawlor goodwin.lawlor@gmail.com + * + * ttk::range widget. + */ + +#include <tk.h> +#include <string.h> +#include <stdio.h> +#include "ttkTheme.h" +#include "ttkWidget.h" + +#define DEF_RANGE_LENGTH "100" + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +/* + * Range widget record + */ +typedef struct +{ + /* slider element options */ + Tcl_Obj *fromObj; /* from value - lower bound*/ + Tcl_Obj *toObj; /* to value - upper bound*/ + Tcl_Obj *minvalueObj; /* min value */ + Tcl_Obj *maxvalueObj; /* max value */ + Tcl_Obj *lengthObj; /* length of the long axis of the range */ + Tcl_Obj *orientObj; /* widget orientation */ + int orient; + + /* widget options */ + Tcl_Obj *commandObj; + Tcl_Obj *minvariableObj; + Tcl_Obj *maxvariableObj; + + /* internal state */ + Ttk_TraceHandle *minvariableTrace; + Ttk_TraceHandle *maxvariableTrace; + +} RangePart; + +typedef struct +{ + WidgetCore core; + RangePart range; +} Range; + +static Tk_OptionSpec RangeOptionSpecs[] = +{ + WIDGET_TAKES_FOCUS, + + {TK_OPTION_STRING, "-command", "command", "Command", "", + Tk_Offset(Range,range.commandObj), -1, + TK_OPTION_NULL_OK,0,0}, + {TK_OPTION_STRING, "-minvariable", "minvariable", "Minvariable", "", + Tk_Offset(Range,range.minvariableObj), -1, + 0,0,0}, + {TK_OPTION_STRING, "-maxvariable", "maxvariable", "Maxvariable", "", + Tk_Offset(Range,range.maxvariableObj), -1, + 0,0,0}, + {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient", "horizontal", + Tk_Offset(Range,range.orientObj), + Tk_Offset(Range,range.orient), 0, + (ClientData)ttkOrientStrings, STYLE_CHANGED }, + + {TK_OPTION_DOUBLE, "-from", "from", "From", "0", + Tk_Offset(Range,range.fromObj), -1, 0, 0, 0}, + {TK_OPTION_DOUBLE, "-to", "to", "To", "1.0", + Tk_Offset(Range,range.toObj), -1, 0, 0, 0}, + {TK_OPTION_DOUBLE, "-minvalue", "minvalue", "Minvalue", "0", + Tk_Offset(Range,range.minvalueObj), -1, 0, 0, 0}, + {TK_OPTION_DOUBLE, "-maxvalue", "maxvalue", "Maxvalue", "1.0", + Tk_Offset(Range,range.maxvalueObj), -1, 0, 0, 0}, + {TK_OPTION_PIXELS, "-length", "length", "Length", + DEF_RANGE_LENGTH, Tk_Offset(Range,range.lengthObj), -1, 0, 0, + GEOMETRY_CHANGED}, + + WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs) +}; + +static XPoint ValueToPoint(Range *rangePtr, double value); +static double PointToValue(Range *rangePtr, int x, int y); + +/* RangeMinVariableChanged -- + * Variable trace procedure for range -variable; + * Updates the range's value. + * If the linked variable is not a valid double, + * sets the 'invalid' state. + */ +static void RangeMinVariableChanged(void *recordPtr, const char *minvalue) +{ + Range *range = recordPtr; + double v; + + if (minvalue == NULL || Tcl_GetDouble(0, minvalue, &v) != TCL_OK) { + TtkWidgetChangeState(&range->core, TTK_STATE_INVALID, 0); + } else { + Tcl_Obj *minvalueObj = Tcl_NewDoubleObj(v); + Tcl_IncrRefCount(minvalueObj); + Tcl_DecrRefCount(range->range.minvalueObj); + range->range.minvalueObj = minvalueObj; + TtkWidgetChangeState(&range->core, 0, TTK_STATE_INVALID); + } + TtkRedisplayWidget(&range->core); +} + +/* RangeMaxVariableChanged -- + * Variable trace procedure for range -variable; + * Updates the range's value. + * If the linked variable is not a valid double, + * sets the 'invalid' state. + */ +static void RangeMaxVariableChanged(void *recordPtr, const char *maxvalue) +{ + Range *range = recordPtr; + double v; + + if (maxvalue == NULL || Tcl_GetDouble(0, maxvalue, &v) != TCL_OK) { + TtkWidgetChangeState(&range->core, TTK_STATE_INVALID, 0); + } else { + Tcl_Obj *maxvalueObj = Tcl_NewDoubleObj(v); + Tcl_IncrRefCount(maxvalueObj); + Tcl_DecrRefCount(range->range.maxvalueObj); + range->range.maxvalueObj = maxvalueObj; + TtkWidgetChangeState(&range->core, 0, TTK_STATE_INVALID); + } + TtkRedisplayWidget(&range->core); +} + +/* RangeInitialize -- + * Range widget initialization hook. + */ +static void RangeInitialize(Tcl_Interp *interp, void *recordPtr) +{ + Range *rangePtr = recordPtr; + TtkTrackElementState(&rangePtr->core); + +} + +static void RangeCleanup(void *recordPtr) +{ + Range *range = recordPtr; + + if (range->range.minvariableTrace) { + Ttk_UntraceVariable(range->range.minvariableTrace); + range->range.minvariableTrace = 0; + } + + if (range->range.maxvariableTrace) { + Ttk_UntraceVariable(range->range.maxvariableTrace); + range->range.maxvariableTrace = 0; + } +} + +/* RangeConfigure -- + * Configuration hook. + */ +static int RangeConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Range *range = recordPtr; + Tcl_Obj *minvarName = range->range.minvariableObj; + Tcl_Obj *maxvarName = range->range.maxvariableObj; + Ttk_TraceHandle *minvt = 0; + Ttk_TraceHandle *maxvt = 0; + + if (minvarName != NULL && *Tcl_GetString(minvarName) != '\0') { + minvt = Ttk_TraceVariable(interp,minvarName, RangeMinVariableChanged,recordPtr); + if (!minvt) return TCL_ERROR; + } + + if (maxvarName != NULL && *Tcl_GetString(maxvarName) != '\0') { + maxvt = Ttk_TraceVariable(interp,maxvarName, RangeMaxVariableChanged,recordPtr); + if (!maxvt) return TCL_ERROR; + } + + if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) { + if (minvt) Ttk_UntraceVariable(minvt); + if (maxvt) Ttk_UntraceVariable(maxvt); + return TCL_ERROR; + } + + if (range->range.minvariableTrace) { + Ttk_UntraceVariable(range->range.minvariableTrace); + } + range->range.minvariableTrace = minvt; + + if (range->range.maxvariableTrace) { + Ttk_UntraceVariable(range->range.maxvariableTrace); + } + range->range.maxvariableTrace = maxvt; + + return TCL_OK; +} + +/* RangePostConfigure -- + * Post-configuration hook. + */ +static int RangePostConfigure( + Tcl_Interp *interp, void *recordPtr, int mask) +{ + Range *range = recordPtr; + int minstatus = TCL_OK; + int maxstatus = TCL_OK; + + if (range->range.minvariableTrace) { + minstatus = Ttk_FireTrace(range->range.minvariableTrace); + if (WidgetDestroyed(&range->core)) { + return TCL_ERROR; + } + if (minstatus != TCL_OK) { + /* Unset -variable: */ + Ttk_UntraceVariable(range->range.minvariableTrace); + Tcl_DecrRefCount(range->range.minvariableObj); + range->range.minvariableTrace = 0; + range->range.minvariableObj = NULL; + minstatus = TCL_ERROR; + } + } + + if (range->range.maxvariableTrace) { + maxstatus = Ttk_FireTrace(range->range.maxvariableTrace); + if (WidgetDestroyed(&range->core)) { + return TCL_ERROR; + } + if (maxstatus != TCL_OK) { + /* Unset -variable: */ + Ttk_UntraceVariable(range->range.maxvariableTrace); + Tcl_DecrRefCount(range->range.maxvariableObj); + range->range.maxvariableTrace = 0; + range->range.maxvariableObj = NULL; + maxstatus = TCL_ERROR; + } + } + if (minstatus != TCL_OK || maxstatus != TCL_OK) { + return TCL_ERROR; + } else { + return TCL_OK; + } + +} + +/* RangeGetLayout -- + * getLayout hook. + */ +static Ttk_Layout +RangeGetLayout(Tcl_Interp *interp, Ttk_Theme theme, void *recordPtr) +{ + Range *rangePtr = recordPtr; + return TtkWidgetGetOrientedLayout( + interp, theme, recordPtr, rangePtr->range.orientObj); +} + +/* + * TroughBox -- + * Returns the inner area of the trough element. + */ +static Ttk_Box TroughBox(Range *rangePtr) +{ + return Ttk_ClientRegion(rangePtr->core.layout, "trough"); +} + +/* + * TroughRange -- + * Return the value area of the trough element, adjusted + * for slider size. + */ +static Ttk_Box TroughRange(Range *rangePtr) +{ + Ttk_Box troughBox = TroughBox(rangePtr); + Ttk_Element slider = Ttk_FindElement(rangePtr->core.layout,"minslider"); + + /* + * If this is a range widget, adjust range for slider: + */ + if (slider) { + Ttk_Box sliderBox = Ttk_ElementParcel(slider); + if (rangePtr->range.orient == TTK_ORIENT_HORIZONTAL) { + troughBox.x += sliderBox.width / 2; + troughBox.width -= sliderBox.width; + } else { + troughBox.y += sliderBox.height / 2; + troughBox.height -= sliderBox.height; + } + } + + return troughBox; +} + +/* + * RangeFraction -- + */ +static double RangeFraction(Range *rangePtr, double value) +{ + double from = 0, to = 1, fraction; + + Tcl_GetDoubleFromObj(NULL, rangePtr->range.fromObj, &from); + Tcl_GetDoubleFromObj(NULL, rangePtr->range.toObj, &to); + + if (from == to) { + return 1.0; + } + + fraction = (value - from) / (to - from); + + return fraction < 0 ? 0 : fraction > 1 ? 1 : fraction; +} + +/* $range get ?x y? -- + * Returns the current value of the range widget, or if $x and + * $y are specified, the value represented by point @x,y. + */ +static int +RangeGetCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + int x, y, r = TCL_OK; + double value = 0; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "get ?x y?"); + return TCL_ERROR; + } + + r = Tcl_GetIntFromObj(interp, objv[2], &x); + if (r == TCL_OK) + r = Tcl_GetIntFromObj(interp, objv[3], &y); + if (r == TCL_OK) { + value = PointToValue(rangePtr, x, y); + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(value)); + } + + return r; +} + +static int +RangeGetMinCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "getmin"); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, rangePtr->range.minvalueObj); + + return TCL_OK; +} + +static int +RangeGetMaxCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "getmax"); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, rangePtr->range.maxvalueObj); + + return TCL_OK; +} + +/* $range setmin $newValue + */ +static int +RangeSetMinCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + double from = 0.0, to = 1.0, minvalue, maxvalue; + int result = TCL_OK; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "setmin value"); + return TCL_ERROR; + } + + if (Tcl_GetDoubleFromObj(interp, objv[2], &minvalue) != TCL_OK) { + return TCL_ERROR; + } + + if (rangePtr->core.state & TTK_STATE_DISABLED) { + return TCL_OK; + } + + /* ASSERT: fromObj and toObj are valid doubles. + */ + Tcl_GetDoubleFromObj(interp, rangePtr->range.fromObj, &from); + Tcl_GetDoubleFromObj(interp, rangePtr->range.toObj, &to); + + /* Limit new value to between 'from' and 'to': + */ + if (from < to) { + minvalue = minvalue < from ? from : minvalue > to ? to : minvalue; + } else { + minvalue = minvalue < to ? to : minvalue > from ? from : minvalue; + } + + // Must be <= maxvalue too. + Tcl_GetDoubleFromObj(NULL, rangePtr->range.maxvalueObj, &maxvalue); + minvalue = minvalue < maxvalue ? minvalue : maxvalue; + + /* + * Set value: + */ + Tcl_DecrRefCount(rangePtr->range.minvalueObj); + rangePtr->range.minvalueObj = Tcl_NewDoubleObj(minvalue); + Tcl_IncrRefCount(rangePtr->range.minvalueObj); + TtkRedisplayWidget(&rangePtr->core); + + /* + * Set attached variable, if any: + */ + if (rangePtr->range.minvariableObj != NULL) { + Tcl_ObjSetVar2(interp, rangePtr->range.minvariableObj, NULL, + rangePtr->range.minvalueObj, TCL_GLOBAL_ONLY); + } + if (WidgetDestroyed(&rangePtr->core)) { + return TCL_ERROR; + } + + /* + * Invoke -command, if any: + */ + if (rangePtr->range.commandObj != NULL) { + Tcl_Obj *cmdObj = Tcl_DuplicateObj(rangePtr->range.commandObj); + Tcl_IncrRefCount(cmdObj); + Tcl_AppendToObj(cmdObj, " ", 1); + Tcl_AppendObjToObj(cmdObj, rangePtr->range.minvalueObj); + Tcl_AppendToObj(cmdObj, " ", 1); + Tcl_AppendObjToObj(cmdObj, rangePtr->range.maxvalueObj); + result = Tcl_EvalObjEx(interp, cmdObj, TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(cmdObj); + } + + return result; +} + +/* $range setmax $newValue + */ +static int +RangeSetMaxCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + double from = 0.0, to = 1.0, minvalue, maxvalue; + int result = TCL_OK; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "setmax value"); + return TCL_ERROR; + } + + if (Tcl_GetDoubleFromObj(interp, objv[2], &maxvalue) != TCL_OK) { + return TCL_ERROR; + } + + if (rangePtr->core.state & TTK_STATE_DISABLED) { + return TCL_OK; + } + + /* ASSERT: fromObj and toObj are valid doubles. + */ + Tcl_GetDoubleFromObj(interp, rangePtr->range.fromObj, &from); + Tcl_GetDoubleFromObj(interp, rangePtr->range.toObj, &to); + + /* Limit new value to between 'from' and 'to': + */ + if (from < to) { + maxvalue = maxvalue < from ? from : maxvalue > to ? to : maxvalue; + } else { + maxvalue = maxvalue < to ? to : maxvalue > from ? from : maxvalue; + } + + // Must be >= minvalue too. + Tcl_GetDoubleFromObj(NULL, rangePtr->range.minvalueObj, &minvalue); + maxvalue = maxvalue > minvalue ? maxvalue : minvalue; + + /* + * Set value: + */ + Tcl_DecrRefCount(rangePtr->range.maxvalueObj); + rangePtr->range.maxvalueObj = Tcl_NewDoubleObj(maxvalue); + Tcl_IncrRefCount(rangePtr->range.maxvalueObj); + TtkRedisplayWidget(&rangePtr->core); + + /* + * Set attached variable, if any: + */ + if (rangePtr->range.maxvariableObj != NULL) { + Tcl_ObjSetVar2(interp, rangePtr->range.maxvariableObj, NULL, + rangePtr->range.maxvalueObj, TCL_GLOBAL_ONLY); + } + if (WidgetDestroyed(&rangePtr->core)) { + return TCL_ERROR; + } + + /* + * Invoke -command, if any: + */ + if (rangePtr->range.commandObj != NULL) { + Tcl_Obj *cmdObj = Tcl_DuplicateObj(rangePtr->range.commandObj); + Tcl_IncrRefCount(cmdObj); + Tcl_AppendToObj(cmdObj, " ", 1); + Tcl_AppendObjToObj(cmdObj, rangePtr->range.minvalueObj); + Tcl_AppendToObj(cmdObj, " ", 1); + Tcl_AppendObjToObj(cmdObj, rangePtr->range.maxvalueObj); + result = Tcl_EvalObjEx(interp, cmdObj, TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(cmdObj); + } + + return result; +} + +static int +RangeCoordsCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + double value; + int r = TCL_OK; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "coords ?value?"); + return TCL_ERROR; + } + + r = Tcl_GetDoubleFromObj(interp, objv[2], &value); + + if (r == TCL_OK) { + Tcl_Obj *point[2]; + XPoint pt = ValueToPoint(rangePtr, value); + point[0] = Tcl_NewIntObj(pt.x); + point[1] = Tcl_NewIntObj(pt.y); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, point)); + } + return r; +} + +static int +RangeMinCoordsCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + double value; + int r = TCL_OK; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "mincoords"); + return TCL_ERROR; + } + + r = Tcl_GetDoubleFromObj(interp, rangePtr->range.minvalueObj, &value); + + if (r == TCL_OK) { + Tcl_Obj *point[2]; + XPoint pt = ValueToPoint(rangePtr, value); + point[0] = Tcl_NewIntObj(pt.x); + point[1] = Tcl_NewIntObj(pt.y); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, point)); + } + return r; +} + +static int +RangeMaxCoordsCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Range *rangePtr = recordPtr; + double value; + int r = TCL_OK; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "maxcoords"); + return TCL_ERROR; + } + + r = Tcl_GetDoubleFromObj(interp, rangePtr->range.maxvalueObj, &value); + + if (r == TCL_OK) { + Tcl_Obj *point[2]; + XPoint pt = ValueToPoint(rangePtr, value); + point[0] = Tcl_NewIntObj(pt.x); + point[1] = Tcl_NewIntObj(pt.y); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, point)); + } + return r; +} + +static void RangeDoLayout(void *clientData) +{ + WidgetCore *corePtr = clientData; + Ttk_Element minslider = Ttk_FindElement(corePtr->layout, "minslider"); + Ttk_Element maxslider = Ttk_FindElement(corePtr->layout, "maxslider"); + + Ttk_PlaceLayout(corePtr->layout,corePtr->state,Ttk_WinBox(corePtr->tkwin)); + + /* Adjust the slider position: + */ + if (minslider && maxslider) { + Range *rangePtr = clientData; + Ttk_Box troughBox = TroughBox(rangePtr); + Ttk_Box minsliderBox = Ttk_ElementParcel(minslider); + Ttk_Box maxsliderBox = Ttk_ElementParcel(maxslider); + double minvalue = 0.0, maxvalue = 1.0; + double minfraction, maxfraction; + int range, offset; + + Tcl_GetDoubleFromObj(NULL, rangePtr->range.minvalueObj, &minvalue); + Tcl_GetDoubleFromObj(NULL, rangePtr->range.maxvalueObj, &maxvalue); + minfraction = RangeFraction(rangePtr, minvalue); + maxfraction = RangeFraction(rangePtr, maxvalue); + + if (rangePtr->range.orient == TTK_ORIENT_HORIZONTAL) { + range = troughBox.width - minsliderBox.width; + offset = minsliderBox.x; + minsliderBox.x = offset + (int)(minfraction * range); + maxsliderBox.x = offset + (int)(maxfraction * range); + } else { + range = troughBox.height - minsliderBox.height; + offset = minsliderBox.y; + minsliderBox.y = offset + (int)(minfraction * range); + maxsliderBox.y = offset + (int)(maxfraction * range); + } + + Ttk_PlaceElement(corePtr->layout, minslider, minsliderBox); + Ttk_PlaceElement(corePtr->layout, maxslider, maxsliderBox); + + } +} + +/* + * RangeSize -- + * Compute requested size of range. + */ +static int RangeSize(void *clientData, int *widthPtr, int *heightPtr) +{ + WidgetCore *corePtr = clientData; + Range *rangePtr = clientData; + int length; + + Ttk_LayoutSize(corePtr->layout, corePtr->state, widthPtr, heightPtr); + + /* Assert the -length configuration option */ + Tk_GetPixelsFromObj(NULL, corePtr->tkwin, + rangePtr->range.lengthObj, &length); + if (rangePtr->range.orient == TTK_ORIENT_VERTICAL) { + *heightPtr = MAX(*heightPtr, length); + } else { + *widthPtr = MAX(*widthPtr, length); + } + + return 1; +} + +static double +PointToValue(Range *rangePtr, int x, int y) +{ + Ttk_Box troughBox = TroughRange(rangePtr); + double from = 0, to = 1, fraction; + + Tcl_GetDoubleFromObj(NULL, rangePtr->range.fromObj, &from); + Tcl_GetDoubleFromObj(NULL, rangePtr->range.toObj, &to); + + if (rangePtr->range.orient == TTK_ORIENT_HORIZONTAL) { + fraction = (double)(x - troughBox.x) / (double)troughBox.width; + } else { + fraction = (double)(y - troughBox.y) / (double)troughBox.height; + } + + fraction = fraction < 0 ? 0 : fraction > 1 ? 1 : fraction; + + return from + fraction * (to-from); +} + +/* + * Return the center point in the widget corresponding to the given + * value. This point can be used to center the slider. + */ + +static XPoint +ValueToPoint(Range *rangePtr, double value) +{ + Ttk_Box troughBox = TroughRange(rangePtr); + double fraction = RangeFraction(rangePtr, value); + XPoint pt = {0, 0}; + + if (rangePtr->range.orient == TTK_ORIENT_HORIZONTAL) { + pt.x = troughBox.x + (int)(fraction * troughBox.width); + pt.y = troughBox.y + troughBox.height / 2; + } else { + pt.x = troughBox.x + troughBox.width / 2; + pt.y = troughBox.y + (int)(fraction * troughBox.height); + } + return pt; +} + +static const Ttk_Ensemble RangeCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + { "setmin", RangeSetMinCommand,0 }, + { "setmax", RangeSetMaxCommand,0 }, + { "get", RangeGetCommand,0 }, + { "getmin", RangeGetMinCommand,0 }, + { "getmax", RangeGetMaxCommand,0 }, + { "coords", RangeCoordsCommand,0 }, + { "mincoords", RangeMinCoordsCommand,0 }, + { "maxcoords", RangeMaxCoordsCommand,0 }, + { 0,0,0 } +}; + +static WidgetSpec RangeWidgetSpec = +{ + "TRange", /* Class name */ + sizeof(Range), /* record size */ + RangeOptionSpecs, /* option specs */ + RangeCommands, /* widget commands */ + RangeInitialize, /* initialization proc */ + RangeCleanup, /* cleanup proc */ + RangeConfigure, /* configure proc */ + RangePostConfigure, /* postConfigure */ + RangeGetLayout, /* getLayoutProc */ + RangeSize, /* sizeProc */ + RangeDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(VerticalRangeLayout) + TTK_GROUP("Vertical.Range.trough", TTK_FILL_BOTH, + TTK_NODE("Vertical.Range.minslider", TTK_PACK_TOP) + TTK_NODE("Vertical.Range.maxslider", TTK_PACK_BOTTOM) ) +TTK_END_LAYOUT + +TTK_BEGIN_LAYOUT(HorizontalRangeLayout) + TTK_GROUP("Horizontal.Range.trough", TTK_FILL_BOTH, + TTK_NODE("Horizontal.Range.minslider", TTK_PACK_LEFT) + TTK_NODE("Horizontal.Range.maxslider", TTK_PACK_RIGHT) ) +TTK_END_LAYOUT + +/* + * Initialization. + */ +MODULE_SCOPE +void TtkRange_Init(Tcl_Interp *interp) +{ + Ttk_Theme theme = Ttk_GetDefaultTheme(interp); + + Ttk_RegisterLayout(theme, "Vertical.TRange", VerticalRangeLayout); + Ttk_RegisterLayout(theme, "Horizontal.TRange", HorizontalRangeLayout); + + RegisterWidget(interp, "ttk::range", &RangeWidgetSpec); +} + |