Unnamed Fossil Project

Artifact [22bb48677f]
Login

Artifact [22bb48677f]

Artifact 22bb48677f5eaf2c80f0b138dab43f1fe63da477b0f561f3211c42ee10fe36ee:


/* $Id: button.c,v 1.28 2004/06/05 19:35:54 jenglish Exp $
 * Copyright (c) 2003, Joe English
 *
 * Tile package: button, checkbutton, radiobutton, and menubutton widgets.
 */

#include <string.h>
#include <tk.h>
#include "tkTheme.h"
#include "widget.h"
#include "compat.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;

    TraceHandle	*textVariableTrace;
    Tk_Image	image;

    /*
     * Image element resources:
     */
    Tcl_Obj *imageObj;

    /*
     * Compound label/image resources:
     */
    Tcl_Obj *compoundObj;
    Tcl_Obj *paddingObj;	/* Possibly does not belong here */

    /*
     * Compatibility/legacy resources:
     */
    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 resources
     */
    {TK_OPTION_STRING, "-image", "image", "Image", NULL/*default*/,
	Tk_Offset(Base,base.imageObj), -1, 
	TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },

    /*
     * Compound base/image resources.
     */
    {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 resources:
     */
    {TK_OPTION_STRING, "-state", "state", "State",
	 "normal", Tk_Offset(Base,base.stateObj), -1,
	 0,0,STATE_CHANGED },

    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)CoreOptionSpecs, 0}
};

/*
 * Variable trace procedure for -textvariable resource:
 */
static int
TextVariableChanged(
    Tcl_Interp *interp, 	/* interpreter containing variable. */
    void *clientData,		/* widget record pointer */
    const char *value)		/* variable's new value */
{
    Base *basePtr = clientData;
    Tcl_Obj *newText = Tcl_NewStringObj(value, -1);

    Tcl_IncrRefCount(newText);
    Tcl_DecrRefCount(basePtr->base.textObj);
    basePtr->base.textObj = newText;

    WidgetChanged(&basePtr->core, REDISPLAY_REQUIRED|RELAYOUT_REQUIRED);

    return TCL_OK;
}

/*
 * Tk_ImageChangedProc for -image option:
 */
static void BaseImageChangedProc(ClientData clientData,
    int x, int y, int width, int height, int imageWidth, int imageHeight)
{
    WidgetCore *corePtr = (WidgetCore *)clientData;
    WidgetChanged(corePtr, REDISPLAY_REQUIRED);
}

static int
BaseInitialize(Tcl_Interp *interp, void *recordPtr)
{
    Base *basePtr = recordPtr;
    basePtr->base.textVariableTrace = 0;
    basePtr->base.image = 0;
    return TCL_OK;
}

static void
BaseCleanup(void *recordPtr)
{
    Base *basePtr = recordPtr;
    if (basePtr->base.textVariableTrace)
	UntraceVariable(basePtr->base.textVariableTrace);
    if (basePtr->base.image)
	Tk_FreeImage(basePtr->base.image);
}

static int
BaseConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
{
    Base *basePtr = recordPtr;
    Tcl_Obj *textVarName = basePtr->base.textVariableObj;
    TraceHandle *vt;
    Tk_Image image = NULL;

    if (textVarName == NULL || *Tcl_GetString(textVarName) == '\0') {
	vt = 0;
    } else {
	vt = TraceVariable(interp, basePtr->base.textVariableObj,
	    TextVariableChanged, basePtr);
	if (!vt) return TCL_ERROR;
    }

    if (basePtr->base.imageObj) {
	const char *imageName = Tcl_GetString(basePtr->base.imageObj);
	if (imageName) {
	    image = Tk_GetImage(interp, basePtr->core.tkwin, imageName, 
		    BaseImageChangedProc, basePtr);
	    if (!image)
		goto error;
	}
    }

    if (CoreConfigure(interp, recordPtr, mask) != TCL_OK) {
error:
	if (image) Tk_FreeImage(image);
	if (vt) UntraceVariable(vt);
	return TCL_ERROR;
    }

    if (basePtr->base.textVariableTrace)
	UntraceVariable(basePtr->base.textVariableTrace);
    basePtr->base.textVariableTrace = vt;

    if (image != basePtr->base.image) {
	if (basePtr->base.image)
	    Tk_FreeImage(basePtr->base.image);
	basePtr->base.image = image;
    }

    if (mask & STATE_CHANGED) {
	CheckStateOption(&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 = FireTrace(basePtr->base.textVariableTrace);

    return status;
}


/*------------------------------------------------------------------------
 * +++ Label widget:
 * Just a base widget that adds a few appearance-related resources.
 */

typedef struct
{
    Tcl_Obj *borderWidthObj;
    Tcl_Obj *reliefObj;
    Tcl_Obj *anchorObj;
    Tcl_Obj *foregroundObj;
    Tcl_Obj *backgroundObj;
} LabelPart;

typedef struct
{
    WidgetCore	core;
    BasePart	base;
    LabelPart	label;
    LABEL_COMPAT_DECLS
} Label;

static Tk_OptionSpec LabelOptionSpecs[] =
{
    {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", 
	"2", Tk_Offset(Label,label.borderWidthObj), -1,
	TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
    {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
	"flat", Tk_Offset(Label,label.reliefObj), -1,
	TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
    {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
	DEFAULT_ANCHOR, Tk_Offset(Label,label.anchorObj), -1, 0, 0, 0},
    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)BaseOptionSpecs, 0}
};

LABEL_COMPAT_OPTIONS

static WidgetCommandSpec LabelCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    { NULL, NULL }
};

WidgetSpec LabelWidgetSpec =
{
    "TLabel",			/* className */
    sizeof(Label),		/* recordSize */
    LabelCompatOptionSpecs,	/* optionSpecs */
    LabelCommands,		/* subcommands */
    BaseInitialize,		/* initializeProc */
    BaseCleanup,		/* cleanupProc */
    BaseConfigure,		/* configureProc */
    BasePostConfigure,		/* postConfigureProc */
    WidgetGetLayout, 		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay,		/* displayProc */
    WIDGET_SPEC_END		/* sentinel */
};

/*------------------------------------------------------------------------
 * +++ Button widget.
 * Adds a new subcommand "invoke", and resources "-command" and "-default"
 */

typedef struct
{
    Tcl_Obj *commandObj;
    Tcl_Obj *defaultStateObj;
} ButtonPart;

typedef struct
{
    WidgetCore	core;
    BasePart	base;
    ButtonPart	button;
    BUTTON_COMPAT_DECLS
} Button;

/*
 * Option specifications:
 */
static Tk_OptionSpec ButtonOptionSpecs[] =
{
    WIDGET_TAKES_FOCUS,

    {TK_OPTION_STRING, "-command", "command", "Command",
	"", Tk_Offset(Button, button.commandObj), -1, 0,0,0},
    {TK_OPTION_STRING_TABLE, "-default", "default", "Default",
	"disabled", Tk_Offset(Button, button.defaultStateObj), -1,
	0, (ClientData) TTKDefaultStrings, DEFAULTSTATE_CHANGED},

    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)BaseOptionSpecs, 0}
};

BUTTON_COMPAT_OPTIONS

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) {
	    WidgetChangeState(&buttonPtr->core, TTK_STATE_DEFAULT, 0);
	} else {
	    WidgetChangeState(&buttonPtr->core, 0, TTK_STATE_DEFAULT);
	}
    }
    return TCL_OK;
}

static int
ButtonInvokeCommand(
    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], void *recordPtr)
{
    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 WidgetCommandSpec ButtonCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "invoke",		ButtonInvokeCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    { NULL, NULL }
};

WidgetSpec ButtonWidgetSpec =
{
    "TButton",			/* className */
    sizeof(Button),		/* recordSize */
    ButtonCompatOptionSpecs,	/* optionSpecs */
    ButtonCommands,		/* subcommands */
    BaseInitialize,		/* initializeProc */
    BaseCleanup,		/* cleanupProc */
    ButtonConfigure,		/* configureProc */
    BasePostConfigure,		/* postConfigureProc */
    WidgetGetLayout, 		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay,		/* displayProc */

    WIDGET_SPEC_END		/* sentinel */
};

/*------------------------------------------------------------------------
 * +++ Checkbutton widget.
 */
typedef struct
{
    Tcl_Obj *variableObj;
    Tcl_Obj *onValueObj;
    Tcl_Obj *offValueObj;
    Tcl_Obj *commandObj;

    TraceHandle *variableTrace;

} CheckbuttonPart;

typedef struct
{
    WidgetCore core;
    BasePart base;
    CheckbuttonPart checkbutton;
    CHECKBUTTON_COMPAT_DECLS
} Checkbutton;

/*
 * Option specifications:
 */
static Tk_OptionSpec CheckbuttonOptionSpecs[] =
{
    WIDGET_TAKES_FOCUS,

    {TK_OPTION_STRING, "-variable", "variable", "Variable",
	"", Tk_Offset(Checkbutton, checkbutton.variableObj), -1, 0,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},

    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)BaseOptionSpecs, 0}
};

CHECKBUTTON_COMPAT_OPTIONS

/*
 * Variable trace procedure for checkbutton -variable resource:
 */
static int
CheckbuttonVariableChanged(
    Tcl_Interp *interp, 	/* Interpreter containing variable. */
    void *clientData,		/* widget record pointer */
    const char *value)		/* Variable's new value */
{
    Checkbutton *checkPtr = clientData;

    if (!strcmp(value, Tcl_GetString(checkPtr->checkbutton.onValueObj))) {
	WidgetChangeState(&checkPtr->core, TTK_STATE_SELECTED, 0);
    } else {
	WidgetChangeState(&checkPtr->core, 0, TTK_STATE_SELECTED);
    }

    return TCL_OK;
}

static int
CheckbuttonInitialize(Tcl_Interp *interp, void *recordPtr)
{
    Checkbutton *checkPtr = recordPtr;
    checkPtr->checkbutton.variableTrace = TraceVariable(
	interp, checkPtr->checkbutton.variableObj,
	CheckbuttonVariableChanged, checkPtr);

    if (!checkPtr->checkbutton.variableTrace) return TCL_ERROR;

    return BaseInitialize(interp, recordPtr);
}

static void
CheckbuttonCleanup(void *recordPtr)
{
    Checkbutton *checkPtr = recordPtr;
    UntraceVariable(checkPtr->checkbutton.variableTrace);
    checkPtr->checkbutton.variableTrace = 0;
    BaseCleanup(recordPtr);
}

static int
CheckbuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
{
    Checkbutton *checkPtr = recordPtr;
    TraceHandle *vt = TraceVariable(interp, checkPtr->checkbutton.variableObj,
	CheckbuttonVariableChanged, checkPtr);

    if (!vt) 
	return TCL_ERROR;

    if (BaseConfigure(interp, recordPtr, mask) != TCL_OK){
	UntraceVariable(vt);
	return TCL_ERROR;
    }

    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 = 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(
    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], void *recordPtr)
{
    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 WidgetCommandSpec CheckbuttonCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "invoke",		CheckbuttonInvokeCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    /* MISSING: select, deselect, toggle */
    { NULL, NULL }
};

WidgetSpec CheckbuttonWidgetSpec =
{
    "TCheckbutton",		/* className */
    sizeof(Checkbutton),	/* recordSize */
    CheckbuttonCompatOptionSpecs,	/* optionSpecs */
    CheckbuttonCommands,	/* subcommands */
    CheckbuttonInitialize,	/* initializeProc */
    CheckbuttonCleanup,		/* cleanupProc */
    CheckbuttonConfigure,	/* configureProc */
    CheckbuttonPostConfigure,	/* postConfigureProc */
    WidgetGetLayout, 		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay,		/* displayProc */

    WIDGET_SPEC_END		/* sentinel */
};

/*------------------------------------------------------------------------
 * +++ Radiobutton widget.
 */

typedef struct
{
    Tcl_Obj *variableObj;
    Tcl_Obj *valueObj;
    Tcl_Obj *commandObj;

    TraceHandle	*variableTrace;

} RadiobuttonPart;

typedef struct
{
    WidgetCore core;
    BasePart base;
    RadiobuttonPart radiobutton;
    RADIOBUTTON_COMPAT_DECLS
} Radiobutton;

/*
 * Option specifications:
 */
static Tk_OptionSpec RadiobuttonOptionSpecs[] =
{
    WIDGET_TAKES_FOCUS,

    {TK_OPTION_STRING, "-variable", "variable", "Variable",
	"", 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},

    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)BaseOptionSpecs, 0}
};

RADIOBUTTON_COMPAT_OPTIONS

/*
 * Variable trace procedure for radiobuttons.
 */
static int
RadiobuttonVariableChanged(
    Tcl_Interp *interp, 	/* Interpreter containing variable. */
    void *clientData,		/* widget record pointer */
    const char *value)		/* Variable's new value */
{
    Radiobutton *radioPtr = clientData;

    if (!strcmp(value, Tcl_GetString(radioPtr->radiobutton.valueObj))) {
	WidgetChangeState(&radioPtr->core, TTK_STATE_SELECTED, 0);
    } else {
	WidgetChangeState(&radioPtr->core, 0, TTK_STATE_SELECTED);
    }

    return TCL_OK;
}

static int
RadiobuttonInitialize(Tcl_Interp *interp, void *recordPtr)
{
    Radiobutton *radioPtr = recordPtr;

    radioPtr->radiobutton.variableTrace = TraceVariable(
	interp, radioPtr->radiobutton.variableObj,
	RadiobuttonVariableChanged, radioPtr);
    if (!radioPtr->radiobutton.variableTrace) 
	return TCL_ERROR;

    return BaseInitialize(interp, recordPtr);
}

static void
RadiobuttonCleanup(void *recordPtr)
{
    Radiobutton *radioPtr = recordPtr;
    UntraceVariable(radioPtr->radiobutton.variableTrace);
    radioPtr->radiobutton.variableTrace = 0;
    BaseCleanup(recordPtr);
}

static int
RadiobuttonConfigure(Tcl_Interp *interp, void *recordPtr, int mask)
{
    Radiobutton *radioPtr = recordPtr;
    TraceHandle *vt = TraceVariable(interp, radioPtr->radiobutton.variableObj,
	RadiobuttonVariableChanged, radioPtr);

    if (!vt) 
	return TCL_ERROR;

    if (BaseConfigure(interp, recordPtr, mask) != TCL_OK) {
	UntraceVariable(vt);
	return TCL_ERROR;
    }

    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 = 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(
    Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], void *recordPtr)
{
    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 WidgetCommandSpec RadiobuttonCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "invoke",		RadiobuttonInvokeCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    /* MISSING: select, deselect */
    { NULL, NULL }
};

WidgetSpec RadiobuttonWidgetSpec =
{
    "TRadiobutton",		/* className */
    sizeof(Radiobutton),	/* recordSize */
    RadiobuttonCompatOptionSpecs,	/* optionSpecs */
    RadiobuttonCommands,	/* subcommands */
    RadiobuttonInitialize,	/* initializeProc */
    RadiobuttonCleanup,		/* cleanupProc */
    RadiobuttonConfigure,	/* configureProc */
    RadiobuttonPostConfigure,	/* postConfigureProc */
    WidgetGetLayout, 		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay,		/* displayProc */
    WIDGET_SPEC_END		/* sentinel */
};

/*------------------------------------------------------------------------
 * +++ Menubutton widget.
 */

typedef struct
{
    Tcl_Obj *menuObj;
    Tcl_Obj *directionObj;
} MenubuttonPart;

typedef struct
{
    WidgetCore core;
    BasePart base;
    MenubuttonPart menubutton;
    MENUBUTTON_COMPAT_DECLS
} Menubutton;

/*
 * Option specifications:
 */
static const char *directionStrings[] = {
    "above", "below", "left", "right", "flush", NULL
};
static Tk_OptionSpec MenubuttonOptionSpecs[] =
{
    WIDGET_TAKES_FOCUS,

    {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},

    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0, (ClientData)BaseOptionSpecs, 0}
};

MENUBUTTON_COMPAT_OPTIONS

static WidgetCommandSpec MenubuttonCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    { NULL, NULL }
};

WidgetSpec MenubuttonWidgetSpec =
{
    "TMenubutton",		/* className */
    sizeof(Menubutton), 	/* recordSize */
    MenubuttonCompatOptionSpecs,/* optionSpecs */
    MenubuttonCommands,  	/* subcommands */
    BaseInitialize,     	/* initializeProc */
    BaseCleanup,		/* cleanupProc */
    BaseConfigure,		/* configureProc */
    BasePostConfigure,  	/* postConfigureProc */
    WidgetGetLayout, 		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay,		/* displayProc */
    WIDGET_SPEC_END		/* sentinel */
};