Unnamed Fossil Project

Artifact [e2c64347e0]
Login

Artifact [e2c64347e0]

Artifact e2c64347e0a72c3fc28451612e8288305f56017396e997e9ca75b66490859619:


/* $Id: tkElements.c,v 1.31 2004/01/16 03:25:20 jenglish Exp $
 *
 * Copyright (c) 2003, Joe English
 *
 * Default implementation for themed elements.
 *
 * Implements the "classic" Motif-like Tk look.
 * Also provides default fallback elements for other themes.
 *
 */

#include <tcl.h>
#include <tk.h>
#include <string.h>
#include "tkTheme.h"

#define DEFAULT_SLIDER_LENGTH "30"
#define DEFAULT_ARROW_SIZE "15"
#define MIN_THUMB_SIZE 10

/*----------------------------------------------------------------------
 * +++ Background element implementation.
 *
 * This element simply clears the entire widget area
 * with the background color.
 *
 * It should be the first element drawn.
 */

typedef struct
{
    Tcl_Obj	*backgroundObj;
} BackgroundElement;

static TTK_ElementOptionSpec BackgroundElementOptions[] =
{
    { "-background", TK_OPTION_BORDER,
	    Tk_Offset(BackgroundElement,backgroundObj), DEFAULT_BACKGROUND },
    {NULL}
};

static void
BackgroundElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    /* Default zero is OK */
}

static void
BackgroundElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    BackgroundElement *bg = elementRecord;
    Tk_3DBorder backgroundPtr = NULL;

    /*
     * Extract option values.
     */
    if (bg->backgroundObj)
	backgroundPtr = Tk_Get3DBorderFromObj(tkwin, bg->backgroundObj);

    /*
     * Draw stuff.
     */
    if (backgroundPtr) {
	XFillRectangle(Tk_Display(tkwin), d,
	    Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
	    0,0,(unsigned)Tk_Width(tkwin), (unsigned)Tk_Height(tkwin));
    }
}

static TTK_ElementSpec BackgroundElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(BackgroundElement),
    BackgroundElementOptions,
    BackgroundElementGeometry,
    BackgroundElementDraw
};

/*----------------------------------------------------------------------
 * +++ Highlight element implementation.
 * 	Draws the focus ring (if the widget has keyboard focus).
 */

typedef struct {
    Tcl_Obj	*highlightColorObj;
    Tcl_Obj	*highlightThicknessObj;
} HighlightElement;

static TTK_ElementOptionSpec HighlightElementOptions[] = {
    { "-highlightcolor",TK_OPTION_COLOR,
	Tk_Offset(HighlightElement,highlightColorObj), "black" },
    { "-highlightthickness",TK_OPTION_PIXELS,
	Tk_Offset(HighlightElement,highlightThicknessObj), "1" },
    {NULL}
};

static void
HighlightElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    HighlightElement *highlight = elementRecord;
    int highlightThickness = 0;

    Tcl_GetIntFromObj(NULL, highlight->highlightThicknessObj, &highlightThickness);
    paddingPtr->left = paddingPtr->top 
	= paddingPtr->right = paddingPtr->bottom = highlightThickness;
}

static void
HighlightElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    HighlightElement *hl = elementRecord;
    int highlightThickness = 0;
    XColor *highlightColor = NULL;

    if (state & TTK_STATE_FOCUS) {
	Tcl_GetIntFromObj(NULL,hl->highlightThicknessObj,&highlightThickness);
	highlightColor = Tk_GetColorFromObj(tkwin, hl->highlightColorObj);

	if (highlightColor && highlightThickness > 0) {
	    GC gc = Tk_GCForColor(highlightColor, d);
	    Tk_DrawFocusHighlight(tkwin, gc, highlightThickness, d);
	}
    }
}

static TTK_ElementSpec HighlightElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(HighlightElement),
    HighlightElementOptions,
    HighlightElementGeometry,
    HighlightElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Border element implementation.
 *
 * The standard border on X11 consists of (from outside-in):
 *
 * + focus indicator (controlled by -highlightcolor and -highlightthickness),
 * + shaded border (controlled by -background, -borderwidth, and -relief)
 *
 * A "Pushable" border also adds 2 pixels of padding to shift child elements
 * "in" or "out" depending on -relief.
 *
 * In addition, buttons may have an additional "default ring",
 * controlled by the -default option.  There is a separate
 * Border element for Buttons than for everything else.
 * everything else.  While in principle it makes sense for
 * checkbuttons and radio buttons to be the "default" element,
 * we don't want to draw a default ring around them in this
 * theme since it's rilly ugly.
 *
 *
 *----------------------------------------------------------------------
 */

typedef struct
{
    Tcl_Obj	*borderObj;
    Tcl_Obj	*borderWidthObj;
    Tcl_Obj	*defaultStateObj;
    Tcl_Obj	*reliefObj;
} BorderElement;

static TTK_ElementOptionSpec BorderElementOptions[] =
{
    { "-background", TK_OPTION_BORDER, Tk_Offset(BorderElement,borderObj),
    	DEFAULT_BACKGROUND },
    { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(BorderElement,borderWidthObj),
    	DEFAULT_BORDERWIDTH },
    { "-default", TK_OPTION_ANY, Tk_Offset(BorderElement,defaultStateObj),
    	"disabled" },
    { "-relief", TK_OPTION_RELIEF, Tk_Offset(BorderElement,reliefObj),
    	"flat" },
    {NULL}
};

static void
BorderElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    BorderElement *bd = elementRecord;
    int borderWidth = 0;
    int relief = TK_RELIEF_FLAT;

    Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);

    paddingPtr->left = paddingPtr->right = paddingPtr->top = paddingPtr->bottom
	= borderWidth;
}

static void PushableBorderElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    BorderElement *border = elementRecord;
    int relief;

    BorderElementGeometry(clientData, elementRecord, tkwin,
	    widthPtr, heightPtr, paddingPtr);

    /* Distribute extra 2 pixels of padding according to current relief: */
    Tk_GetReliefFromObj(NULL, border->reliefObj, &relief);
    switch (relief)
    {
	case TK_RELIEF_RAISED:
	    paddingPtr->right += 2;
	    paddingPtr->bottom += 2;
	    break;
	case TK_RELIEF_SUNKEN:	/* shift */
	    paddingPtr->left += 2;
	    paddingPtr->top += 2;
	    break;
	default:
	    paddingPtr->left += 1;
	    paddingPtr->top += 1;
	    paddingPtr->right += 1;
	    paddingPtr->bottom += 1;
	    break;
    }
}

/*
 * ButtonBorderElementGeometry --
 * 	Same as PushableBorderElementGeometry(), but allocate extra space
 * 	for the default ring.
 */
static void
ButtonBorderElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    BorderElement *bd = elementRecord;
    int defaultState = TK_BUTTON_DEFAULT_DISABLED;

    PushableBorderElementGeometry(clientData, elementRecord, tkwin,
	    widthPtr, heightPtr, paddingPtr);

    TkGetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);
    if (defaultState != TK_BUTTON_DEFAULT_DISABLED) {
	paddingPtr->left += 5;
	paddingPtr->right += 5;
	paddingPtr->top += 5;
	paddingPtr->bottom += 5;
    }
}

/*
 * BorderElementDraw --
 * 	Draw default borders.
 * 	See also ButtonBorderElementDraw, which handles the default ring.
 */
static void
BorderElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    BorderElement *bd = elementRecord;
    Tk_3DBorder border = NULL;
    int borderWidth = 1, relief = TK_RELIEF_FLAT;
    int inset = 0;

    /*
     * Get option values.
     */
    border = Tk_Get3DBorderFromObj(tkwin, bd->borderObj);
    Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);

    /*
     * 3-D shadow:
     */
    if (border && borderWidth > 0 && relief != TK_RELIEF_FLAT) {
	Tk_Draw3DRectangle(tkwin, d, border,
	    x+inset, y+inset, width - 2*inset, height - 2*inset,
	    borderWidth,relief);
    }
}

/*
 * ButtonBorderElementDraw --
 * 	Same as BorderElementDraw, but has extra logic to deal with
 * 	the default ring.
 * 	(@@@ Note: still buggy: padding for default ring is drawn in
 * 	the wrong color when the button is active.  I'm not going to
 * 	bother to fix this right now.)
 */
static void
ButtonBorderElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    BorderElement *bd = elementRecord;
    Tk_3DBorder border = NULL;
    int borderWidth = 1, relief = TK_RELIEF_FLAT;
    int defaultState = TK_BUTTON_DEFAULT_DISABLED;
    int inset = 0;

    /*
     * Get option values.
     */
    border = Tk_Get3DBorderFromObj(tkwin, bd->borderObj);
    Tcl_GetIntFromObj(NULL, bd->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief);
    TkGetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState);

    /*
     * Default ring:
     */
    switch (defaultState)
    {
	case TK_BUTTON_DEFAULT_DISABLED :
	    break;
	case TK_BUTTON_DEFAULT_NORMAL :
	    inset += 5;
	    break;
	case TK_BUTTON_DEFAULT_ACTIVE :
            Tk_Draw3DRectangle(tkwin, d, border,
		    inset, inset, width - 2*inset, height - 2*inset,
		    2, TK_RELIEF_FLAT);
            inset += 2;
            Tk_Draw3DRectangle(tkwin, d, border,
		    inset, inset, width - 2*inset, height - 2*inset,
		    1, TK_RELIEF_SUNKEN);
	    ++inset;
            Tk_Draw3DRectangle(tkwin, d, border,
		    inset, inset, width - 2*inset, height - 2*inset,
		    2, TK_RELIEF_FLAT);
	    inset += 2;
	    break;
    }

    /*
     * 3-D border:
     */
    if (border && borderWidth > 0) {
	Tk_Draw3DRectangle(tkwin, d, border,
	    x+inset, y+inset, width - 2*inset, height - 2*inset,
	    borderWidth,relief);
    }
}

static TTK_ElementSpec BorderElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(BorderElement),
    BorderElementOptions,
    BorderElementGeometry,
    BorderElementDraw
};

static TTK_ElementSpec PushableBorderElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(BorderElement),
    BorderElementOptions,
    PushableBorderElementGeometry,
    BorderElementDraw
};

static TTK_ElementSpec ButtonBorderElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(BorderElement),
    BorderElementOptions,
    ButtonBorderElementGeometry,
    ButtonBorderElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Padding element implementation.
 *
 * This element has no visual representation, only geometry.
 * It adds a (possibly non-uniform) internal border.
 */

typedef struct
{
    Tcl_Obj	*paddingObj;
} PaddingElement;

static TTK_ElementOptionSpec PaddingElementOptions[] =
{
    { "-padding", TK_OPTION_STRING,
	    Tk_Offset(PaddingElement,paddingObj), "0" },
    {NULL}
};

static void
PaddingElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    PaddingElement *padding = elementRecord;
    TTK_GetPaddingFromObj(NULL,tkwin,padding->paddingObj,paddingPtr);
}

static void
PaddingElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    /* No-op */
}

static TTK_ElementSpec PaddingElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(PaddingElement),
    PaddingElementOptions,
    PaddingElementGeometry,
    PaddingElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Text element implementation.
 *
 * This element displays a textual label in the foreground color.
 *
 * Optionally underlines the mnemonic character if the -underline resource
 * is present and >= 0.
 */

typedef struct
{
    /*
     * Element resources:
     */
    Tcl_Obj	*textObj;
    Tcl_Obj	*fontObj;
    Tcl_Obj	*foregroundObj;
    Tcl_Obj	*underlineObj;
    Tcl_Obj	*widthObj;

    /*
     * Computed resources:
     */
    Tk_Font		tkfont;
    Tk_TextLayout	textLayout;
    int 		width;
    int 		height;

} TextElement;

/* Text element options table.
 * NB: Keep in sync with compound element option table.
 */
static TTK_ElementOptionSpec TextElementOptions[] =
{
    { "-text", TK_OPTION_STRING,
	Tk_Offset(TextElement,textObj), "" },
    { "-font", TK_OPTION_FONT,
	Tk_Offset(TextElement,fontObj), DEFAULT_FONT },
    { "-foreground", TK_OPTION_COLOR,
	Tk_Offset(TextElement,foregroundObj), "black" },
    { "-underline", TK_OPTION_INT,
	Tk_Offset(TextElement,underlineObj), "-1"},
    { "-width", TK_OPTION_INT,
	Tk_Offset(TextElement,widthObj), "-1"},
    {NULL}
};

static int TextSetup(TextElement *text, Tk_Window tkwin)
{
    const char *string;
    int nChars;
    int justify = TK_JUSTIFY_LEFT;

    text->width = text->height = 0;

    if (!text->textObj || !text->fontObj)
	return 0;
    text->tkfont = Tk_GetFontFromObj(tkwin, text->fontObj);
    if (!text->tkfont)
	return 0;

    string = Tcl_GetStringFromObj(text->textObj, &nChars);

    /* @@@ POSSIBLY: just use Tk_MeasureChars() and font metrics?
     * @@@ Do we really need to support multi-line text labels here?
     */
    text->textLayout = Tk_ComputeTextLayout(
	    text->tkfont, string, nChars, 0, justify,
	    0/*flags*/, &text->width, &text->height);

    return 1;
}


/*
 * TextReqWidth -- compute the requested width of a text element.
 *	
 * If -width is positive, use that as the width
 * If -width is negative, use that as the minimum width
 * If not specified or empty, use the natural size of the text
 */

static int TextReqWidth(TextElement *text)
{
    int reqWidth;

    if (   text->widthObj 
	&& Tcl_GetIntFromObj(NULL, text->widthObj, &reqWidth) == TCL_OK) 
    {
	int avgWidth = Tk_TextWidth(text->tkfont, "0", 1);
	if (reqWidth <= 0) {
	    int specWidth = avgWidth * -reqWidth;
	    if (specWidth > text->width)
		return specWidth;
	} else {
	    return avgWidth * reqWidth;
	}
    }
    return text->width;
}


static void TextCleanup(TextElement *text)
{
    Tk_FreeTextLayout(text->textLayout);
}

/*
 * TextDraw --
 * 	Draw a text element.
 * 	Called by TextElementDraw() and CompoundElementDraw().
 */
static void TextDraw(TextElement *text, Tk_Window tkwin,
	Drawable d, int x, int y, int width, int height)
{
    int underline = -1;
    unsigned long mask = 0;
    XGCValues gcValues;
    GC gc;

    mask = 0;
    getFontFromObj(tkwin, text->fontObj, &mask, &gcValues);
    getForegroundFromObj(tkwin, text->foregroundObj, &mask, &gcValues);
    gc = Tk_GetGC(tkwin, mask, &gcValues);

    adjustAnchor(TK_ANCHOR_CENTER, &x,&y, width,height,
	    text->width,text->height);
    Tk_DrawTextLayout(Tk_Display(tkwin), d, gc,
	    text->textLayout, x, y, 0/*firstChar*/, -1/*lastChar*/);

    Tcl_GetIntFromObj(NULL, text->underlineObj, &underline);
    if (underline >= 0)
	Tk_UnderlineTextLayout(Tk_Display(tkwin), d, gc,
	    text->textLayout, x, y, underline);

    Tk_FreeGC(Tk_Display(tkwin), gc);
}

static void
TextElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    TextElement *text = elementRecord;

    if (!TextSetup(text, tkwin))
	return;

    *heightPtr = text->height;
    *widthPtr = text->width;

    TextCleanup(text);

    return;
}

static void
TextElementDraw(
    ClientData clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    TextElement *text = elementRecord;
    if (!TextSetup(text, tkwin))
	return;
    TextDraw(text, tkwin, d, x, y, width, height);
    TextCleanup(text);
}

static TTK_ElementSpec TextElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(TextElement),
    TextElementOptions,
    TextElementGeometry,
    TextElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Image element implementation.
 *
 * Draws an image.
 *
 * The clientData parameter is a Tcl_Interp, which is needed for
 * the call to Tk_GetImage().
 */

typedef struct
{
    Tcl_Obj	*imageObj;

    Tk_Image	tkimg;
    int 	width;
    int		height;
} ImageElement;

static TTK_ElementOptionSpec ImageElementOptions[] =
{
    { "-image", TK_OPTION_STRING, Tk_Offset(ImageElement,imageObj), "" },
    {NULL}
};

/*
 * ImageSetup() --
 * 	Look up the Tk_Image from the image element's imageObj resource.
 * 	Caller must release the image with ImageCleanup().
 *
 * Returns:
 * 	1 if successful, 0 if there was an error (unreported)
 * 	or the image resource was not specified.
 */

static int ImageSetup(ImageElement *image, Tk_Window tkwin, Tcl_Interp *interp)
{
    const char *imageName;

    if (!image->imageObj)		/* No -image resource specified */
	return 0;

    imageName = Tcl_GetString(image->imageObj);
    if (!imageName || !*imageName)	/* Empty string. */
	return 0;

    image->tkimg = Tk_GetImage(interp, tkwin, imageName, 0, 0);
    if (!image->tkimg)			/* No such image */
	return 0;

    Tk_SizeOfImage(image->tkimg, &image->width, &image->height);

    return 1;
}

static void ImageCleanup(ImageElement *image)
{
    Tk_FreeImage(image->tkimg);
}

static void ImageDraw(ImageElement *image, Tk_Window tkwin, Drawable d,
	int x, int y, int width, int height)
{
    adjustAnchor(TK_ANCHOR_CENTER, &x,&y, width,height,
	    image->width,image->height);
    Tk_RedrawImage(image->tkimg, 0, 0, image->width, image->height, d, x, y);
}

static void
ImageElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    ImageElement *image = elementRecord;
    Tcl_Interp *interp = clientData;

    if (ImageSetup(image, tkwin, interp)) {
	*widthPtr = image->width;
	*heightPtr = image->height;
	ImageCleanup(image);
    }
}

static void
ImageElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    ImageElement *image = elementRecord;
    Tcl_Interp *interp = clientData;

    if (ImageSetup(image, tkwin, interp)) {
	ImageDraw(image, tkwin, d, x, y, width, height);
	ImageCleanup(image);
    }
}

static TTK_ElementSpec ImageElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(ImageElement),
    ImageElementOptions,
    ImageElementGeometry,
    ImageElementDraw
};

/*------------------------------------------------------------------------
 * +++ Compound element implementation.
 *
 * Displays an image and/or text, as determined by the -compound
 * resource.
 *
 * The clientData parameter is a Tcl_Interp; this is needed for the
 * image part.
 *
 * Differences from Tk 8.4 compound elements:
 *
 * This adds two new options for the -compound resource, "text"
 * and "image".  (This is useful for configuring toolbars to
 * display icons, text and icons, or text only, as found in
 * many browsers.)
 *
 * "-compound none" is supported, but I'd like to get rid of it;
 * it makes the logic more complex, and the only benefit is
 * backwards compatibility with Tk < 8.3.0 scripts.
 *
 * This adds a new resource, -space, for determining how much
 * space to leave between the text and image; Tk 8.4 reuses the
 * -padx or -pady option for this purpose.
 *
 * Tk 8.4 ignores -padx and -pady if -compound is set to "none".
 * This element always includes padding.
 */

typedef struct
{
    /*
     * Element resources:
     */
    Tcl_Obj		*compoundObj;
    Tcl_Obj		*spaceObj;
    Tcl_Obj		*anchorObj;
    TextElement 	text;
    ImageElement	image;

    /*
     * Computed values (see CompoundSetup)
     */
    TkCompoundLayout 	layout;
    int  		space;
    int			textWidth, textHeight;
    int 		imageWidth, imageHeight;
    int 		totalWidth, totalHeight;
} CompoundElement;

static TTK_ElementOptionSpec CompoundElementOptions[] =
{
    { "-compound", TK_OPTION_ANY,
	Tk_Offset(CompoundElement,compoundObj), "none" },
    { "-anchor", TK_OPTION_ANCHOR,
	Tk_Offset(CompoundElement,anchorObj), "center"},
    { "-space", TK_OPTION_PIXELS,
	Tk_Offset(CompoundElement,spaceObj), "4" },

    /* Text element part:
     * NB: Keep in sync with TextElementOptions.
     */
    { "-text", TK_OPTION_STRING,
	Tk_Offset(CompoundElement,text.textObj), "" },
    { "-font", TK_OPTION_FONT,
	Tk_Offset(CompoundElement,text.fontObj), DEFAULT_FONT },
    { "-foreground", TK_OPTION_COLOR,
	Tk_Offset(CompoundElement,text.foregroundObj), "black" },
    { "-underline", TK_OPTION_INT,
	Tk_Offset(CompoundElement,text.underlineObj), "-1"},
    { "-width", TK_OPTION_INT,
	Tk_Offset(CompoundElement,text.widthObj), ""},

    /* Image element part:
     * NB: Keep in sync with ImageElementOptions.
     */
    { "-image", TK_OPTION_STRING,
	Tk_Offset(CompoundElement,image.imageObj), "" },

    {NULL}
};

/*
 * CompoundSetup --
 * 	Fills in computed fields of the compound element.
 *
 * 	Calculate the text, image, and total width and height.
 */

#define MAX(a,b) ((a) > (b) ? a : b);
static void CompoundSetup(
    CompoundElement *c, Tk_Window tkwin, Tcl_Interp *interp)
{
    Tk_GetPixelsFromObj(NULL,tkwin,c->spaceObj,&c->space);
    TkGetCompoundLayoutFromObj(NULL,c->compoundObj,(int*)&c->layout);

    /*
     * Deal with TK_COMPOUND_LAYOUT_NONE.
     */
    if (c->layout == TK_COMPOUND_LAYOUT_NONE) {
	if (ImageSetup(&c->image, tkwin, interp)) {
	    c->layout = TK_COMPOUND_LAYOUT_IMAGE;
	} else {
	    c->layout = TK_COMPOUND_LAYOUT_TEXT;
	}
    } else if (c->layout != TK_COMPOUND_LAYOUT_TEXT) {
    	if (!ImageSetup(&c->image, tkwin, interp)) {
	    c->layout = TK_COMPOUND_LAYOUT_TEXT;
	}
    }
    if (c->layout != TK_COMPOUND_LAYOUT_IMAGE)
	TextSetup(&c->text, tkwin);

    /*
     * ASSERT:
     * if c->layout != LAYOUT_IMAGE, then TextSetup() has been called
     * if c->layout != LAYOUT_TEXT, then ImageSetup() has returned successfully
     * c->layout != LAYOUT_NONE.
     */

    switch (c->layout)
    {
	case TK_COMPOUND_LAYOUT_NONE:
	    Tcl_Panic("This can't happen");
	    break;
	case TK_COMPOUND_LAYOUT_TEXT:
	    c->totalWidth  = TextReqWidth(&c->text);
	    c->totalHeight = c->text.height;
	    break;
	case TK_COMPOUND_LAYOUT_IMAGE:
	    c->totalWidth  = c->image.width;
	    c->totalHeight = c->image.height;
	    break;
	case TK_COMPOUND_LAYOUT_CENTER:
	    c->totalWidth  = MAX(c->image.width, c->text.width);
	    c->totalHeight = MAX(c->image.height, c->text.height);
	    break;
	case TK_COMPOUND_LAYOUT_TOP:
	case TK_COMPOUND_LAYOUT_BOTTOM:
	    c->totalWidth  = MAX(c->image.width, c->text.width);
	    c->totalHeight = c->image.height + c->text.height + c->space;
	    break;

	case TK_COMPOUND_LAYOUT_LEFT:
	case TK_COMPOUND_LAYOUT_RIGHT:
	    c->totalWidth  = c->image.width + c->text.width + c->space;
	    c->totalHeight = MAX(c->image.height, c->text.height);
	    break;
    }
}

static void CompoundCleanup(CompoundElement *c)
{
    if (c->layout != TK_COMPOUND_LAYOUT_TEXT)
	ImageCleanup(&c->image);
    if (c->layout != TK_COMPOUND_LAYOUT_IMAGE)
	TextCleanup(&c->text);
}

static void
CompoundElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    CompoundElement *compound = elementRecord;
    Tcl_Interp *interp = clientData;

    CompoundSetup(compound, tkwin, interp);
    *widthPtr = compound->totalWidth;
    *heightPtr = compound->totalHeight;
    CompoundCleanup(compound);
}

static void
CompoundElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    CompoundElement *c = elementRecord;
    Tcl_Interp *interp = clientData;
    Tk_Anchor anchor = TK_ANCHOR_CENTER;

    CompoundSetup(c, tkwin, interp);

    /*
     * Adjust parcel:
     */
    Tk_GetAnchorFromObj(NULL, c->anchorObj, &anchor);
    adjustAnchor(anchor, &x, &y, width, height, c->totalWidth, c->totalHeight);

    switch (c->layout)
    {
	case TK_COMPOUND_LAYOUT_NONE:
	    Tcl_Panic("This can't happen");
	    break;
	case TK_COMPOUND_LAYOUT_TEXT:
	    TextDraw(&c->text,tkwin,d,x,y, c->totalWidth,c->totalHeight);
	    break;
	case TK_COMPOUND_LAYOUT_IMAGE:
	    ImageDraw(&c->image,tkwin,d,x,y, c->totalWidth,c->totalHeight);
	    break;
	case TK_COMPOUND_LAYOUT_CENTER:
	    ImageDraw(&c->image,tkwin,d,x,y, c->totalWidth,c->totalHeight);
	    TextDraw(&c->text,tkwin,d,x,y, c->totalWidth,c->totalHeight);
	    break;
	case TK_COMPOUND_LAYOUT_TOP:
	    ImageDraw(&c->image,tkwin,d,x,y, c->totalWidth,c->image.height);
	    y += c->image.height + c->space;
	    TextDraw(&c->text,tkwin,d,x,y, c->totalWidth,c->text.height);
	    break;
	case TK_COMPOUND_LAYOUT_BOTTOM:
	    TextDraw(&c->text,tkwin,d,x,y, c->totalWidth,c->text.height);
	    y += c->text.height + c->space;
	    ImageDraw(&c->image,tkwin,d,x,y, c->totalWidth,c->image.height);
	    break;
	case TK_COMPOUND_LAYOUT_LEFT:
	    ImageDraw(&c->image,tkwin,d,x,y, c->image.width,c->totalHeight);
	    x += c->image.width + c->space;
	    TextDraw(&c->text,tkwin,d,x,y, c->text.width,c->totalHeight);
	    break;
	case TK_COMPOUND_LAYOUT_RIGHT:
	    TextDraw(&c->text,tkwin,d,x,y, c->text.width,c->totalHeight);
	    x += c->text.width + c->space;
	    ImageDraw(&c->image,tkwin,d,x,y, c->image.width,c->totalHeight);
	    break;
    }

    CompoundCleanup(c);
}

static TTK_ElementSpec CompoundElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(CompoundElement),
    CompoundElementOptions,
    CompoundElementGeometry,
    CompoundElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Indicator element implementation.
 *
 * Draws the on/off indicator for checkbuttons and radiobuttons.
 *
 * Draws a 3-D square (or diamond), raised if off, sunken if on.
 *
 * This is actually a regression from Tk 8.5 back to the ugly old Motif
 * style; use "altTheme" for the newer, nicer version.
 */

typedef struct
{
    Tcl_Obj *backgroundObj;
    Tcl_Obj *reliefObj;
    Tcl_Obj *colorObj;
    Tcl_Obj *diameterObj;
    Tcl_Obj *marginObj;
    Tcl_Obj *borderWidthObj;
} IndicatorElement;

static TTK_ElementOptionSpec IndicatorElementOptions[] =
{
    { "-background", TK_OPTION_BORDER,
	    Tk_Offset(IndicatorElement,backgroundObj), DEFAULT_BACKGROUND },
    { "-indicatorcolor", TK_OPTION_BORDER,
	    Tk_Offset(IndicatorElement,colorObj), DEFAULT_BACKGROUND },
    { "-indicatorrelief", TK_OPTION_RELIEF,
	    Tk_Offset(IndicatorElement,reliefObj), "raised" },
    { "-indicatordiameter", TK_OPTION_PIXELS,
	    Tk_Offset(IndicatorElement,diameterObj), "12" },
    { "-indicatormargin", TK_OPTION_STRING,
	    Tk_Offset(IndicatorElement,marginObj), "0 2 4 2" },
    { "-borderwidth", TK_OPTION_PIXELS,
	Tk_Offset(IndicatorElement,borderWidthObj), DEFAULT_BORDERWIDTH },
    {NULL}
};

/*
 * Checkbutton indicators (default): 3-D square.
 */
static void
SquareIndicatorElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    IndicatorElement *indicator = elementRecord;
    int diameter = 0;
    TTK_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, paddingPtr);
    Tk_GetPixelsFromObj(NULL, tkwin, indicator->diameterObj, &diameter);
    *widthPtr = *heightPtr = diameter;
}

static void
SquareIndicatorElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    IndicatorElement *indicator = elementRecord;
    Tk_3DBorder border = 0, interior = 0;
    int relief = TK_RELIEF_RAISED;
    int borderWidth = 2;
    int diameter;

    interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
    if (indicator->backgroundObj)
	border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
    else
	border = interior;
    if (indicator->borderWidthObj)
	Tcl_GetIntFromObj(NULL,indicator->borderWidthObj,&borderWidth);
    if (indicator->marginObj) {
	TTK_Padding margin;
	TTK_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&margin);
	adjustPadding(&margin, &x,&y,&width,&height);
    }
    Tk_GetReliefFromObj(NULL,indicator->reliefObj,&relief);

    /*
     * Ensure square, center in parcel:
     */
    diameter = adjustAspect(&x,&y, &width, &height);
    Tk_Fill3DRectangle(tkwin, d, interior, x, y, 
	    diameter, diameter,borderWidth, TK_RELIEF_FLAT);
    Tk_Draw3DRectangle(tkwin, d, border, x, y, 
	    diameter, diameter, borderWidth, relief);
}

/*
 * Radiobutton indicators:  3-D diamond.
 */
static void
DiamondIndicatorElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    IndicatorElement *indicator = elementRecord;
    int diameter = 0;
    TTK_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, paddingPtr);
    Tk_GetPixelsFromObj(NULL, tkwin, indicator->diameterObj, &diameter);
    *widthPtr = *heightPtr = diameter + 3;
}

static void
DiamondIndicatorElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    IndicatorElement *indicator = elementRecord;
    Tk_3DBorder border = 0, interior = 0;
    int borderWidth = 2;
    int relief = TK_RELIEF_RAISED;
    int diameter, radius;
    XPoint points[4];

    interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
    if (indicator->backgroundObj)
	border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
    else
	border = interior;
    if (indicator->borderWidthObj)
	Tcl_GetIntFromObj(NULL,indicator->borderWidthObj,&borderWidth);
    if (indicator->marginObj) {
	TTK_Padding margin;
	TTK_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&margin);
	adjustPadding(&margin, &x,&y,&width,&height);
    }
    Tk_GetReliefFromObj(NULL,indicator->reliefObj,&relief);

    /*
     * Ensure square, center in parcel:
     */
    diameter = adjustAspect(&x,&y, &width, &height);
    radius = diameter / 2;

    points[0].x = x;
    points[0].y = y + radius;
    points[1].x = x + radius;
    points[1].y = y + 2*radius;
    points[2].x = x + 2*radius;
    points[2].y = y + radius;
    points[3].x = x + radius;
    points[3].y = y;

    Tk_Fill3DPolygon(tkwin,d,interior,points,4,borderWidth,TK_RELIEF_FLAT);
    Tk_Draw3DPolygon(tkwin,d,border,points,4,borderWidth,relief);
}

static TTK_ElementSpec IndicatorElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(IndicatorElement),
    IndicatorElementOptions,
    SquareIndicatorElementGeometry,
    SquareIndicatorElementDraw
};

static TTK_ElementSpec RadiobuttonIndicatorElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(IndicatorElement),
    IndicatorElementOptions,
    DiamondIndicatorElementGeometry,
    DiamondIndicatorElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Arrow element(s)
 *
 * Draws a 3-D shaded triangle.
 *
 * clientData is an enum ArrowDirection pointer.
 */

enum { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };

/* ClientData storage:
 *
 * OK, it looks kind of silly doing things this way, but it
 * avoids casting pointers to integers.
 */
static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };

typedef struct
{
    Tcl_Obj *sizeObj;

    Tcl_Obj *borderObj;
    Tcl_Obj *borderWidthObj;
    Tcl_Obj *reliefObj;
} ArrowElement;

static TTK_ElementOptionSpec ArrowElementOptions[] =
{
    { "-width", TK_OPTION_PIXELS, Tk_Offset(ArrowElement,sizeObj),
	DEFAULT_ARROW_SIZE },
    { "-background", TK_OPTION_BORDER, Tk_Offset(ArrowElement,borderObj),
    	DEFAULT_BACKGROUND },
    { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(ArrowElement,borderWidthObj),
    	DEFAULT_BORDERWIDTH },
    { "-relief", TK_OPTION_RELIEF, Tk_Offset(ArrowElement,reliefObj),"raised" },
    { NULL }
};

static void
ArrowElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    ArrowElement *arrow = elementRecord;
    int size = 12;

    Tk_GetPixelsFromObj(NULL, tkwin, arrow->sizeObj, &size);
    *widthPtr = *heightPtr = size;
}

static void
ArrowElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    int direction = *(int *)clientData;
    ArrowElement *arrow = elementRecord;

    Tk_3DBorder border;
    int borderWidth;
    int relief = TK_RELIEF_RAISED;
    int size;
    XPoint points[3];

    Tk_GetPixelsFromObj(NULL, tkwin, arrow->borderWidthObj, &borderWidth);
    border = Tk_Get3DBorderFromObj(tkwin, arrow->borderObj);
    Tk_GetReliefFromObj(NULL, arrow->reliefObj, &relief);

    size = adjustAspect(&x,&y,&width,&height);

    /*
     * @@@ There are off-by-one pixel errors in the way these are drawn;
     * @@@ need to take a look at Tk_Fill3DPolygon and X11 to find the
     * @@@ exact rules.
     */
    switch (direction)
    {
	case ARROW_UP:
	    points[2].x = x; 		points[2].y = y + size;
	    points[1].x = x + size / 2;	points[1].y = y;
	    points[0].x = x + size;	points[0].y = y + size;
	    break;
	case ARROW_DOWN:
	    points[0].x = x; 		points[0].y = y;
	    points[1].x = x + size / 2;	points[1].y = y + size;
	    points[2].x = x + size;	points[2].y = y;
	    break;
	case ARROW_LEFT:
	    points[0].x = x; 		points[0].y = y + size / 2;
	    points[1].x = x + size;	points[1].y = y + size;
	    points[2].x = x + size;	points[2].y = y;
	    break;
	case ARROW_RIGHT:
	    points[0].x = x + size;	points[0].y = y + size / 2;
	    points[1].x = x;		points[1].y = y;
	    points[2].x = x;		points[2].y = y + size;
	    break;
    }

    Tk_Fill3DPolygon(tkwin, d, border, points, 3, borderWidth, relief);
}

static TTK_ElementSpec ArrowElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(ArrowElement),
    ArrowElementOptions,
    ArrowElementGeometry,
    ArrowElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Trough element
 *
 * Used in scrollbars and scales in place of "border".
 */

typedef struct
{
    Tcl_Obj *colorObj;
    Tcl_Obj *borderWidthObj;
    Tcl_Obj *reliefObj;
} TroughElement;

static TTK_ElementOptionSpec TroughElementOptions[] =
{
    { "-borderwidth", TK_OPTION_PIXELS,
	Tk_Offset(TroughElement,borderWidthObj), DEFAULT_BORDERWIDTH },
    { "-troughcolor", TK_OPTION_BORDER,
	Tk_Offset(TroughElement,colorObj), DEFAULT_BACKGROUND },
    { "-troughrelief",TK_OPTION_RELIEF,
	Tk_Offset(TroughElement,reliefObj), "sunken" },
    { NULL }
};

static void
TroughElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    TroughElement *troughPtr = elementRecord;
    int borderWidth = 2;

    Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);
    paddingPtr->left = paddingPtr->right = paddingPtr->top = paddingPtr->bottom
	= borderWidth;
}

static void
TroughElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    TroughElement *troughPtr = elementRecord;
    Tk_3DBorder border = NULL;
    int borderWidth = 2, relief = TK_RELIEF_SUNKEN;

    border = Tk_Get3DBorderFromObj(tkwin, troughPtr->colorObj);
    Tk_GetReliefFromObj(NULL, troughPtr->reliefObj, &relief);
    Tk_GetPixelsFromObj(NULL, tkwin, troughPtr->borderWidthObj, &borderWidth);

    Tk_Fill3DRectangle(tkwin, d, border, x, y, width, height,
	    borderWidth, relief);    
}

static TTK_ElementSpec TroughElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(TroughElement),
    TroughElementOptions,
    TroughElementGeometry,
    TroughElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Thumb element.
 *
 * Used in scrollbars.
 */

typedef struct
{
    Tcl_Obj *sizeObj;
    Tcl_Obj *reliefObj;
    Tcl_Obj *borderObj;
    Tcl_Obj *orientObj;
} ThumbElement;

static TTK_ElementOptionSpec ThumbElementOptions[] =
{
    { "-width", TK_OPTION_PIXELS, Tk_Offset(ThumbElement,sizeObj),
	DEFAULT_ARROW_SIZE },
    { "-relief", TK_OPTION_RELIEF,
	    Tk_Offset(ThumbElement,reliefObj), "raised" },
    { "-background", TK_OPTION_BORDER,
	    Tk_Offset(ThumbElement,borderObj), DEFAULT_BACKGROUND },
    { "-orient", TK_OPTION_ANY, 
	Tk_Offset(ThumbElement, orientObj), "horizontal" },
    { NULL }
};

static void
ThumbElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    ThumbElement *thumb = elementRecord;
    int orient, size;

    Tk_GetPixelsFromObj(NULL, tkwin, thumb->sizeObj, &size);
    TTK_GetOrientationFromObj(NULL, thumb->orientObj, &orient);

    if (orient == TTK_ORIENT_VERTICAL) {
	*widthPtr = size;
	*heightPtr = MIN_THUMB_SIZE;
    } else {
	*widthPtr = MIN_THUMB_SIZE;
	*heightPtr = size;
    }
}

static void
ThumbElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    ThumbElement *thumb = elementRecord;
    Tk_3DBorder  border = Tk_Get3DBorderFromObj(tkwin, thumb->borderObj);
    int borderWidth = 2;
    int relief = TK_RELIEF_RAISED;

    Tk_GetReliefFromObj(NULL, thumb->reliefObj, &relief);
    Tk_Fill3DRectangle(tkwin, d, border, x, y, width, height,
	    borderWidth, relief);
}

static TTK_ElementSpec ThumbElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(ThumbElement),
    ThumbElementOptions,
    ThumbElementGeometry,
    ThumbElementDraw
};

/*
 *----------------------------------------------------------------------
 * +++ Slider element.
 *
 * This is the moving part of the scale widget.  Drawn as a raised box.
 */

typedef struct
{
    Tcl_Obj *orientObj;	     /* orientation of overall slider */
    Tcl_Obj *lengthObj;      /* the length of the slider (if a flat style) */
    Tcl_Obj *widthObj;       /* the width of the slider (height if horizontal) */
    Tcl_Obj *reliefObj;      /* the relief for this object */
    Tcl_Obj *borderObj;      /* the background color */
    Tcl_Obj *borderWidthObj; /* the size of the border */
} SliderElement;

static TTK_ElementOptionSpec SliderElementOptions[] =
{
    { "-sliderlength", TK_OPTION_PIXELS, Tk_Offset(SliderElement,lengthObj),
	DEFAULT_SLIDER_LENGTH },
    { "-width", TK_OPTION_PIXELS, Tk_Offset(SliderElement,widthObj),
	"3" },
    { "-sliderrelief", TK_OPTION_RELIEF, Tk_Offset(SliderElement,reliefObj),
	"raised" },
    { "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(SliderElement,borderWidthObj),
	DEFAULT_BORDERWIDTH },
    { "-background", TK_OPTION_BORDER, Tk_Offset(SliderElement,borderObj),
	DEFAULT_BACKGROUND },
    { "-orient", TK_OPTION_ANY, Tk_Offset(SliderElement,orientObj),
	"horizontal" },
    { NULL }
};

static void
SliderElementGeometry(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, int *widthPtr, int *heightPtr, TTK_Padding *paddingPtr)
{
    SliderElement *slider = elementRecord;
    int borderWidth, sliderwidth, sliderlength, orient;

    TTK_GetOrientationFromObj(NULL, slider->orientObj, &orient);
    Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
    Tk_GetPixelsFromObj(NULL, tkwin, slider->lengthObj, &sliderlength);
    Tk_GetPixelsFromObj(NULL, tkwin, slider->widthObj, &sliderwidth);

    switch (orient) {
	case TTK_ORIENT_VERTICAL:
	    *widthPtr = sliderwidth;
	    *heightPtr = sliderlength;
	    break;

	case TTK_ORIENT_HORIZONTAL:
	    *heightPtr = sliderwidth;
	    *widthPtr = sliderlength;
	    break;
    }
}

static void
SliderElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    SliderElement *slider = elementRecord;
    Tk_3DBorder border = NULL;
    int relief, borderWidth, orient;

    border = Tk_Get3DBorderFromObj(tkwin, slider->borderObj);
    TTK_GetOrientationFromObj(NULL, slider->orientObj, &orient);
    Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, slider->reliefObj, &relief);

    Tk_Fill3DRectangle(tkwin, d, border,
	x, y, width, height,
	borderWidth, relief);

    if (relief != TK_RELIEF_FLAT) {
	if (orient == TTK_ORIENT_HORIZONTAL) {
	    if (width > 4) {
		x += width/2;
		XDrawLine(Tk_Display(tkwin), d, 
		    Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
		    x-1, y+borderWidth, x-1, y+height-borderWidth);
		XDrawLine(Tk_Display(tkwin), d, 
		    Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC),
		    x, y+borderWidth, x, y+height-borderWidth);
	    }
	} else {
	    if (height > 4) {
		y += height/2;
		XDrawLine(Tk_Display(tkwin), d,
		    Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC),
		    x+borderWidth, y-1, x+width-borderWidth, y-1);
		XDrawLine(Tk_Display(tkwin), d, 
		    Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC),
		    x+borderWidth, y, x+width-borderWidth, y);
	    }
	}
    }
}

static TTK_ElementSpec SliderElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(SliderElement),
    SliderElementOptions,
    SliderElementGeometry,
    SliderElementDraw
};



/*
 *----------------------------------------------------------------------
 * +++ Progress element:
 *
 * A progress bar.
 * @@@ reuses most of the slider element 
 */

static void
ProgressElementDraw(
    void *clientData, void *elementRecord,
    Tk_Window tkwin, Drawable d,
    int x, int y, int width, int height,
    unsigned int state)
{
    SliderElement *slider = elementRecord;
    Tk_3DBorder border = NULL;
    int relief, borderWidth, orient;

    border = Tk_Get3DBorderFromObj(tkwin, slider->borderObj);
    TTK_GetOrientationFromObj(NULL, slider->orientObj, &orient);
    Tk_GetPixelsFromObj(NULL, tkwin, slider->borderWidthObj, &borderWidth);
    Tk_GetReliefFromObj(NULL, slider->reliefObj, &relief);

    Tk_Fill3DRectangle(tkwin, d, border,
	x, y, width, height,
	borderWidth, relief);
}

static TTK_ElementSpec ProgressElementSpec =
{
    TK_STYLE_VERSION_2,
    sizeof(SliderElement),
    SliderElementOptions,
    SliderElementGeometry,
    ProgressElementDraw
};

/*------------------------------------------------------------------------
 * Layouts:
 */

TTK_BEGIN_LAYOUT(LabelLayout)
    TTK_LAYOUT_DRAW("Label.background")
    TTK_LAYOUT_INSIDE("Label.border",
	TTK_LAYOUT_INSIDE("Label.padding",
	    TTK_LAYOUT_HFILL("Label.compound")))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(ButtonLayout)
    TTK_LAYOUT_DRAW("Button.background")
    TTK_LAYOUT_INSIDE("Button.highlight", 
        TTK_LAYOUT_INSIDE("Button.border",
	    TTK_LAYOUT_INSIDE("Button.padding",
	        TTK_LAYOUT_HFILL("Button.compound"))))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(CheckbuttonLayout)
    TTK_LAYOUT_DRAW("Checkbutton.background")
    TTK_LAYOUT_INSIDE("Checkbutton.highlight",
        TTK_LAYOUT_INSIDE("Checkbutton.border",
	    TTK_LAYOUT_INSIDE("Checkbutton.padding",
	        TTK_LAYOUT_LEFT("Checkbutton.indicator")
	        TTK_LAYOUT_HFILL("Checkbutton.compound"))))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(RadiobuttonLayout)
    TTK_LAYOUT_DRAW("Radiobutton.background")
    TTK_LAYOUT_INSIDE("Radiobutton.highlight",
        TTK_LAYOUT_INSIDE("Radiobutton.border",
            TTK_LAYOUT_INSIDE("Radiobutton.padding",
	        TTK_LAYOUT_LEFT("Radiobutton.indicator")
	        TTK_LAYOUT_HFILL("Radiobutton.compound"))))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(VerticalScrollbarLayout)
    TTK_LAYOUT_DRAW("Scrollbar.background")
    TTK_LAYOUT_INSIDE("Scrollbar.trough", 
	TTK_LAYOUT_TOP("Scrollbar.uparrow")
	TTK_LAYOUT_BOTTOM("Scrollbar.downarrow")
	TTK_LAYOUT_VFILL("Scrollbar.thumb")   )
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(HorizontalScrollbarLayout)
    TTK_LAYOUT_DRAW("Scrollbar.background")
    TTK_LAYOUT_INSIDE("Scrollbar.trough",
	TTK_LAYOUT_LEFT("Scrollbar.leftarrow")
	TTK_LAYOUT_RIGHT("Scrollbar.rightarrow")
	TTK_LAYOUT_HFILL("Scrollbar.thumb")   )
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(VerticalScaleLayout)
    TTK_LAYOUT_DRAW("Scale.background")
    TTK_LAYOUT_INSIDE("Scale.highlight",
	TTK_LAYOUT_INSIDE("Scale.padding",
	    TTK_LAYOUT_INSIDE("Scale.trough",
		TTK_LAYOUT_TOP("Scale.slider") )))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(HorizontalScaleLayout)
    TTK_LAYOUT_DRAW("Scale.background")
    TTK_LAYOUT_INSIDE("Scale.highlight", 
	TTK_LAYOUT_INSIDE("Scale.padding",
	    TTK_LAYOUT_INSIDE("Scale.trough",
		TTK_LAYOUT_LEFT("Scale.slider") )))
TTK_END_LAYOUT

TTK_BEGIN_LAYOUT(ProgressBarLayout)
    TTK_LAYOUT_INSIDE("Progress.trough",
	TTK_LAYOUT_TOP("Progress.bar"))
TTK_END_LAYOUT

/*----------------------------------------------------------------------
 * RegisterElements --
 *
 *	Register all elements and layouts defined in this package.
 */
void RegisterElements(Tcl_Interp *interp)
{
    TTK_Theme themePtr =  TTK_GetTheme(interp, NULL);

    /*
     * Elements:
     */
    TTK_RegisterElementSpec(themePtr, "background",
	    &BackgroundElementSpec,NULL);
    TTK_RegisterElementSpec(themePtr, "border", &BorderElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "Button.border",
	    &ButtonBorderElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "Checkbutton.border",
	    &PushableBorderElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "Radiobutton.border",
	    &PushableBorderElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "padding", &PaddingElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "text", &TextElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "image", &ImageElementSpec, interp);
    TTK_RegisterElementSpec(themePtr, "compound", &CompoundElementSpec, interp);
    TTK_RegisterElementSpec(themePtr, "indicator",&IndicatorElementSpec,NULL);
    TTK_RegisterElementSpec(themePtr, "Radiobutton.indicator",
	    &RadiobuttonIndicatorElementSpec, NULL);

    TTK_RegisterElementSpec(themePtr, "uparrow",
	    &ArrowElementSpec, &ArrowElements[0]);
    TTK_RegisterElementSpec(themePtr, "downarrow",
	    &ArrowElementSpec, &ArrowElements[1]);
    TTK_RegisterElementSpec(themePtr, "leftarrow",
	    &ArrowElementSpec, &ArrowElements[2]);
    TTK_RegisterElementSpec(themePtr, "rightarrow",
	    &ArrowElementSpec, &ArrowElements[3]);
    TTK_RegisterElementSpec(themePtr, "arrow",
	    &ArrowElementSpec, &ArrowElements[0]);

    TTK_RegisterElementSpec(themePtr, "trough", &TroughElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "thumb", &ThumbElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "slider", &SliderElementSpec, NULL);
    TTK_RegisterElementSpec(themePtr, "highlight", &HighlightElementSpec, NULL);

    TTK_RegisterElementSpec(themePtr,"Progress.bar",&ProgressElementSpec,NULL);

    /*
     * Layouts:
     */
    TTK_RegisterLayout(themePtr, "TLabel", LabelLayout);
    TTK_RegisterLayout(themePtr, "TButton", ButtonLayout);
    TTK_RegisterLayout(themePtr, "TCheckbutton", CheckbuttonLayout);
    TTK_RegisterLayout(themePtr, "TRadiobutton", RadiobuttonLayout);
    TTK_RegisterLayout(themePtr,
	    "Vertical.TScrollbar", VerticalScrollbarLayout);
    TTK_RegisterLayout(themePtr,
	    "Horizontal.TScrollbar", HorizontalScrollbarLayout);
    TTK_RegisterLayout(themePtr,
	    "Vertical.TScale", VerticalScaleLayout);
    TTK_RegisterLayout(themePtr,
	    "Horizontal.TScale", HorizontalScaleLayout);
    TTK_RegisterLayout(themePtr, "TProgress",  ProgressBarLayout);
}

/*EOF*/