1/*
2 * $Id$
3 *
4 * Copyright (C) 2004 Joe English
5 *
6 * "clam" theme; inspired by the XFCE family of Gnome themes.
7 */
8
9#include <tk.h>
10#include "ttkTheme.h"
11
12/*
13 * Under windows, the Tk-provided XDrawLine and XDrawArc have an
14 * off-by-one error in the end point. This is especially apparent with this
15 * theme. Defining this macro as true handles this case.
16 */
17#if defined(WIN32) && !defined(WIN32_XDRAWLINE_HACK)
18#	define WIN32_XDRAWLINE_HACK 1
19#else
20#	define WIN32_XDRAWLINE_HACK 0
21#endif
22
23#define STR(x) StR(x)
24#define StR(x) #x
25
26#define SCROLLBAR_THICKNESS 14
27
28#define FRAME_COLOR	"#dcdad5"
29#define LIGHT_COLOR  	"#ffffff"
30#define DARK_COLOR  	"#cfcdc8"
31#define DARKER_COLOR 	"#bab5ab"
32#define DARKEST_COLOR	"#9e9a91"
33
34/*------------------------------------------------------------------------
35 * +++ Utilities.
36 */
37
38static GC Ttk_GCForColor(Tk_Window tkwin, Tcl_Obj* colorObj, Drawable d)
39{
40    GC gc = Tk_GCForColor(Tk_GetColorFromObj(tkwin, colorObj), d);
41
42#ifdef MAC_OSX_TK
43    /*
44     * Workaround for Tk bug under Aqua where the default line width is 0.
45     */
46    Display *display = Tk_Display(tkwin);
47    unsigned long mask = 0ul;
48    XGCValues gcValues;
49
50    gcValues.line_width = 1;
51    mask = GCLineWidth;
52
53    XChangeGC(display, gc, mask, &gcValues);
54#endif
55
56    return gc;
57}
58
59static void DrawSmoothBorder(
60    Tk_Window tkwin, Drawable d, Ttk_Box b,
61    Tcl_Obj *outerColorObj, Tcl_Obj *upperColorObj, Tcl_Obj *lowerColorObj)
62{
63    Display *display = Tk_Display(tkwin);
64    int x1 = b.x, x2 = b.x + b.width - 1;
65    int y1 = b.y, y2 = b.y + b.height - 1;
66    const int w = WIN32_XDRAWLINE_HACK;
67    GC gc;
68
69    if (   outerColorObj
70	&& (gc=Ttk_GCForColor(tkwin,outerColorObj,d)))
71    {
72	XDrawLine(display,d,gc, x1+1,y1, x2-1+w,y1); /* N */
73	XDrawLine(display,d,gc, x1+1,y2, x2-1+w,y2); /* S */
74	XDrawLine(display,d,gc, x1,y1+1, x1,y2-1+w); /* E */
75	XDrawLine(display,d,gc, x2,y1+1, x2,y2-1+w); /* W */
76    }
77
78    if (   upperColorObj
79	&& (gc=Ttk_GCForColor(tkwin,upperColorObj,d)))
80    {
81	XDrawLine(display,d,gc, x1+1,y1+1, x2-1+w,y1+1); /* N */
82	XDrawLine(display,d,gc, x1+1,y1+1, x1+1,y2-1);   /* E */
83    }
84
85    if (   lowerColorObj
86	&& (gc=Ttk_GCForColor(tkwin,lowerColorObj,d)))
87    {
88	XDrawLine(display,d,gc, x2-1,y2-1, x1+1-w,y2-1); /* S */
89	XDrawLine(display,d,gc, x2-1,y2-1, x2-1,y1+1-w); /* W */
90    }
91}
92
93static GC BackgroundGC(Tk_Window tkwin, Tcl_Obj *backgroundObj)
94{
95    Tk_3DBorder bd = Tk_Get3DBorderFromObj(tkwin, backgroundObj);
96    return Tk_3DBorderGC(tkwin, bd, TK_3D_FLAT_GC);
97}
98
99/*------------------------------------------------------------------------
100 * +++ Border element.
101 */
102
103typedef struct {
104    Tcl_Obj 	*borderColorObj;
105    Tcl_Obj 	*lightColorObj;
106    Tcl_Obj 	*darkColorObj;
107    Tcl_Obj 	*reliefObj;
108    Tcl_Obj 	*borderWidthObj;	/* See <<NOTE-BORDERWIDTH>> */
109} BorderElement;
110
111static Ttk_ElementOptionSpec BorderElementOptions[] = {
112    { "-bordercolor", TK_OPTION_COLOR,
113	Tk_Offset(BorderElement,borderColorObj), DARKEST_COLOR },
114    { "-lightcolor", TK_OPTION_COLOR,
115	Tk_Offset(BorderElement,lightColorObj), LIGHT_COLOR },
116    { "-darkcolor", TK_OPTION_COLOR,
117	Tk_Offset(BorderElement,darkColorObj), DARK_COLOR },
118    { "-relief", TK_OPTION_RELIEF,
119	Tk_Offset(BorderElement,reliefObj), "flat" },
120    { "-borderwidth", TK_OPTION_PIXELS,
121	Tk_Offset(BorderElement,borderWidthObj), "2" },
122    { NULL, 0, 0, NULL }
123};
124
125/*
126 * <<NOTE-BORDERWIDTH>>: -borderwidth is only partially supported:
127 * in this theme, borders are always exactly 2 pixels thick.
128 * With -borderwidth 0, border is not drawn at all;
129 * otherwise a 2-pixel border is used.  For -borderwidth > 2,
130 * the excess is used as padding.
131 */
132
133static void BorderElementSize(
134    void *clientData, void *elementRecord, Tk_Window tkwin,
135    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
136{
137    BorderElement *border = (BorderElement*)elementRecord;
138    int borderWidth = 2;
139    Tk_GetPixelsFromObj(NULL, tkwin, border->borderWidthObj, &borderWidth);
140    if (borderWidth == 1) ++borderWidth;
141    *paddingPtr = Ttk_UniformPadding((short)borderWidth);
142}
143
144static void BorderElementDraw(
145    void *clientData, void *elementRecord, Tk_Window tkwin,
146    Drawable d, Ttk_Box b, unsigned state)
147{
148    BorderElement *border = elementRecord;
149    int relief = TK_RELIEF_FLAT;
150    int borderWidth = 2;
151    Tcl_Obj *outer = 0, *upper = 0, *lower = 0;
152
153    Tk_GetReliefFromObj(NULL, border->reliefObj, &relief);
154    Tk_GetPixelsFromObj(NULL, tkwin, border->borderWidthObj, &borderWidth);
155
156    if (borderWidth == 0) return;
157
158    switch (relief) {
159	case TK_RELIEF_GROOVE :
160	case TK_RELIEF_RIDGE :
161	case TK_RELIEF_RAISED :
162	    outer = border->borderColorObj;
163	    upper = border->lightColorObj;
164	    lower = border->darkColorObj;
165	    break;
166	case TK_RELIEF_SUNKEN :
167	    outer = border->borderColorObj;
168	    upper = border->darkColorObj;
169	    lower = border->lightColorObj;
170	    break;
171	case TK_RELIEF_FLAT :
172	    outer = upper = lower = 0;
173	    break;
174	case TK_RELIEF_SOLID :
175	    outer = upper = lower = border->borderColorObj;
176	    break;
177    }
178
179    DrawSmoothBorder(tkwin, d, b, outer, upper, lower);
180}
181
182static Ttk_ElementSpec BorderElementSpec = {
183    TK_STYLE_VERSION_2,
184    sizeof(BorderElement),
185    BorderElementOptions,
186    BorderElementSize,
187    BorderElementDraw
188};
189
190/*------------------------------------------------------------------------
191 * +++ Field element.
192 */
193
194typedef struct {
195    Tcl_Obj 	*borderColorObj;
196    Tcl_Obj 	*lightColorObj;
197    Tcl_Obj 	*darkColorObj;
198    Tcl_Obj 	*backgroundObj;
199} FieldElement;
200
201static Ttk_ElementOptionSpec FieldElementOptions[] = {
202    { "-bordercolor", TK_OPTION_COLOR,
203	Tk_Offset(FieldElement,borderColorObj), DARKEST_COLOR },
204    { "-lightcolor", TK_OPTION_COLOR,
205	Tk_Offset(FieldElement,lightColorObj), LIGHT_COLOR },
206    { "-darkcolor", TK_OPTION_COLOR,
207	Tk_Offset(FieldElement,darkColorObj), DARK_COLOR },
208    { "-fieldbackground", TK_OPTION_BORDER,
209	Tk_Offset(FieldElement,backgroundObj), "white" },
210    { NULL, 0, 0, NULL }
211};
212
213static void FieldElementSize(
214    void *clientData, void *elementRecord, Tk_Window tkwin,
215    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
216{
217    *paddingPtr = Ttk_UniformPadding(2);
218}
219
220static void FieldElementDraw(
221    void *clientData, void *elementRecord, Tk_Window tkwin,
222    Drawable d, Ttk_Box b, unsigned state)
223{
224    FieldElement *field = elementRecord;
225    Tk_3DBorder bg = Tk_Get3DBorderFromObj(tkwin, field->backgroundObj);
226    Ttk_Box f = Ttk_PadBox(b, Ttk_UniformPadding(2));
227    Tcl_Obj *outer = field->borderColorObj,
228	    *inner = field->lightColorObj;
229
230    DrawSmoothBorder(tkwin, d, b, outer, inner, inner);
231    Tk_Fill3DRectangle(
232	tkwin, d, bg, f.x, f.y, f.width, f.height, 0, TK_RELIEF_SUNKEN);
233}
234
235static Ttk_ElementSpec FieldElementSpec = {
236    TK_STYLE_VERSION_2,
237    sizeof(FieldElement),
238    FieldElementOptions,
239    FieldElementSize,
240    FieldElementDraw
241};
242
243/*
244 * Modified field element for comboboxes:
245 * 	Right edge is expanded to overlap the dropdown button.
246 */
247static void ComboboxFieldElementDraw(
248    void *clientData, void *elementRecord, Tk_Window tkwin,
249    Drawable d, Ttk_Box b, unsigned state)
250{
251    FieldElement *field = elementRecord;
252    GC gc = Ttk_GCForColor(tkwin,field->borderColorObj,d);
253
254    ++b.width;
255    FieldElementDraw(clientData, elementRecord, tkwin, d, b, state);
256
257    XDrawLine(Tk_Display(tkwin), d, gc,
258	    b.x + b.width - 1, b.y,
259	    b.x + b.width - 1, b.y + b.height - 1 + WIN32_XDRAWLINE_HACK);
260}
261
262static Ttk_ElementSpec ComboboxFieldElementSpec = {
263    TK_STYLE_VERSION_2,
264    sizeof(FieldElement),
265    FieldElementOptions,
266    FieldElementSize,
267    ComboboxFieldElementDraw
268};
269
270/*------------------------------------------------------------------------
271 * +++ Indicator elements for check and radio buttons.
272 */
273
274typedef struct {
275    Tcl_Obj *sizeObj;
276    Tcl_Obj *marginObj;
277    Tcl_Obj *backgroundObj;
278    Tcl_Obj *foregroundObj;
279    Tcl_Obj *upperColorObj;
280    Tcl_Obj *lowerColorObj;
281} IndicatorElement;
282
283static Ttk_ElementOptionSpec IndicatorElementOptions[] = {
284    { "-indicatorsize", TK_OPTION_PIXELS,
285	Tk_Offset(IndicatorElement,sizeObj), "10" },
286    { "-indicatormargin", TK_OPTION_STRING,
287	Tk_Offset(IndicatorElement,marginObj), "1" },
288    { "-indicatorbackground", TK_OPTION_COLOR,
289	Tk_Offset(IndicatorElement,backgroundObj), "white" },
290    { "-indicatorforeground", TK_OPTION_COLOR,
291	Tk_Offset(IndicatorElement,foregroundObj), "black" },
292    { "-upperbordercolor", TK_OPTION_COLOR,
293	Tk_Offset(IndicatorElement,upperColorObj), DARKEST_COLOR },
294    { "-lowerbordercolor", TK_OPTION_COLOR,
295	Tk_Offset(IndicatorElement,lowerColorObj), DARK_COLOR },
296    { NULL, 0, 0, NULL }
297};
298
299static void IndicatorElementSize(
300    void *clientData, void *elementRecord, Tk_Window tkwin,
301    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
302{
303    IndicatorElement *indicator = elementRecord;
304    Ttk_Padding margins;
305    int size = 10;
306    Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &margins);
307    Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
308    *widthPtr = size + Ttk_PaddingWidth(margins);
309    *heightPtr = size + Ttk_PaddingHeight(margins);
310}
311
312static void RadioIndicatorElementDraw(
313    void *clientData, void *elementRecord, Tk_Window tkwin,
314    Drawable d, Ttk_Box b, unsigned state)
315{
316    IndicatorElement *indicator = elementRecord;
317    GC gcb=Ttk_GCForColor(tkwin,indicator->backgroundObj,d);
318    GC gcf=Ttk_GCForColor(tkwin,indicator->foregroundObj,d);
319    GC gcu=Ttk_GCForColor(tkwin,indicator->upperColorObj,d);
320    GC gcl=Ttk_GCForColor(tkwin,indicator->lowerColorObj,d);
321    Ttk_Padding padding;
322
323    Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &padding);
324    b = Ttk_PadBox(b, padding);
325
326    XFillArc(Tk_Display(tkwin),d,gcb, b.x,b.y,b.width,b.height, 0,360*64);
327    XDrawArc(Tk_Display(tkwin),d,gcl, b.x,b.y,b.width,b.height, 225*64,180*64);
328    XDrawArc(Tk_Display(tkwin),d,gcu, b.x,b.y,b.width,b.height, 45*64,180*64);
329
330    if (state & TTK_STATE_SELECTED) {
331	b = Ttk_PadBox(b,Ttk_UniformPadding(3));
332	XFillArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 0,360*64);
333	XDrawArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 0,360*64);
334#if WIN32_XDRAWLINE_HACK
335	XDrawArc(Tk_Display(tkwin),d,gcf, b.x,b.y,b.width,b.height, 300*64,360*64);
336#endif
337    }
338}
339
340static void CheckIndicatorElementDraw(
341    void *clientData, void *elementRecord, Tk_Window tkwin,
342    Drawable d, Ttk_Box b, unsigned state)
343{
344    Display *display = Tk_Display(tkwin);
345    IndicatorElement *indicator = elementRecord;
346    GC gcb=Ttk_GCForColor(tkwin,indicator->backgroundObj,d);
347    GC gcf=Ttk_GCForColor(tkwin,indicator->foregroundObj,d);
348    GC gcu=Ttk_GCForColor(tkwin,indicator->upperColorObj,d);
349    GC gcl=Ttk_GCForColor(tkwin,indicator->lowerColorObj,d);
350    Ttk_Padding padding;
351    const int w = WIN32_XDRAWLINE_HACK;
352
353    Ttk_GetPaddingFromObj(NULL, tkwin, indicator->marginObj, &padding);
354    b = Ttk_PadBox(b, padding);
355
356    XFillRectangle(display,d,gcb, b.x,b.y,b.width,b.height);
357    XDrawLine(display,d,gcl,b.x,b.y+b.height,b.x+b.width+w,b.y+b.height);/*S*/
358    XDrawLine(display,d,gcl,b.x+b.width,b.y,b.x+b.width,b.y+b.height+w); /*E*/
359    XDrawLine(display,d,gcu,b.x,b.y, b.x,b.y+b.height+w); /*W*/
360    XDrawLine(display,d,gcu,b.x,b.y, b.x+b.width+w,b.y);  /*N*/
361
362    if (state & TTK_STATE_SELECTED) {
363	int p,q,r,s;
364
365	b = Ttk_PadBox(b,Ttk_UniformPadding(2));
366	p = b.x, q = b.y, r = b.x+b.width, s = b.y+b.height;
367
368	r+=w, s+=w;
369	XDrawLine(display, d, gcf, p,   q,   r,   s);
370	XDrawLine(display, d, gcf, p+1, q,   r,   s-1);
371	XDrawLine(display, d, gcf, p,   q+1, r-1, s);
372
373	s-=w, q-=w;
374	XDrawLine(display, d, gcf, p,   s,   r,   q);
375	XDrawLine(display, d, gcf, p+1, s,   r,   q+1);
376	XDrawLine(display, d, gcf, p,   s-1, r-1, q);
377    }
378}
379
380static Ttk_ElementSpec RadioIndicatorElementSpec = {
381    TK_STYLE_VERSION_2,
382    sizeof(IndicatorElement),
383    IndicatorElementOptions,
384    IndicatorElementSize,
385    RadioIndicatorElementDraw
386};
387
388static Ttk_ElementSpec CheckIndicatorElementSpec = {
389    TK_STYLE_VERSION_2,
390    sizeof(IndicatorElement),
391    IndicatorElementOptions,
392    IndicatorElementSize,
393    CheckIndicatorElementDraw
394};
395
396#define MENUBUTTON_ARROW_SIZE 5
397
398typedef struct {
399    Tcl_Obj *sizeObj;
400    Tcl_Obj *colorObj;
401    Tcl_Obj *paddingObj;
402} MenuIndicatorElement;
403
404static Ttk_ElementOptionSpec MenuIndicatorElementOptions[] =
405{
406    { "-arrowsize", TK_OPTION_PIXELS,
407	Tk_Offset(MenuIndicatorElement,sizeObj),
408	STR(MENUBUTTON_ARROW_SIZE)},
409    { "-arrowcolor",TK_OPTION_COLOR,
410	Tk_Offset(MenuIndicatorElement,colorObj),
411	"black" },
412    { "-arrowpadding",TK_OPTION_STRING,
413	Tk_Offset(MenuIndicatorElement,paddingObj),
414	"3" },
415    { NULL, 0, 0, NULL }
416};
417
418static void MenuIndicatorElementSize(
419    void *clientData, void *elementRecord, Tk_Window tkwin,
420    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
421{
422    MenuIndicatorElement *indicator = elementRecord;
423    Ttk_Padding margins;
424    int size = MENUBUTTON_ARROW_SIZE;
425    Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
426    Ttk_GetPaddingFromObj(NULL, tkwin, indicator->paddingObj, &margins);
427    TtkArrowSize(size, ARROW_DOWN, widthPtr, heightPtr);
428    *widthPtr += Ttk_PaddingWidth(margins);
429    *heightPtr += Ttk_PaddingHeight(margins);
430}
431
432static void MenuIndicatorElementDraw(
433    void *clientData, void *elementRecord, Tk_Window tkwin,
434    Drawable d, Ttk_Box b, unsigned int state)
435{
436    MenuIndicatorElement *indicator = elementRecord;
437    XColor *arrowColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
438    GC gc = Tk_GCForColor(arrowColor, d);
439    int size = MENUBUTTON_ARROW_SIZE;
440    int width, height;
441
442    Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
443
444    TtkArrowSize(size, ARROW_DOWN, &width, &height);
445    b = Ttk_StickBox(b, width, height, 0);
446    TtkFillArrow(Tk_Display(tkwin), d, gc, b, ARROW_DOWN);
447}
448
449static Ttk_ElementSpec MenuIndicatorElementSpec =
450{
451    TK_STYLE_VERSION_2,
452    sizeof(MenuIndicatorElement),
453    MenuIndicatorElementOptions,
454    MenuIndicatorElementSize,
455    MenuIndicatorElementDraw
456};
457
458/*------------------------------------------------------------------------
459 * +++ Grips.
460 *
461 * TODO: factor this with ThumbElementDraw
462 */
463
464static Ttk_Orient GripClientData[] = {
465    TTK_ORIENT_HORIZONTAL, TTK_ORIENT_VERTICAL
466};
467
468typedef struct {
469    Tcl_Obj 	*lightColorObj;
470    Tcl_Obj 	*borderColorObj;
471    Tcl_Obj 	*gripCountObj;
472} GripElement;
473
474static Ttk_ElementOptionSpec GripElementOptions[] = {
475    { "-lightcolor", TK_OPTION_COLOR,
476	Tk_Offset(GripElement,lightColorObj), LIGHT_COLOR },
477    { "-bordercolor", TK_OPTION_COLOR,
478	Tk_Offset(GripElement,borderColorObj), DARKEST_COLOR },
479    { "-gripcount", TK_OPTION_INT,
480	Tk_Offset(GripElement,gripCountObj), "5" },
481    { NULL, 0, 0, NULL }
482};
483
484static void GripElementSize(
485    void *clientData, void *elementRecord, Tk_Window tkwin,
486    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
487{
488    int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
489    GripElement *grip = elementRecord;
490    int gripCount = 0;
491
492    Tcl_GetIntFromObj(NULL, grip->gripCountObj, &gripCount);
493    if (horizontal) {
494	*widthPtr = 2*gripCount;
495    } else {
496	*heightPtr = 2*gripCount;
497    }
498}
499
500static void GripElementDraw(
501    void *clientData, void *elementRecord, Tk_Window tkwin,
502    Drawable d, Ttk_Box b, unsigned state)
503{
504    const int w = WIN32_XDRAWLINE_HACK;
505    int horizontal = *((Ttk_Orient*)clientData) == TTK_ORIENT_HORIZONTAL;
506    GripElement *grip = elementRecord;
507    GC lightGC = Ttk_GCForColor(tkwin,grip->lightColorObj,d);
508    GC darkGC = Ttk_GCForColor(tkwin,grip->borderColorObj,d);
509    int gripPad = 1, gripCount = 0;
510    int i;
511
512    Tcl_GetIntFromObj(NULL, grip->gripCountObj, &gripCount);
513
514    if (horizontal) {
515	int x = b.x + b.width / 2 - gripCount;
516	int y1 = b.y + gripPad, y2 = b.y + b.height - gripPad - 1 + w;
517	for (i=0; i<gripCount; ++i) {
518	    XDrawLine(Tk_Display(tkwin), d, darkGC,  x,y1, x,y2); ++x;
519	    XDrawLine(Tk_Display(tkwin), d, lightGC, x,y1, x,y2); ++x;
520	}
521    } else {
522	int y = b.y + b.height / 2 - gripCount;
523	int x1 = b.x + gripPad, x2 = b.x + b.width - gripPad - 1 + w;
524	for (i=0; i<gripCount; ++i) {
525	    XDrawLine(Tk_Display(tkwin), d, darkGC,  x1,y, x2,y); ++y;
526	    XDrawLine(Tk_Display(tkwin), d, lightGC, x1,y, x2,y); ++y;
527	}
528    }
529}
530
531static Ttk_ElementSpec GripElementSpec = {
532    TK_STYLE_VERSION_2,
533    sizeof(GripElement),
534    GripElementOptions,
535    GripElementSize,
536    GripElementDraw
537};
538
539/*------------------------------------------------------------------------
540 * +++ Scrollbar elements: trough, arrows, thumb.
541 *
542 * Notice that the trough element has 0 internal padding;
543 * that way the thumb and arrow borders overlap the trough.
544 */
545
546typedef struct { /* Common element record for scrollbar elements */
547    Tcl_Obj 	*orientObj;
548    Tcl_Obj 	*backgroundObj;
549    Tcl_Obj 	*borderColorObj;
550    Tcl_Obj 	*troughColorObj;
551    Tcl_Obj 	*lightColorObj;
552    Tcl_Obj 	*darkColorObj;
553    Tcl_Obj 	*arrowColorObj;
554    Tcl_Obj 	*arrowSizeObj;
555    Tcl_Obj 	*gripCountObj;
556    Tcl_Obj 	*sliderlengthObj;
557} ScrollbarElement;
558
559static Ttk_ElementOptionSpec ScrollbarElementOptions[] = {
560    { "-orient", TK_OPTION_ANY,
561	Tk_Offset(ScrollbarElement, orientObj), "horizontal" },
562    { "-background", TK_OPTION_BORDER,
563	Tk_Offset(ScrollbarElement,backgroundObj), FRAME_COLOR },
564    { "-bordercolor", TK_OPTION_COLOR,
565	Tk_Offset(ScrollbarElement,borderColorObj), DARKEST_COLOR },
566    { "-troughcolor", TK_OPTION_COLOR,
567	Tk_Offset(ScrollbarElement,troughColorObj), DARKER_COLOR },
568    { "-lightcolor", TK_OPTION_COLOR,
569	Tk_Offset(ScrollbarElement,lightColorObj), LIGHT_COLOR },
570    { "-darkcolor", TK_OPTION_COLOR,
571	Tk_Offset(ScrollbarElement,darkColorObj), DARK_COLOR },
572    { "-arrowcolor", TK_OPTION_COLOR,
573	Tk_Offset(ScrollbarElement,arrowColorObj), "#000000" },
574    { "-arrowsize", TK_OPTION_PIXELS,
575	Tk_Offset(ScrollbarElement,arrowSizeObj), STR(SCROLLBAR_THICKNESS) },
576    { "-gripcount", TK_OPTION_INT,
577	Tk_Offset(ScrollbarElement,gripCountObj), "5" },
578    { "-sliderlength", TK_OPTION_INT,
579	Tk_Offset(ScrollbarElement,sliderlengthObj), "30" },
580    { NULL, 0, 0, NULL }
581};
582
583static void TroughElementDraw(
584    void *clientData, void *elementRecord, Tk_Window tkwin,
585    Drawable d, Ttk_Box b, unsigned state)
586{
587    ScrollbarElement *sb = elementRecord;
588    GC gcb = Ttk_GCForColor(tkwin,sb->borderColorObj,d);
589    GC gct = Ttk_GCForColor(tkwin,sb->troughColorObj,d);
590    XFillRectangle(Tk_Display(tkwin), d, gct, b.x, b.y, b.width-1, b.height-1);
591    XDrawRectangle(Tk_Display(tkwin), d, gcb, b.x, b.y, b.width-1, b.height-1);
592}
593
594static Ttk_ElementSpec TroughElementSpec = {
595    TK_STYLE_VERSION_2,
596    sizeof(ScrollbarElement),
597    ScrollbarElementOptions,
598    TtkNullElementSize,
599    TroughElementDraw
600};
601
602static void ThumbElementSize(
603    void *clientData, void *elementRecord, Tk_Window tkwin,
604    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
605{
606    ScrollbarElement *sb = elementRecord;
607    int size = SCROLLBAR_THICKNESS;
608    Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &size);
609    *widthPtr = *heightPtr = size;
610}
611
612static void ThumbElementDraw(
613    void *clientData, void *elementRecord, Tk_Window tkwin,
614    Drawable d, Ttk_Box b, unsigned state)
615{
616    ScrollbarElement *sb = elementRecord;
617    int gripCount = 0, orient = TTK_ORIENT_HORIZONTAL;
618    GC lightGC, darkGC;
619    int x1, y1, x2, y2, dx, dy, i;
620    const int w = WIN32_XDRAWLINE_HACK;
621
622    DrawSmoothBorder(tkwin, d, b,
623	sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
624    XFillRectangle(
625	Tk_Display(tkwin), d, BackgroundGC(tkwin, sb->backgroundObj),
626	b.x+2, b.y+2, b.width-4, b.height-4);
627
628    /*
629     * Draw grip:
630     */
631    Ttk_GetOrientFromObj(NULL, sb->orientObj, &orient);
632    Tcl_GetIntFromObj(NULL, sb->gripCountObj, &gripCount);
633    lightGC = Ttk_GCForColor(tkwin,sb->lightColorObj,d);
634    darkGC = Ttk_GCForColor(tkwin,sb->borderColorObj,d);
635
636    if (orient == TTK_ORIENT_HORIZONTAL) {
637	dx = 1; dy = 0;
638	x1 = x2 = b.x + b.width / 2 - gripCount;
639	y1 = b.y + 2;
640	y2 = b.y + b.height - 3 + w;
641    } else {
642	dx = 0; dy = 1;
643	y1 = y2 = b.y + b.height / 2 - gripCount;
644	x1 = b.x + 2;
645	x2 = b.x + b.width - 3 + w;
646    }
647
648    for (i=0; i<gripCount; ++i) {
649	XDrawLine(Tk_Display(tkwin), d, darkGC, x1,y1, x2,y2);
650	x1 += dx; x2 += dx; y1 += dy; y2 += dy;
651	XDrawLine(Tk_Display(tkwin), d, lightGC, x1,y1, x2,y2);
652	x1 += dx; x2 += dx; y1 += dy; y2 += dy;
653    }
654}
655
656static Ttk_ElementSpec ThumbElementSpec = {
657    TK_STYLE_VERSION_2,
658    sizeof(ScrollbarElement),
659    ScrollbarElementOptions,
660    ThumbElementSize,
661    ThumbElementDraw
662};
663
664/*------------------------------------------------------------------------
665 * +++ Slider element.
666 */
667static void SliderElementSize(
668    void *clientData, void *elementRecord, Tk_Window tkwin,
669    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
670{
671    ScrollbarElement *sb = elementRecord;
672    int length, thickness, orient;
673
674    length = thickness = SCROLLBAR_THICKNESS;
675    Ttk_GetOrientFromObj(NULL, sb->orientObj, &orient);
676    Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &thickness);
677    Tk_GetPixelsFromObj(NULL, tkwin, sb->sliderlengthObj, &length);
678    if (orient == TTK_ORIENT_VERTICAL) {
679	*heightPtr = length;
680	*widthPtr = thickness;
681    } else {
682	*heightPtr = thickness;
683	*widthPtr = length;
684    }
685
686}
687
688static Ttk_ElementSpec SliderElementSpec = {
689    TK_STYLE_VERSION_2,
690    sizeof(ScrollbarElement),
691    ScrollbarElementOptions,
692    SliderElementSize,
693    ThumbElementDraw
694};
695
696/*------------------------------------------------------------------------
697 * +++ Progress bar element
698 */
699static void PbarElementSize(
700    void *clientData, void *elementRecord, Tk_Window tkwin,
701    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
702{
703    SliderElementSize(clientData, elementRecord, tkwin,
704	    widthPtr, heightPtr, paddingPtr);
705    *paddingPtr = Ttk_UniformPadding(2);
706    *widthPtr += 4;
707    *heightPtr += 4;
708}
709
710static void PbarElementDraw(
711    void *clientData, void *elementRecord, Tk_Window tkwin,
712    Drawable d, Ttk_Box b, unsigned state)
713{
714    ScrollbarElement *sb = elementRecord;
715
716    b = Ttk_PadBox(b, Ttk_UniformPadding(2));
717    if (b.width > 4 && b.height > 4) {
718	DrawSmoothBorder(tkwin, d, b,
719	    sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
720	XFillRectangle(Tk_Display(tkwin), d,
721	    BackgroundGC(tkwin, sb->backgroundObj),
722	    b.x+2, b.y+2, b.width-4, b.height-4);
723    }
724}
725
726static Ttk_ElementSpec PbarElementSpec = {
727    TK_STYLE_VERSION_2,
728    sizeof(ScrollbarElement),
729    ScrollbarElementOptions,
730    PbarElementSize,
731    PbarElementDraw
732};
733
734
735/*------------------------------------------------------------------------
736 * +++ Scrollbar arrows.
737 */
738static int ArrowElements[] = { ARROW_UP, ARROW_DOWN, ARROW_LEFT, ARROW_RIGHT };
739
740static void ArrowElementSize(
741    void *clientData, void *elementRecord, Tk_Window tkwin,
742    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
743{
744    ScrollbarElement *sb = elementRecord;
745    int size = SCROLLBAR_THICKNESS;
746    Tcl_GetIntFromObj(NULL, sb->arrowSizeObj, &size);
747    *widthPtr = *heightPtr = size;
748}
749
750static void ArrowElementDraw(
751    void *clientData, void *elementRecord, Tk_Window tkwin,
752    Drawable d, Ttk_Box b, unsigned state)
753{
754    ArrowDirection dir = *(ArrowDirection*)clientData;
755    ScrollbarElement *sb = elementRecord;
756    GC gc = Ttk_GCForColor(tkwin,sb->arrowColorObj, d);
757    int h, cx, cy;
758
759    DrawSmoothBorder(tkwin, d, b,
760	sb->borderColorObj, sb->lightColorObj, sb->darkColorObj);
761
762    XFillRectangle(
763	Tk_Display(tkwin), d, BackgroundGC(tkwin, sb->backgroundObj),
764	b.x+2, b.y+2, b.width-4, b.height-4);
765
766    b = Ttk_PadBox(b, Ttk_UniformPadding(3));
767    h = b.width < b.height ? b.width : b.height;
768    TtkArrowSize(h/2, dir, &cx, &cy);
769    b = Ttk_AnchorBox(b, cx, cy, TK_ANCHOR_CENTER);
770
771    TtkFillArrow(Tk_Display(tkwin), d, gc, b, dir);
772}
773
774static Ttk_ElementSpec ArrowElementSpec = {
775    TK_STYLE_VERSION_2,
776    sizeof(ScrollbarElement),
777    ScrollbarElementOptions,
778    ArrowElementSize,
779    ArrowElementDraw
780};
781
782
783/*------------------------------------------------------------------------
784 * +++ Notebook elements.
785 *
786 * Note: Tabs, except for the rightmost, overlap the neighbor to
787 * their right by one pixel.
788 */
789
790typedef struct {
791    Tcl_Obj *backgroundObj;
792    Tcl_Obj *borderColorObj;
793    Tcl_Obj *lightColorObj;
794    Tcl_Obj *darkColorObj;
795} NotebookElement;
796
797static Ttk_ElementOptionSpec NotebookElementOptions[] = {
798    { "-background", TK_OPTION_BORDER,
799	Tk_Offset(NotebookElement,backgroundObj), FRAME_COLOR },
800    { "-bordercolor", TK_OPTION_COLOR,
801	Tk_Offset(NotebookElement,borderColorObj), DARKEST_COLOR },
802    { "-lightcolor", TK_OPTION_COLOR,
803	Tk_Offset(NotebookElement,lightColorObj), LIGHT_COLOR },
804    { "-darkcolor", TK_OPTION_COLOR,
805	Tk_Offset(NotebookElement,darkColorObj), DARK_COLOR },
806    { NULL, 0, 0, NULL }
807};
808
809static void TabElementSize(
810    void *clientData, void *elementRecord, Tk_Window tkwin,
811    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
812{
813    int borderWidth = 2;
814    paddingPtr->top = paddingPtr->left = paddingPtr->right = borderWidth;
815    paddingPtr->bottom = 0;
816}
817
818static void TabElementDraw(
819    void *clientData, void *elementRecord, Tk_Window tkwin,
820    Drawable d, Ttk_Box b, unsigned int state)
821{
822    NotebookElement *tab = elementRecord;
823    Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, tab->backgroundObj);
824    Display *display = Tk_Display(tkwin);
825    int borderWidth = 2, dh = 0;
826    int x1,y1,x2,y2;
827    GC gc;
828    const int w = WIN32_XDRAWLINE_HACK;
829
830    if (state & TTK_STATE_SELECTED) {
831	dh = borderWidth;
832    }
833
834    if (state & TTK_STATE_USER2) {	/* Rightmost tab */
835	--b.width;
836    }
837
838    Tk_Fill3DRectangle(tkwin, d, border,
839	b.x+2, b.y+2, b.width-1, b.height-2+dh, borderWidth, TK_RELIEF_FLAT);
840
841    x1 = b.x, x2 = b.x + b.width;
842    y1 = b.y, y2 = b.y + b.height;
843
844
845    gc=Ttk_GCForColor(tkwin,tab->borderColorObj,d);
846    XDrawLine(display,d,gc, x1,y1+1, x1,y2+w);
847    XDrawLine(display,d,gc, x2,y1+1, x2,y2+w);
848    XDrawLine(display,d,gc, x1+1,y1, x2-1+w,y1);
849
850    gc=Ttk_GCForColor(tkwin,tab->lightColorObj,d);
851    XDrawLine(display,d,gc, x1+1,y1+1, x1+1,y2-1+dh+w);
852    XDrawLine(display,d,gc, x1+1,y1+1, x2-1+w,y1+1);
853}
854
855static Ttk_ElementSpec TabElementSpec =
856{
857    TK_STYLE_VERSION_2,
858    sizeof(NotebookElement),
859    NotebookElementOptions,
860    TabElementSize,
861    TabElementDraw
862};
863
864static void ClientElementSize(
865    void *clientData, void *elementRecord, Tk_Window tkwin,
866    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
867{
868    int borderWidth = 2;
869    *paddingPtr = Ttk_UniformPadding((short)borderWidth);
870}
871
872static void ClientElementDraw(
873    void *clientData, void *elementRecord, Tk_Window tkwin,
874    Drawable d, Ttk_Box b, unsigned int state)
875{
876    NotebookElement *ce = elementRecord;
877    Tk_3DBorder border = Tk_Get3DBorderFromObj(tkwin, ce->backgroundObj);
878    int borderWidth = 2;
879
880    Tk_Fill3DRectangle(tkwin, d, border,
881	b.x, b.y, b.width, b.height, borderWidth,TK_RELIEF_FLAT);
882    DrawSmoothBorder(tkwin, d, b,
883    	ce->borderColorObj, ce->lightColorObj, ce->darkColorObj);
884}
885
886static Ttk_ElementSpec ClientElementSpec =
887{
888    TK_STYLE_VERSION_2,
889    sizeof(NotebookElement),
890    NotebookElementOptions,
891    ClientElementSize,
892    ClientElementDraw
893};
894
895/*------------------------------------------------------------------------
896 * +++ Modified widget layouts.
897 */
898
899TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
900
901TTK_LAYOUT("TCombobox",
902    TTK_NODE("Combobox.downarrow", TTK_PACK_RIGHT|TTK_FILL_Y)
903    TTK_GROUP("Combobox.field", TTK_PACK_LEFT|TTK_FILL_BOTH|TTK_EXPAND,
904	TTK_GROUP("Combobox.padding", TTK_FILL_BOTH,
905	    TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))))
906
907TTK_LAYOUT("Horizontal.Sash",
908    TTK_GROUP("Sash.hsash", TTK_FILL_BOTH,
909	TTK_NODE("Sash.hgrip", TTK_FILL_BOTH)))
910
911TTK_LAYOUT("Vertical.Sash",
912    TTK_GROUP("Sash.vsash", TTK_FILL_BOTH,
913	TTK_NODE("Sash.vgrip", TTK_FILL_BOTH)))
914
915TTK_END_LAYOUT_TABLE
916
917/*------------------------------------------------------------------------
918 * +++ Initialization.
919 */
920
921MODULE_SCOPE int
922TtkClamTheme_Init(Tcl_Interp *interp)
923{
924    Ttk_Theme theme = Ttk_CreateTheme(interp, "clam", 0);
925
926    if (!theme) {
927        return TCL_ERROR;
928    }
929
930    Ttk_RegisterElement(interp,
931	theme, "border", &BorderElementSpec, NULL);
932    Ttk_RegisterElement(interp,
933	theme, "field", &FieldElementSpec, NULL);
934    Ttk_RegisterElement(interp,
935	theme, "Combobox.field", &ComboboxFieldElementSpec, NULL);
936    Ttk_RegisterElement(interp,
937	theme, "trough", &TroughElementSpec, NULL);
938    Ttk_RegisterElement(interp,
939	theme, "thumb", &ThumbElementSpec, NULL);
940    Ttk_RegisterElement(interp,
941	theme, "uparrow", &ArrowElementSpec, &ArrowElements[0]);
942    Ttk_RegisterElement(interp,
943	theme, "downarrow", &ArrowElementSpec, &ArrowElements[1]);
944    Ttk_RegisterElement(interp,
945	theme, "leftarrow", &ArrowElementSpec, &ArrowElements[2]);
946    Ttk_RegisterElement(interp,
947	theme, "rightarrow", &ArrowElementSpec, &ArrowElements[3]);
948
949    Ttk_RegisterElement(interp,
950	theme, "Radiobutton.indicator", &RadioIndicatorElementSpec, NULL);
951    Ttk_RegisterElement(interp,
952	theme, "Checkbutton.indicator", &CheckIndicatorElementSpec, NULL);
953    Ttk_RegisterElement(interp,
954	theme, "Menubutton.indicator", &MenuIndicatorElementSpec, NULL);
955
956    Ttk_RegisterElement(interp, theme, "tab", &TabElementSpec, NULL);
957    Ttk_RegisterElement(interp, theme, "client", &ClientElementSpec, NULL);
958
959    Ttk_RegisterElement(interp, theme, "slider", &SliderElementSpec, NULL);
960    Ttk_RegisterElement(interp, theme, "bar", &PbarElementSpec, NULL);
961    Ttk_RegisterElement(interp, theme, "pbar", &PbarElementSpec, NULL);
962
963    Ttk_RegisterElement(interp, theme, "hgrip",
964	    &GripElementSpec,  &GripClientData[0]);
965    Ttk_RegisterElement(interp, theme, "vgrip",
966	    &GripElementSpec,  &GripClientData[1]);
967
968    Ttk_RegisterLayouts(theme, LayoutTable);
969
970    Tcl_PkgProvide(interp, "ttk::theme::clam", TTK_VERSION);
971
972    return TCL_OK;
973}
974