diff options
Diffstat (limited to 'generic/ttk/ttkButton.c')
-rw-r--r-- | generic/ttk/ttkButton.c | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/generic/ttk/ttkButton.c b/generic/ttk/ttkButton.c new file mode 100644 index 0000000..2954184 --- /dev/null +++ b/generic/ttk/ttkButton.c @@ -0,0 +1,853 @@ +/* + * Copyright (c) 2003, Joe English + * + * label, button, checkbutton, radiobutton, and menubutton widgets. + */ + +#include <string.h> +#include <tk.h> +#include "ttkTheme.h" +#include "ttkWidget.h" + +/* Bit fields for OptionSpec mask field: + */ +#define STATE_CHANGED (0x100) /* -state option changed */ +#define DEFAULTSTATE_CHANGED (0x200) /* -default option changed */ + +/*------------------------------------------------------------------------ + * +++ Base resources for labels, buttons, checkbuttons, etc: + */ +typedef struct +{ + /* + * Text element resources: + */ + Tcl_Obj *textObj; + Tcl_Obj *textVariableObj; + Tcl_Obj *underlineObj; + Tcl_Obj *widthObj; + + Ttk_TraceHandle *textVariableTrace; + Ttk_ImageSpec *imageSpec; + + /* + * Image element resources: + */ + Tcl_Obj *imageObj; + + /* + * Compound label/image resources: + */ + Tcl_Obj *compoundObj; + Tcl_Obj *paddingObj; + + /* + * Compatibility/legacy options: + */ + Tcl_Obj *stateObj; + +} BasePart; + +typedef struct +{ + WidgetCore core; + BasePart base; +} Base; + +static Tk_OptionSpec BaseOptionSpecs[] = +{ + {TK_OPTION_STRING, "-text", "text", "Text", "", + Tk_Offset(Base,base.textObj), -1, + 0,0,GEOMETRY_CHANGED }, + {TK_OPTION_STRING, "-textvariable", "textVariable", "Variable", "", + Tk_Offset(Base,base.textVariableObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + {TK_OPTION_INT, "-underline", "underline", "Underline", + "-1", Tk_Offset(Base,base.underlineObj), -1, + 0,0,0 }, + /* SB: OPTION_INT, see <<NOTE-NULLOPTIONS>> */ + {TK_OPTION_STRING, "-width", "width", "Width", + NULL, Tk_Offset(Base,base.widthObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + + /* + * Image options + */ + {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/, + Tk_Offset(Base,base.imageObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + + /* + * Compound base/image options + */ + {TK_OPTION_STRING_TABLE, "-compound", "compound", "Compound", + "none", Tk_Offset(Base,base.compoundObj), -1, + 0,(ClientData)ttkCompoundStrings,GEOMETRY_CHANGED }, + {TK_OPTION_STRING, "-padding", "padding", "Pad", + NULL, Tk_Offset(Base,base.paddingObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED}, + + /* + * Compatibility/legacy options + */ + {TK_OPTION_STRING, "-state", "state", "State", + "normal", Tk_Offset(Base,base.stateObj), -1, + 0,0,STATE_CHANGED }, + + WIDGET_INHERIT_OPTIONS(ttkCoreOptionSpecs) +}; + +/* + * Variable trace procedure for -textvariable option: + */ +static void TextVariableChanged(void *clientData, const char *value) +{ + Base *basePtr = clientData; + Tcl_Obj *newText; + + if (WidgetDestroyed(&basePtr->core)) { + return; + } + + newText = value ? Tcl_NewStringObj(value, -1) : Tcl_NewStringObj("", 0); + + Tcl_IncrRefCount(newText); + Tcl_DecrRefCount(basePtr->base.textObj); + basePtr->base.textObj = newText; + + TtkResizeWidget(&basePtr->core); +} + +static void +BaseInitialize(Tcl_Interp *interp, void *recordPtr) +{ + Base *basePtr = recordPtr; + basePtr->base.textVariableTrace = 0; + basePtr->base.imageSpec = NULL; +} + +static void +BaseCleanup(void *recordPtr) +{ + Base *basePtr = recordPtr; + if (basePtr->base.textVariableTrace) + Ttk_UntraceVariable(basePtr->base.textVariableTrace); + if (basePtr->base.imageSpec) + TtkFreeImageSpec(basePtr->base.imageSpec); +} + +static int BaseConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Base *basePtr = recordPtr; + Tcl_Obj *textVarName = basePtr->base.textVariableObj; + Ttk_TraceHandle *vt = 0; + Ttk_ImageSpec *imageSpec = NULL; + + if (textVarName != NULL && *Tcl_GetString(textVarName) != '\0') { + vt = Ttk_TraceVariable(interp,textVarName,TextVariableChanged,basePtr); + if (!vt) return TCL_ERROR; + } + + if (basePtr->base.imageObj) { + imageSpec = TtkGetImageSpec( + interp, basePtr->core.tkwin, basePtr->base.imageObj); + if (!imageSpec) { + goto error; + } + } + + if (TtkCoreConfigure(interp, recordPtr, mask) != TCL_OK) { +error: + if (imageSpec) TtkFreeImageSpec(imageSpec); + if (vt) Ttk_UntraceVariable(vt); + return TCL_ERROR; + } + + if (basePtr->base.textVariableTrace) { + Ttk_UntraceVariable(basePtr->base.textVariableTrace); + } + basePtr->base.textVariableTrace = vt; + + if (basePtr->base.imageSpec) { + TtkFreeImageSpec(basePtr->base.imageSpec); + } + basePtr->base.imageSpec = imageSpec; + + if (mask & STATE_CHANGED) { + TtkCheckStateOption(&basePtr->core, basePtr->base.stateObj); + } + + return TCL_OK; +} + +static int +BasePostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Base *basePtr = recordPtr; + int status = TCL_OK; + + if (basePtr->base.textVariableTrace) { + status = Ttk_FireTrace(basePtr->base.textVariableTrace); + } + + return status; +} + +/*------------------------------------------------------------------------ + * +++ Label widget. + * Just a base widget that adds a few appearance-related options + */ + +typedef struct +{ + Tcl_Obj *backgroundObj; + Tcl_Obj *foregroundObj; + Tcl_Obj *fontObj; + Tcl_Obj *borderWidthObj; + Tcl_Obj *reliefObj; + Tcl_Obj *anchorObj; + Tcl_Obj *justifyObj; + Tcl_Obj *wrapLengthObj; +} LabelPart; + +typedef struct +{ + WidgetCore core; + BasePart base; + LabelPart label; +} Label; + +static Tk_OptionSpec LabelOptionSpecs[] = +{ + {TK_OPTION_BORDER, "-background", "frameColor", "FrameColor", + NULL, Tk_Offset(Label,label.backgroundObj), -1, + TK_OPTION_NULL_OK,0,0 }, + {TK_OPTION_COLOR, "-foreground", "textColor", "TextColor", + NULL, Tk_Offset(Label,label.foregroundObj), -1, + TK_OPTION_NULL_OK,0,0 }, + {TK_OPTION_FONT, "-font", "font", "Font", + NULL, Tk_Offset(Label,label.fontObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + NULL, Tk_Offset(Label,label.borderWidthObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + {TK_OPTION_RELIEF, "-relief", "relief", "Relief", + NULL, Tk_Offset(Label,label.reliefObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor", + NULL, Tk_Offset(Label,label.anchorObj), -1, + TK_OPTION_NULL_OK, 0, GEOMETRY_CHANGED}, + {TK_OPTION_JUSTIFY, "-justify", "justify", "Justify", + NULL, Tk_Offset(Label, label.justifyObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED }, + {TK_OPTION_PIXELS, "-wraplength", "wrapLength", "WrapLength", + NULL, Tk_Offset(Label, label.wrapLengthObj), -1, + TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED /*SB: SIZE_CHANGED*/ }, + + WIDGET_TAKEFOCUS_FALSE, + WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) +}; + +static const Ttk_Ensemble LabelCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + { 0,0,0 } +}; + +static WidgetSpec LabelWidgetSpec = +{ + "TLabel", /* className */ + sizeof(Label), /* recordSize */ + LabelOptionSpecs, /* optionSpecs */ + LabelCommands, /* subcommands */ + BaseInitialize, /* initializeProc */ + BaseCleanup, /* cleanupProc */ + BaseConfigure, /* configureProc */ + BasePostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + TtkWidgetDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(LabelLayout) + TTK_GROUP("Label.border", TTK_FILL_BOTH|TTK_BORDER, + TTK_GROUP("Label.padding", TTK_FILL_BOTH|TTK_BORDER, + TTK_NODE("Label.label", TTK_FILL_BOTH))) +TTK_END_LAYOUT + +/*------------------------------------------------------------------------ + * +++ Button widget. + * Adds a new subcommand "invoke", and options "-command" and "-default" + */ + +typedef struct +{ + Tcl_Obj *commandObj; + Tcl_Obj *defaultStateObj; +} ButtonPart; + +typedef struct +{ + WidgetCore core; + BasePart base; + ButtonPart button; +} Button; + +/* + * Option specifications: + */ +static Tk_OptionSpec ButtonOptionSpecs[] = +{ + {TK_OPTION_STRING, "-command", "command", "Command", + "", Tk_Offset(Button, button.commandObj), -1, 0,0,0}, + {TK_OPTION_STRING_TABLE, "-default", "default", "Default", + "normal", Tk_Offset(Button, button.defaultStateObj), -1, + 0, (ClientData) ttkDefaultStrings, DEFAULTSTATE_CHANGED}, + + WIDGET_TAKEFOCUS_TRUE, + WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) +}; + +static int ButtonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Button *buttonPtr = recordPtr; + + if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) { + return TCL_ERROR; + } + + /* Handle "-default" option: + */ + if (mask & DEFAULTSTATE_CHANGED) { + int defaultState = TTK_BUTTON_DEFAULT_DISABLED; + Ttk_GetButtonDefaultStateFromObj( + NULL, buttonPtr->button.defaultStateObj, &defaultState); + if (defaultState == TTK_BUTTON_DEFAULT_ACTIVE) { + TtkWidgetChangeState(&buttonPtr->core, TTK_STATE_ALTERNATE, 0); + } else { + TtkWidgetChangeState(&buttonPtr->core, 0, TTK_STATE_ALTERNATE); + } + } + return TCL_OK; +} + +/* $button invoke -- + * Evaluate the button's -command. + */ +static int +ButtonInvokeCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Button *buttonPtr = recordPtr; + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "invoke"); + return TCL_ERROR; + } + if (buttonPtr->core.state & TTK_STATE_DISABLED) { + return TCL_OK; + } + return Tcl_EvalObjEx(interp, buttonPtr->button.commandObj, TCL_EVAL_GLOBAL); +} + +static const Ttk_Ensemble ButtonCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "invoke", ButtonInvokeCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + { 0,0,0 } +}; + +static WidgetSpec ButtonWidgetSpec = +{ + "TButton", /* className */ + sizeof(Button), /* recordSize */ + ButtonOptionSpecs, /* optionSpecs */ + ButtonCommands, /* subcommands */ + BaseInitialize, /* initializeProc */ + BaseCleanup, /* cleanupProc */ + ButtonConfigure, /* configureProc */ + BasePostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + TtkWidgetDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(ButtonLayout) + TTK_GROUP("Button.border", TTK_FILL_BOTH|TTK_BORDER, + TTK_GROUP("Button.focus", TTK_FILL_BOTH, + TTK_GROUP("Button.padding", TTK_FILL_BOTH, + TTK_NODE("Button.label", TTK_FILL_BOTH)))) +TTK_END_LAYOUT + +/*------------------------------------------------------------------------ + * +++ Checkbutton widget. + */ +typedef struct +{ + Tcl_Obj *variableObj; + Tcl_Obj *onValueObj; + Tcl_Obj *offValueObj; + Tcl_Obj *commandObj; + + Ttk_TraceHandle *variableTrace; + +} CheckbuttonPart; + +typedef struct +{ + WidgetCore core; + BasePart base; + CheckbuttonPart checkbutton; +} Checkbutton; + +/* + * Option specifications: + */ +static Tk_OptionSpec CheckbuttonOptionSpecs[] = +{ + {TK_OPTION_STRING, "-variable", "variable", "Variable", + "", Tk_Offset(Checkbutton, checkbutton.variableObj), -1, + TK_OPTION_DONT_SET_DEFAULT,0,0}, + {TK_OPTION_STRING, "-onvalue", "onValue", "OnValue", + "1", Tk_Offset(Checkbutton, checkbutton.onValueObj), -1, + 0,0,0}, + {TK_OPTION_STRING, "-offvalue", "offValue", "OffValue", + "0", Tk_Offset(Checkbutton, checkbutton.offValueObj), -1, + 0,0,0}, + {TK_OPTION_STRING, "-command", "command", "Command", + "", Tk_Offset(Checkbutton, checkbutton.commandObj), -1, + 0,0,0}, + + WIDGET_TAKEFOCUS_TRUE, + WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) +}; + +/* + * Variable trace procedure for checkbutton -variable option + */ +static void CheckbuttonVariableChanged(void *clientData, const char *value) +{ + Checkbutton *checkPtr = clientData; + + if (WidgetDestroyed(&checkPtr->core)) { + return; + } + + if (!value) { + TtkWidgetChangeState(&checkPtr->core, TTK_STATE_ALTERNATE, 0); + return; + } + /* else */ + TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_ALTERNATE); + if (!strcmp(value, Tcl_GetString(checkPtr->checkbutton.onValueObj))) { + TtkWidgetChangeState(&checkPtr->core, TTK_STATE_SELECTED, 0); + } else { + TtkWidgetChangeState(&checkPtr->core, 0, TTK_STATE_SELECTED); + } +} + +static void +CheckbuttonInitialize(Tcl_Interp *interp, void *recordPtr) +{ + Checkbutton *checkPtr = recordPtr; + Tcl_Obj *variableObj; + + /* default -variable is the widget name: + */ + variableObj = Tcl_NewStringObj(Tk_PathName(checkPtr->core.tkwin), -1); + Tcl_IncrRefCount(variableObj); + checkPtr->checkbutton.variableObj = variableObj; + BaseInitialize(interp, recordPtr); +} + +static void +CheckbuttonCleanup(void *recordPtr) +{ + Checkbutton *checkPtr = recordPtr; + Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace); + checkPtr->checkbutton.variableTrace = 0; + BaseCleanup(recordPtr); +} + +static int +CheckbuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Checkbutton *checkPtr = recordPtr; + Ttk_TraceHandle *vt = Ttk_TraceVariable( + interp, checkPtr->checkbutton.variableObj, + CheckbuttonVariableChanged, checkPtr); + + if (!vt) { + return TCL_ERROR; + } + + if (BaseConfigure(interp, recordPtr, mask) != TCL_OK){ + Ttk_UntraceVariable(vt); + return TCL_ERROR; + } + + Ttk_UntraceVariable(checkPtr->checkbutton.variableTrace); + checkPtr->checkbutton.variableTrace = vt; + + return TCL_OK; +} + +static int +CheckbuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Checkbutton *checkPtr = recordPtr; + int status = TCL_OK; + + if (checkPtr->checkbutton.variableTrace) + status = Ttk_FireTrace(checkPtr->checkbutton.variableTrace); + if (status == TCL_OK && !WidgetDestroyed(&checkPtr->core)) + status = BasePostConfigure(interp, recordPtr, mask); + return status; +} + +/* + * Checkbutton 'invoke' subcommand: + * Toggles the checkbutton state. + */ +static int +CheckbuttonInvokeCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Checkbutton *checkPtr = recordPtr; + WidgetCore *corePtr = &checkPtr->core; + Tcl_Obj *newValue; + + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "invoke"); + return TCL_ERROR; + } + if (corePtr->state & TTK_STATE_DISABLED) + return TCL_OK; + + /* + * Toggle the selected state. + */ + if (corePtr->state & TTK_STATE_SELECTED) + newValue = checkPtr->checkbutton.offValueObj; + else + newValue = checkPtr->checkbutton.onValueObj; + + if (Tcl_ObjSetVar2(interp, + checkPtr->checkbutton.variableObj, NULL, newValue, + TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) + == NULL) + return TCL_ERROR; + + if (WidgetDestroyed(corePtr)) + return TCL_ERROR; + + return Tcl_EvalObjEx(interp, + checkPtr->checkbutton.commandObj, TCL_EVAL_GLOBAL); +} + +static const Ttk_Ensemble CheckbuttonCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "invoke", CheckbuttonInvokeCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + /* MISSING: select, deselect, toggle */ + { 0,0,0 } +}; + +static WidgetSpec CheckbuttonWidgetSpec = +{ + "TCheckbutton", /* className */ + sizeof(Checkbutton), /* recordSize */ + CheckbuttonOptionSpecs, /* optionSpecs */ + CheckbuttonCommands, /* subcommands */ + CheckbuttonInitialize, /* initializeProc */ + CheckbuttonCleanup, /* cleanupProc */ + CheckbuttonConfigure, /* configureProc */ + CheckbuttonPostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + TtkWidgetDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(CheckbuttonLayout) + TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH, + TTK_NODE("Checkbutton.indicator", TTK_PACK_LEFT) + TTK_GROUP("Checkbutton.focus", TTK_PACK_LEFT | TTK_STICK_W, + TTK_NODE("Checkbutton.label", TTK_FILL_BOTH))) +TTK_END_LAYOUT + +/*------------------------------------------------------------------------ + * +++ Radiobutton widget. + */ + +typedef struct +{ + Tcl_Obj *variableObj; + Tcl_Obj *valueObj; + Tcl_Obj *commandObj; + + Ttk_TraceHandle *variableTrace; + +} RadiobuttonPart; + +typedef struct +{ + WidgetCore core; + BasePart base; + RadiobuttonPart radiobutton; +} Radiobutton; + +/* + * Option specifications: + */ +static Tk_OptionSpec RadiobuttonOptionSpecs[] = +{ + {TK_OPTION_STRING, "-variable", "variable", "Variable", + "::selectedButton", Tk_Offset(Radiobutton, radiobutton.variableObj),-1, + 0,0,0}, + {TK_OPTION_STRING, "-value", "Value", "Value", + "1", Tk_Offset(Radiobutton, radiobutton.valueObj), -1, + 0,0,0}, + {TK_OPTION_STRING, "-command", "command", "Command", + "", Tk_Offset(Radiobutton, radiobutton.commandObj), -1, + 0,0,0}, + + WIDGET_TAKEFOCUS_TRUE, + WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) +}; + +/* + * Variable trace procedure for radiobuttons. + */ +static void +RadiobuttonVariableChanged(void *clientData, const char *value) +{ + Radiobutton *radioPtr = clientData; + + if (WidgetDestroyed(&radioPtr->core)) { + return; + } + + if (!value) { + TtkWidgetChangeState(&radioPtr->core, TTK_STATE_ALTERNATE, 0); + return; + } + /* else */ + TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_ALTERNATE); + if (!strcmp(value, Tcl_GetString(radioPtr->radiobutton.valueObj))) { + TtkWidgetChangeState(&radioPtr->core, TTK_STATE_SELECTED, 0); + } else { + TtkWidgetChangeState(&radioPtr->core, 0, TTK_STATE_SELECTED); + } +} + +static void +RadiobuttonCleanup(void *recordPtr) +{ + Radiobutton *radioPtr = recordPtr; + Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace); + radioPtr->radiobutton.variableTrace = 0; + BaseCleanup(recordPtr); +} + +static int +RadiobuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Radiobutton *radioPtr = recordPtr; + Ttk_TraceHandle *vt = Ttk_TraceVariable( + interp, radioPtr->radiobutton.variableObj, + RadiobuttonVariableChanged, radioPtr); + + if (!vt) { + return TCL_ERROR; + } + + if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) { + Ttk_UntraceVariable(vt); + return TCL_ERROR; + } + + Ttk_UntraceVariable(radioPtr->radiobutton.variableTrace); + radioPtr->radiobutton.variableTrace = vt; + + return TCL_OK; +} + +static int +RadiobuttonPostConfigure(Tcl_Interp *interp, void *recordPtr, int mask) +{ + Radiobutton *radioPtr = recordPtr; + int status = TCL_OK; + + if (radioPtr->radiobutton.variableTrace) + status = Ttk_FireTrace(radioPtr->radiobutton.variableTrace); + if (status == TCL_OK && !WidgetDestroyed(&radioPtr->core)) + status = BasePostConfigure(interp, recordPtr, mask); + return status; +} + +/* + * Radiobutton 'invoke' subcommand: + * Sets the radiobutton -variable to the -value, evaluates the -command. + */ +static int +RadiobuttonInvokeCommand( + void *recordPtr, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) +{ + Radiobutton *radioPtr = recordPtr; + WidgetCore *corePtr = &radioPtr->core; + + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "invoke"); + return TCL_ERROR; + } + if (corePtr->state & TTK_STATE_DISABLED) + return TCL_OK; + + if (Tcl_ObjSetVar2(interp, + radioPtr->radiobutton.variableObj, NULL, + radioPtr->radiobutton.valueObj, + TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) + == NULL) + return TCL_ERROR; + + if (WidgetDestroyed(corePtr)) + return TCL_ERROR; + + return Tcl_EvalObjEx(interp, + radioPtr->radiobutton.commandObj, TCL_EVAL_GLOBAL); +} + +static const Ttk_Ensemble RadiobuttonCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "invoke", RadiobuttonInvokeCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + /* MISSING: select, deselect */ + { 0,0,0 } +}; + +static WidgetSpec RadiobuttonWidgetSpec = +{ + "TRadiobutton", /* className */ + sizeof(Radiobutton), /* recordSize */ + RadiobuttonOptionSpecs, /* optionSpecs */ + RadiobuttonCommands, /* subcommands */ + BaseInitialize, /* initializeProc */ + RadiobuttonCleanup, /* cleanupProc */ + RadiobuttonConfigure, /* configureProc */ + RadiobuttonPostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + TtkWidgetDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(RadiobuttonLayout) + TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH, + TTK_NODE("Radiobutton.indicator", TTK_PACK_LEFT) + TTK_GROUP("Radiobutton.focus", TTK_PACK_LEFT, + TTK_NODE("Radiobutton.label", TTK_FILL_BOTH))) +TTK_END_LAYOUT + +/*------------------------------------------------------------------------ + * +++ Menubutton widget. + */ + +typedef struct +{ + Tcl_Obj *menuObj; + Tcl_Obj *directionObj; +} MenubuttonPart; + +typedef struct +{ + WidgetCore core; + BasePart base; + MenubuttonPart menubutton; +} Menubutton; + +/* + * Option specifications: + */ +static const char *const directionStrings[] = { + "above", "below", "left", "right", "flush", NULL +}; +static Tk_OptionSpec MenubuttonOptionSpecs[] = +{ + {TK_OPTION_STRING, "-menu", "menu", "Menu", + "", Tk_Offset(Menubutton, menubutton.menuObj), -1, 0,0,0}, + {TK_OPTION_STRING_TABLE, "-direction", "direction", "Direction", + "below", Tk_Offset(Menubutton, menubutton.directionObj), -1, + 0,(ClientData)directionStrings,GEOMETRY_CHANGED}, + + WIDGET_TAKEFOCUS_TRUE, + WIDGET_INHERIT_OPTIONS(BaseOptionSpecs) +}; + +static const Ttk_Ensemble MenubuttonCommands[] = { + { "configure", TtkWidgetConfigureCommand,0 }, + { "cget", TtkWidgetCgetCommand,0 }, + { "instate", TtkWidgetInstateCommand,0 }, + { "state", TtkWidgetStateCommand,0 }, + { "identify", TtkWidgetIdentifyCommand,0 }, + { 0,0,0 } +}; + +static WidgetSpec MenubuttonWidgetSpec = +{ + "TMenubutton", /* className */ + sizeof(Menubutton), /* recordSize */ + MenubuttonOptionSpecs, /* optionSpecs */ + MenubuttonCommands, /* subcommands */ + BaseInitialize, /* initializeProc */ + BaseCleanup, /* cleanupProc */ + BaseConfigure, /* configureProc */ + BasePostConfigure, /* postConfigureProc */ + TtkWidgetGetLayout, /* getLayoutProc */ + TtkWidgetSize, /* sizeProc */ + TtkWidgetDoLayout, /* layoutProc */ + TtkWidgetDisplay /* displayProc */ +}; + +TTK_BEGIN_LAYOUT(MenubuttonLayout) + TTK_GROUP("Menubutton.border", TTK_FILL_BOTH, + TTK_GROUP("Menubutton.focus", TTK_FILL_BOTH, + TTK_NODE("Menubutton.indicator", TTK_PACK_RIGHT) + TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X, + TTK_NODE("Menubutton.label", TTK_PACK_LEFT)))) +TTK_END_LAYOUT + +/*------------------------------------------------------------------------ + * +++ Initialization. + */ + +MODULE_SCOPE +void TtkButton_Init(Tcl_Interp *interp) +{ + Ttk_Theme theme = Ttk_GetDefaultTheme(interp); + + Ttk_RegisterLayout(theme, "TLabel", LabelLayout); + Ttk_RegisterLayout(theme, "TButton", ButtonLayout); + Ttk_RegisterLayout(theme, "TCheckbutton", CheckbuttonLayout); + Ttk_RegisterLayout(theme, "TRadiobutton", RadiobuttonLayout); + Ttk_RegisterLayout(theme, "TMenubutton", MenubuttonLayout); + + RegisterWidget(interp, "ttk::label", &LabelWidgetSpec); + RegisterWidget(interp, "ttk::button", &ButtonWidgetSpec); + RegisterWidget(interp, "ttk::checkbutton", &CheckbuttonWidgetSpec); + RegisterWidget(interp, "ttk::radiobutton", &RadiobuttonWidgetSpec); + RegisterWidget(interp, "ttk::menubutton", &MenubuttonWidgetSpec); +} |