/* stepTheme.c - Copyright (C) 2003 Pat Thoyts <patthoyts@users.sf.net>
*
* A OpenStep/NeXTStep style theme.
*
* This illustrates the need to be able to adjust the widget layout per
* style engine.
*
*/
#include <tk.h>
#include "tkTheme.h"
/* ---------------------------------------------------------------------- */
#define DEFAULT_BORDERWIDTH "2"
static GC
GetBlackGC(Tk_Window tkwin)
{
XGCValues gcvalues;
gcvalues.foreground = BlackPixelOfScreen(Tk_Screen(tkwin));
return Tk_GetGC(tkwin, GCForeground, &gcvalues);
}
#ifdef PAT_EXPERIMENTAL
static GC
GetWhiteGC(Tk_Window tkwin)
{
XGCValues gcvalues;
gcvalues.foreground = WhitePixelOfScreen(Tk_Screen(tkwin));
return Tk_GetGC(tkwin, GCForeground, &gcvalues);
}
static void
DrawRing(Display *display, Drawable d, GC gc, int x, int y, int a, int b, int i)
{
XPoint pt[10];
int n = 0;
pt[n].x = x + a + i; pt[n++].y = y + i;
pt[n].x = b; pt[n++].y = 0;
pt[n].x = a; pt[n++].y = a;
pt[n].x = 0; pt[n++].y = b;
pt[n].x = -a; pt[n++].y = a;
pt[n].x = -b; pt[n++].y = 0;
pt[n].x = -a; pt[n++].y = -a;
pt[n].x = 0; pt[n++].y = -b;
pt[n].x = a; pt[n++].y = -a;
XDrawLines(display, d, gc, pt, n, CoordModePrevious);
if (! (i & 1)) {
pt[0].x = x + i + a + b - 1; pt[0].y = y + i;
pt[1].x = x + i + a + b + a; pt[1].y = y + i + a + 1;
XDrawLines(display, d, gc, pt, 2, CoordModeOrigin);
pt[0].x = x + i + a + b - 1; pt[0].y = y + i + a + b + a;
pt[1].x = x + i + a + b + a; pt[1].y = y + i + a + b - 1;
XDrawLines(display, d, gc, pt, 2, CoordModeOrigin);
pt[1].x = x + i + a + 1; pt[1].y = y + i;
pt[0].x = x + i; pt[0].y = y + i + a + 1;
XDrawLines(display, d, gc, pt, 2, CoordModeOrigin);
pt[0].x = x + i; pt[0].y = y + i + a + b - 1;
pt[1].x = x + i + a + 1; pt[1].y = y + i + a + b + a;
XDrawLines(display, d, gc, pt, 2, CoordModeOrigin);
}
}
static void
Draw3DCircle(Tk_Window tkwin, Drawable d, int x, int y, int diameter, Tk_3DBorder border)
{
GC gcLight, gcDark, gcBlack;
Display *display = Tk_Display(tkwin);
diameter = diameter | 1;
gcLight = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
gcDark = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
gcBlack = GetBlackGC(tkwin);
XFillArc(display, d, gcLight, x, y,
(unsigned)diameter, (unsigned)diameter, 0, 360*64);
DrawRing(display, d, gcDark, x, y, 3, 7, 0); /* ring - 0 */
DrawRing(display, d, gcBlack, x, y, 3, 5, 1); /* ring - 1 */
//DrawRing(display, d, gcDark, x, y, 2, 5, 2); /* ring - 2 */
//DrawRing(display, d, gcBlack, x, y, 2, 3, 3); /* ring - 3 */
//DrawRing(display, d, gcDark, x, y, 1, 3, 4); /* ring - 4 */
//DrawRing(display, d, gcBlack, x, y, 1, 1, 5); /* ring - 5 */
//XDrawArc(display, d, gcDark, x, y, diameter, diameter, 30*64, 210*64 /*0, 360*64*/);
//XDrawArc(display, d, gcBlack, x+1, y+1, diameter-1, diameter-1, 30*64, 210*64);
// XDrawArc(display, d, gcLight, x+1, y+1, diameter-2, diameter-2, 30*64, 210*64);
//XDrawArc(display, d, gcDark, x-1, y-1, diameter-2, diameter-2, 230*64, 170*64);
Tk_FreeGC(Tk_Display(tkwin), gcBlack);
}
#endif /*PAT_EXPERIMENTAL*/
/*
*----------------------------------------------------------------------
*
* DrawCheckMark --
*
* Draw an OPENSTEP style check indicator. This is a 8x8 3D tick.
*
*----------------------------------------------------------------------
*/
static void
DrawCheckMark(Tk_Window tkwin, Drawable d, int x, int y, Tk_3DBorder border)
{
GC gcLight, gcDark, gcBlack;
Display *display = Tk_Display(tkwin);
gcBlack = GetBlackGC(tkwin);
gcLight = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
gcDark = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
XDrawLine(display, d, gcBlack, x+1, y+3, x+1, y+6);
XDrawLine(display, d, gcLight, x, y+3, x, y+8);
XDrawLine(display, d, gcLight, x, y+8, x+8, y);
XDrawLine(display, d, gcBlack, x, y+9, x+8, y+1);
XDrawLine(display, d, gcDark, x+1, y+9, x+8, y+2);
Tk_FreeGC(Tk_Display(tkwin), gcBlack);
}
/*
* Element specifications
*/
/*
*----------------------------------------------------------------------
* +++ 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.
*
*/
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: 3-D square with tick
*/
static void
CheckIndicatorElementGeometry(
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 + 2;
}
static void
CheckIndicatorElementDraw(
void *clientData, void *elementRecord,
Tk_Window tkwin, Drawable d,
Ttk_Box b, unsigned int state)
{
IndicatorElement *indicator = elementRecord;
Tk_3DBorder border = 0, interior = 0;
int relief = TK_RELIEF_RAISED;
int borderWidth = 2;
int diameter;
Ttk_Padding margin;
interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
Tcl_GetIntFromObj(NULL, indicator->borderWidthObj, &borderWidth);
Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&margin);
Tk_GetReliefFromObj(NULL, indicator->reliefObj, &relief);
b = Ttk_PadBox(b, margin);
diameter = b.width < b.height ? b.width : b.height;
Tk_Fill3DRectangle(tkwin, d, interior, b.x, b.y,
diameter, diameter, borderWidth, relief);
if (state & TTK_STATE_SELECTED)
DrawCheckMark(tkwin, d, b.x + borderWidth, b.y + borderWidth, border);
}
/*
* Radiobutton indicators: filled sunken circle.
*/
static void
RadioIndicatorElementGeometry(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
RadioIndicatorElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, unsigned int state)
{
IndicatorElement *indicator = elementRecord;
Display *display = Tk_Display(tkwin);
Tk_3DBorder border = 0, interior = 0;
int borderWidth = 2;
unsigned int diameter, radius;
Ttk_Padding margin;
interior = Tk_Get3DBorderFromObj(tkwin, indicator->colorObj);
border = Tk_Get3DBorderFromObj(tkwin, indicator->backgroundObj);
Tcl_GetIntFromObj(NULL,indicator->borderWidthObj,&borderWidth);
Ttk_GetPaddingFromObj(NULL,tkwin,indicator->marginObj,&margin);
b = Ttk_PadBox(b, margin);
/*
* Draw
*/
diameter = b.width < b.height ? b.width : b.height;
radius = diameter / 2;
#ifdef PAT_EXPERIMENTAL
Draw3DCircle(tkwin, d, x, y, diameter, border);
#else
{
GC gcA, gcB;
unsigned int r = radius;
gcA = Tk_3DBorderGC(tkwin, border, TK_3D_LIGHT_GC);
gcB = Tk_3DBorderGC(tkwin, border, TK_3D_DARK_GC);
XSetLineAttributes(display, gcB, 1, LineSolid, CapRound, JoinRound );
XDrawArc(display, d, gcB, b.x, b.y, diameter, diameter, 0, 360*64);
XSetLineAttributes(display, gcA, 1, LineSolid, CapRound, JoinRound );
XDrawArc(display, d, gcA, b.x+1, b.y+1, 2*r-2, 2*r-2, 30*64, 210*64);
XDrawArc(display, d, gcA, b.x-1, b.y-1, 2*r+2, 2*r+2, 230*64, 170*64);
}
#endif /* PAT_EXPERIMENTAL */
/* draw the center spot */
if (state & TTK_STATE_SELECTED) {
GC gcBlack = GetBlackGC(tkwin);
/*
* Draw this dot in two parts to avoid a sliced off edge under win32
*/
radius -= borderWidth;
radius /= 2;
b.x += borderWidth, b.y += borderWidth;
b.x += radius, b.y += radius;
XFillArc(display, d, gcBlack, b.x,b.y, radius*2,radius*2, 1, 12672);
XFillArc(display, d, gcBlack, b.x,b.y, radius*2,radius*2, 12672, 23040);
Tk_FreeGC(Tk_Display(tkwin), gcBlack);
}
}
static Ttk_ElementSpec CheckbuttonIndicatorElementSpec =
{
TK_STYLE_VERSION_2,
sizeof(IndicatorElement),
IndicatorElementOptions,
CheckIndicatorElementGeometry,
CheckIndicatorElementDraw
};
static Ttk_ElementSpec RadiobuttonIndicatorElementSpec =
{
TK_STYLE_VERSION_2,
sizeof(IndicatorElement),
IndicatorElementOptions,
RadioIndicatorElementGeometry,
RadioIndicatorElementDraw
};
/*
*----------------------------------------------------------------------
* +++ Thumb element.
*
*/
#define MIN_THUMB_SIZE 8
typedef struct
{
Tcl_Obj *orientObj;
Tcl_Obj *sizeObj;
Tcl_Obj *borderObj;
Tcl_Obj *reliefObj;
} ThumbElement;
static Ttk_ElementOptionSpec ThumbElementOptions[] =
{
{ "-orient", TK_OPTION_ANY,Tk_Offset(ThumbElement,orientObj),"horizontal"},
{ "-width", TK_OPTION_PIXELS, Tk_Offset(ThumbElement,sizeObj), "8" },
{ "-background", TK_OPTION_BORDER,
Tk_Offset(ThumbElement,borderObj), DEFAULT_BACKGROUND },
{ "-relief", TK_OPTION_RELIEF, Tk_Offset(ThumbElement,reliefObj), "raised"},
{ 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,
Ttk_Box b, unsigned int state)
{
ThumbElement *thumb = elementRecord;
Tk_3DBorder border = NULL;
int borderWidth = 2;
border = Tk_Get3DBorderFromObj(tkwin, thumb->borderObj);
Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y, b.width, b.height,
borderWidth, TK_RELIEF_RAISED);
b.x += (b.width/2) - borderWidth;
b.y += (b.height/2) - borderWidth;
Tk_Fill3DRectangle(tkwin, d, border, b.x, b.y,
borderWidth*2, borderWidth * 2, borderWidth, TK_RELIEF_SUNKEN);
}
static Ttk_ElementSpec ThumbElementSpec =
{
TK_STYLE_VERSION_2,
sizeof(ThumbElement),
ThumbElementOptions,
ThumbElementGeometry,
ThumbElementDraw
};
/*------------------------------------------------------------------------
* +++ Progress bar element.
* Now with stripes!
*/
#if 0
/* @@@ Possibly: split "stripes" out into its own element, and use
* @@@ something like the following instead:
*/
TTK_BEGIN_LAYOUT(VerticalProgressbarLayout)
TTK_GROUP("Vertical.Progressbar.trough", TTK_FILL_BOTH,
TTK_GROUP("Vertical.Progressbar.pbar", TTK_PACK_BOTTOM,
TTK_NODE("Vertical.Progressbar.stripes", TTK_EXPAND)))
TTK_END_LAYOUT
TTK_BEGIN_LAYOUT(HorizontalProgressbarLayout)
TTK_GROUP("Horizontal.Progressbar.trough", TTK_FILL_BOTH,
TTK_GROUP("Horizontal.Progressbar.pbar", TTK_PACK_LEFT,
TTK_NODE("Horizontal.Progressbar.stripes", TTK_EXPAND)))
TTK_END_LAYOUT
#endif
#define DEFAULT_PBAR_THICKNESS "12"
#define DEFAULT_PBAR_LENGTH "24"
#define DEFAULT_PBAR_STRIPE "6"
typedef struct
{
Tcl_Obj *orientObj; /* widget orientation */
Tcl_Obj *thicknessObj; /* the height/width of the bar */
Tcl_Obj *lengthObj; /* default width/height of the bar */
Tcl_Obj *reliefObj; /* border relief for this object */
Tcl_Obj *borderWidthObj; /* thickness of the border */
Tcl_Obj *borderObj; /* background color */
Tcl_Obj *stripeColorObj; /* color for stripes */
Tcl_Obj *stripeThicknessObj; /* stripe thickness */
Tcl_Obj *phaseObj; /* animation phase */
} PbarElement;
static Ttk_ElementOptionSpec PbarElementOptions[] =
{
{ "-orient", TK_OPTION_ANY, Tk_Offset(PbarElement,orientObj),
"horizontal" },
{ "-thickness", TK_OPTION_PIXELS, Tk_Offset(PbarElement,thicknessObj),
DEFAULT_PBAR_THICKNESS },
{ "-barsize", TK_OPTION_PIXELS, Tk_Offset(PbarElement,lengthObj),
DEFAULT_PBAR_LENGTH },
{ "-pbarrelief", TK_OPTION_RELIEF, Tk_Offset(PbarElement,reliefObj),
"raised" },
{ "-borderwidth", TK_OPTION_PIXELS, Tk_Offset(PbarElement,borderWidthObj),
DEFAULT_BORDERWIDTH },
{ "-background", TK_OPTION_BORDER, Tk_Offset(PbarElement,borderObj),
DEFAULT_BACKGROUND },
{ "-stripecolor", TK_OPTION_COLOR, Tk_Offset(PbarElement,stripeColorObj),
DEFAULT_FOREGROUND },
{ "-stripethickness", TK_OPTION_INT,
Tk_Offset(PbarElement,stripeThicknessObj), DEFAULT_PBAR_STRIPE },
{ "-phase", TK_OPTION_INT, Tk_Offset(PbarElement,phaseObj),
"0" },
{ NULL }
};
static void PbarElementGeometry(
void *clientData, void *elementRecord, Tk_Window tkwin,
int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
PbarElement *pbar = elementRecord;
int orient, thickness, length, borderWidth;
Ttk_GetOrientationFromObj(NULL, pbar->orientObj, &orient);
Tk_GetPixelsFromObj(NULL, tkwin, pbar->thicknessObj, &thickness);
Tk_GetPixelsFromObj(NULL, tkwin, pbar->lengthObj, &length);
Tk_GetPixelsFromObj(NULL, tkwin, pbar->borderWidthObj, &borderWidth);
switch (orient) {
case TTK_ORIENT_HORIZONTAL:
*widthPtr = length;
*heightPtr = thickness;
break;
case TTK_ORIENT_VERTICAL:
*widthPtr = thickness;
*heightPtr = length;
break;
}
*paddingPtr = Ttk_UniformPadding((short)borderWidth);
}
static void PbarElementDrawStripes(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
PbarElement *pbar = elementRecord;
Display *display = Tk_Display(tkwin);
XColor *stripeColor = Tk_GetColorFromObj(tkwin, pbar->stripeColorObj);
GC stripeGC = Tk_GCForColor(stripeColor, d);
int phase = 0, stripeThickness = 6, st;
int orient = TTK_ORIENT_HORIZONTAL;
int i;
Tcl_GetIntFromObj(NULL, pbar->phaseObj, &phase);
Tcl_GetIntFromObj(NULL, pbar->stripeThicknessObj, &stripeThickness);
Ttk_GetOrientationFromObj(NULL, pbar->orientObj, &orient);
switch (orient) {
case TTK_ORIENT_HORIZONTAL:
st = 2 * stripeThickness;
for (i=0; i < b.width - 1; ++i) {
int x0 = b.x + i, y0 = b.y;
int dx = b.height - 1;
int on = ((st + (x0 % st) - (phase % st)) / stripeThickness) % 2;
if (!on) continue;
if (x0 + dx >= b.x + b.width)
dx = b.x + b.width - 1 - x0;
XDrawLine(display, d, stripeGC, x0,y0, x0 + dx,y0 + dx);
}
break;
case TTK_ORIENT_VERTICAL:
for (i=0; i <= b.height - 1; ++i) {
int x0 = b.x, y0 = b.y + b.height - i;
int dy = b.width - 1;
int on = ((y0 + phase) / stripeThickness) % 2;
if (!on) continue;
if (y0 - dy < b.y)
dy = y0 - b.y;
XDrawLine(display, d, stripeGC, x0,y0, x0 + dy,y0 - dy);
}
}
}
static void PbarElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
PbarElement *pbar = elementRecord;
Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, pbar->borderObj);
int relief, borderWidth;
Tk_GetPixelsFromObj(NULL, tkwin, pbar->borderWidthObj, &borderWidth);
Tk_GetReliefFromObj(NULL, pbar->reliefObj, &relief);
Tk_Fill3DRectangle(tkwin, d, border,
b.x, b.y, b.width, b.height,
borderWidth, relief);
b = Ttk_PadBox(b, Ttk_UniformPadding(borderWidth));
PbarElementDrawStripes(clientData, elementRecord, tkwin, d, b, state);
}
static Ttk_ElementSpec PbarElementSpec = {
TK_STYLE_VERSION_2,
sizeof(PbarElement),
PbarElementOptions,
PbarElementGeometry,
PbarElementDraw
};
/* ----------------------------------------------------------------------
*
* Widget specifications
*
* ----------------------------------------------------------------------
*/
TTK_BEGIN_LAYOUT(VerticalStepScrollbarLayout)
TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_BOTH,
TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP|TTK_FILL_X)
TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM|TTK_FILL_X)
TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_BOTTOM|TTK_FILL_X)
TTK_NODE("Vertical.Scrollbar.thumb", TTK_PACK_TOP|TTK_EXPAND|TTK_FILL_BOTH) )
TTK_END_LAYOUT
TTK_BEGIN_LAYOUT(HorizontalStepScrollbarLayout)
TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_BOTH,
TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT|TTK_FILL_Y)
TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT|TTK_FILL_Y)
TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_RIGHT|TTK_FILL_Y)
TTK_NODE("Horizontal.Scrollbar.thumb", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH) )
TTK_END_LAYOUT
int DLLEXPORT
StepTheme_Init(Tcl_Interp *interp)
{
Ttk_Theme theme, parentPtr;
/*
* Create the new style engine.
*/
parentPtr = Ttk_GetTheme(interp, "alt");
theme = Ttk_CreateTheme(interp, "step", parentPtr);
if (!theme)
return TCL_ERROR;
/*
* Register any new elements required for this style engine.
*/
Ttk_RegisterElement(interp, theme, "Checkbutton.indicator",
&CheckbuttonIndicatorElementSpec, NULL);
Ttk_RegisterElement(interp, theme, "Radiobutton.indicator",
&RadiobuttonIndicatorElementSpec, NULL);
Ttk_RegisterElement(interp, theme, "thumb", &ThumbElementSpec, NULL);
Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL);
/*
* Register any new widget layouts.
*/
Ttk_RegisterLayout(theme, "Vertical.TScrollbar",
VerticalStepScrollbarLayout);
Ttk_RegisterLayout(theme, "Horizontal.TScrollbar",
HorizontalStepScrollbarLayout);
Tcl_PkgProvide(interp, "tile::theme::step", "0.0.2");
return TCL_OK;
}
/*EOF*/