/* square.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
 *
 * Minimal sample tile widget.
 *
 * $Id: square.c,v 1.4 2005/08/17 23:05:41 jenglish Exp $
 */

#include <tk.h>
#include "tkTheme.h"
#include "widget.h"

#ifndef DEFAULT_BORDERWIDTH
#define DEFAULT_BORDERWIDTH "2"
#endif

/*
 * First, we setup the widget record. The Tile package provides a structure
 * that contains standard widget data so it is only necessary to define
 * a structure that holds the data required for our widget. We do this by
 * defining a widget part and then specifying the widget record as the
 * concatenation of the two structures.
 */

typedef struct
{
    Tcl_Obj *widthObj;
    Tcl_Obj *heightObj;
    Tcl_Obj *reliefObj;
    Tcl_Obj *borderWidthObj;
    Tcl_Obj *foregroundObj;
    Tcl_Obj *paddingObj;
    Tcl_Obj *anchorObj;
} SquarePart;

typedef struct
{
    WidgetCore core;
    SquarePart square;
} Square;

/*
 * Widget options.
 *
 */

static Tk_OptionSpec SquareOptionSpecs[] =
{
    WIDGET_TAKES_FOCUS,

    {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
     DEFAULT_BORDERWIDTH, Tk_Offset(Square,square.borderWidthObj), -1,
     0,0,GEOMETRY_CHANGED },
    {TK_OPTION_BORDER, "-foreground", "foreground", "Foreground",
     "#eeaaaa", Tk_Offset(Square,square.foregroundObj),
     -1, 0, 0, 0},
    
    {TK_OPTION_PIXELS, "-width", "width", "Width",
     "50", Tk_Offset(Square,square.widthObj), -1, 0, 0,
     GEOMETRY_CHANGED},
    {TK_OPTION_PIXELS, "-height", "height", "Height",
     "50", Tk_Offset(Square,square.heightObj), -1, 0, 0,
     GEOMETRY_CHANGED},
    
    {TK_OPTION_STRING, "-padding", "padding", "Pad", NULL,
     Tk_Offset(Square,square.paddingObj), -1, 
     TK_OPTION_NULL_OK,0,GEOMETRY_CHANGED },
    
    {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
     NULL, Tk_Offset(Square,square.reliefObj), -1, TK_OPTION_NULL_OK, 0, 0},
    
    {TK_OPTION_ANCHOR, "-anchor", "anchor", "Anchor",
     NULL, Tk_Offset(Square,square.anchorObj), -1, TK_OPTION_NULL_OK, 0, 0},
    
    WIDGET_INHERIT_OPTIONS(CoreOptionSpecs)
};


/*
 * Widget commands:
 */
static WidgetCommandSpec SquareCommands[] =
{
    { "configure",	WidgetConfigureCommand },
    { "cget",		WidgetCgetCommand },
    { "instate",	WidgetInstateCommand },
    { "state",  	WidgetStateCommand },
    { NULL, NULL }
};

/*
 * Widget specification: 
 */
WidgetSpec SquareWidgetSpec =
{
    "TSquare",			/* className */
    sizeof(Square),		/* recordSize */
    SquareOptionSpecs,		/* optionSpecs */
    SquareCommands,		/* subcommands */
    NullInitialize,		/* initializeProc */
    NullCleanup,		/* cleanupProc */
    CoreConfigure,		/* configureProc */
    NullPostConfigure,		/* postConfigureProc */
    WidgetGetLayout,		/* getLayoutProc */
    WidgetSize, 		/* sizeProc */
    WidgetDoLayout,		/* layoutProc */
    WidgetDisplay		/* displayProc */
};

/* ---------------------------------------------------------------------- 
 * Square element
 *
 * In this section we demonstrate what is required to create a new themed
 * element.
 */

typedef struct
{
    Tcl_Obj *borderObj;
    Tcl_Obj *foregroundObj;
    Tcl_Obj *borderWidthObj;
    Tcl_Obj *reliefObj;
    Tcl_Obj *widthObj;
    Tcl_Obj *heightObj;
} SquareElement;

static Ttk_ElementOptionSpec SquareElementOptions[] = 
{
    { "-background", TK_OPTION_BORDER, Tk_Offset(SquareElement,borderObj),
    	DEFAULT_BACKGROUND },
    { "-foreground", TK_OPTION_BORDER, Tk_Offset(SquareElement,foregroundObj),
    	DEFAULT_BACKGROUND },
    { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SquareElement,borderWidthObj),
    	DEFAULT_BORDERWIDTH },
    { "-relief", TK_OPTION_RELIEF, Tk_Offset(SquareElement,reliefObj),
    	"flat" },
    { "-width",  TK_OPTION_PIXELS, Tk_Offset(SquareElement,widthObj), "20"},
    { "-height", TK_OPTION_PIXELS, Tk_Offset(SquareElement,heightObj), "20"},
    { NULL }
};

static void
SquareElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    SquareElement *square = elementRecord;
    int borderWidth = 0;

    Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
    *paddingPtr = Ttk_UniformPadding((short)borderWidth);
    Tk_GetPixelsFromObj(NULL, tkwin, square->widthObj, widthPtr);
    Tk_GetPixelsFromObj(NULL, tkwin, square->heightObj, heightPtr);
}

static void
SquareElementDraw(void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d, Ttk_Box b, unsigned int state)
{
    SquareElement *square = elementRecord;
    Tk_3DBorder border = NULL, foreground = NULL;
    int borderWidth = 1, relief = TK_RELIEF_FLAT;

    border = Tk_Get3DBorderFromObj(tkwin, square->borderObj);
    foreground = Tk_Get3DBorderFromObj(tkwin, square->foregroundObj);
    Tcl_GetIntFromObj(NULL, square->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, square->reliefObj, &relief);

    Tk_Fill3DRectangle(tkwin, d, foreground,
	b.x, b.y, b.width, b.height, borderWidth, relief);
}

static Ttk_ElementSpec SquareElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(SquareElement),
    SquareElementOptions,
    SquareElementGeometry,
    SquareElementDraw
};

/* ----------------------------------------------------------------------
 *
 * Layout section.
 *
 * Every widget class needs a layout style that specifies which elements
 * are part of the widget and how they should be placed. The element layout
 * engine is similar to the Tk pack geometry manager. Read the documentation
 * for the details. In this example we just need to have the square element
 * that has been defined for this widget placed on a background. We will
 * also need some padding to keep it away from the edges. 
 */

TTK_BEGIN_LAYOUT(SquareLayout)
     TTK_NODE("Square.background", TTK_FILL_BOTH)
     TTK_GROUP("Square.padding", TTK_FILL_BOTH,
	 TTK_NODE("Square.square", TTK_PACK_TOP))
TTK_END_LAYOUT

/* ---------------------------------------------------------------------- 
 *
 * Widget initialization.
 *
 * This file defines a new element and a new widget. We need to register
 * the element with the themes that will need it. In this case we will 
 * register with the default theme that is the root of the theme inheritance
 * tree. This means all themes will find this element.
 * We then need to register the widget class style. This is the layout
 * specification. If a different theme requires an alternative layout, we
 * could register that here. For instance, in some themes the scrollbars have
 * one uparrow, in other themes there are two uparrow elements.
 * Finally we register the widget itself. This step creates a tcl command so
 * that we can actually create an instance of this class. The widget is
 * linked to a particular style by the widget class name. This is important
 * to realise as the programmer may change the classname when creating a
 * new instance. If this is done, a new layout will need to be created (which
 * can be done at script level). Some widgets may require particular elements
 * to be present but we try to avoid this where possible. In this widget's C
 * code, no reference is made to any particular elements. The programmer is
 * free to specify a new style using completely different elements.
 */

/* public */ int
SquareWidget_Init(Tcl_Interp *interp)
{
    Ttk_Theme theme = Ttk_GetDefaultTheme(interp);

    /* register the new elements for this theme engine */
    Ttk_RegisterElement(interp, theme, "square", &SquareElementSpec, NULL);
    
    /* register the layout for this theme */
    Ttk_RegisterLayout(theme, "TSquare", SquareLayout);
    
    /* register the widget */
    RegisterWidget(interp, "ttk::square", &SquareWidgetSpec);

    return TCL_OK;
}
