1/*
2 * $Id$
3 *
4 * Tk theme engine which uses the Windows XP "Visual Styles" API
5 * Adapted from Georgios Petasis' XP theme patch.
6 *
7 * Copyright (c) 2003 by Georgios Petasis, petasis@iit.demokritos.gr.
8 * Copyright (c) 2003 by Joe English
9 * Copyright (c) 2003 by Pat Thoyts
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * See also:
15 *
16 * <URL: http://msdn.microsoft.com/library/en-us/
17 *  	shellcc/platform/commctls/userex/refentry.asp >
18 */
19
20#ifndef HAVE_UXTHEME_H
21/* Stub for platforms that lack the XP theme API headers: */
22#include <tkWinInt.h>
23int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd) { return TCL_OK; }
24#else
25
26#define WINVER 0x0501	/* Requires Windows XP APIs */
27
28#include <windows.h>
29#include <uxtheme.h>
30#ifdef HAVE_VSSYM32_H
31#   include <vssym32.h>
32#else
33#   include <tmschema.h>
34#endif
35
36#include <tkWinInt.h>
37
38#include "ttk/ttkTheme.h"
39
40typedef HTHEME  (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd,
41		 LPCWSTR pszClassList);
42typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme);
43typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme,
44                 HDC hdc, int iPartId, int iStateId, const RECT *pRect,
45                 OPTIONAL const RECT *pClipRect);
46typedef HRESULT	(STDAPICALLTYPE GetThemePartSizeProc)(HTHEME,HDC,
47		 int iPartId, int iStateId,
48		 RECT *prc, enum THEMESIZE eSize, SIZE *psz);
49typedef int     (STDAPICALLTYPE GetThemeSysSizeProc)(HTHEME,int);
50/* GetThemeTextExtent and DrawThemeText only used with BROKEN_TEXT_ELEMENT */
51typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc,
52		 int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
53		 DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtent);
54typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc,
55		 int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
56		 DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
57typedef BOOL    (STDAPICALLTYPE IsThemeActiveProc)(void);
58typedef BOOL    (STDAPICALLTYPE IsAppThemedProc)(void);
59
60typedef struct
61{
62    OpenThemeDataProc			*OpenThemeData;
63    CloseThemeDataProc			*CloseThemeData;
64    GetThemePartSizeProc		*GetThemePartSize;
65    GetThemeSysSizeProc			*GetThemeSysSize;
66    DrawThemeBackgroundProc		*DrawThemeBackground;
67    DrawThemeTextProc		        *DrawThemeText;
68    GetThemeTextExtentProc		*GetThemeTextExtent;
69    IsThemeActiveProc			*IsThemeActive;
70    IsAppThemedProc			*IsAppThemed;
71
72    HWND                                stubWindow;
73} XPThemeProcs;
74
75typedef struct
76{
77    HINSTANCE hlibrary;
78    XPThemeProcs *procs;
79} XPThemeData;
80
81/*
82 *----------------------------------------------------------------------
83 *
84 * LoadXPThemeProcs --
85 *	Initialize XP theming support.
86 *
87 *	XP theme support is included in UXTHEME.DLL
88 *	We dynamically load this DLL at runtime instead of linking
89 *	to it at build-time.
90 *
91 * Returns:
92 *	A pointer to an XPThemeProcs table if successful, NULL otherwise.
93 */
94
95static XPThemeProcs *
96LoadXPThemeProcs(HINSTANCE *phlib)
97{
98    /*
99     * Load the library "uxtheme.dll", where the native widget
100     * drawing routines are implemented.  This will only succeed
101     * if we are running at least on Windows XP.
102     */
103    HINSTANCE handle;
104    *phlib = handle = LoadLibrary(TEXT("uxtheme.dll"));
105    if (handle != 0)
106    {
107	/*
108	 * We have successfully loaded the library. Proceed in storing the
109	 * addresses of the functions we want to use.
110	 */
111	XPThemeProcs *procs = (XPThemeProcs*)ckalloc(sizeof(XPThemeProcs));
112#define LOADPROC(name) \
113	(0 != (procs->name = (name ## Proc *)GetProcAddress(handle, #name) ))
114
115	if (   LOADPROC(OpenThemeData)
116	    && LOADPROC(CloseThemeData)
117	    && LOADPROC(GetThemePartSize)
118	    && LOADPROC(GetThemeSysSize)
119	    && LOADPROC(DrawThemeBackground)
120	    && LOADPROC(GetThemeTextExtent)
121	    && LOADPROC(DrawThemeText)
122	    && LOADPROC(IsThemeActive)
123	    && LOADPROC(IsAppThemed)
124	)
125	{
126	    return procs;
127	}
128#undef LOADPROC
129	ckfree((char*)procs);
130    }
131    return 0;
132}
133
134/*
135 * XPThemeDeleteProc --
136 *
137 *      Release any theme allocated resources.
138 */
139
140static void
141XPThemeDeleteProc(void *clientData)
142{
143    XPThemeData *themeData = clientData;
144    FreeLibrary(themeData->hlibrary);
145    ckfree(clientData);
146}
147
148static int
149XPThemeEnabled(Ttk_Theme theme, void *clientData)
150{
151    XPThemeData *themeData = clientData;
152    int active = themeData->procs->IsThemeActive();
153    int themed = themeData->procs->IsAppThemed();
154    return (active && themed);
155}
156
157/*
158 * BoxToRect --
159 * 	Helper routine.  Returns a RECT data structure.
160 */
161static RECT
162BoxToRect(Ttk_Box b)
163{
164    RECT rc;
165    rc.top = b.y;
166    rc.left = b.x;
167    rc.bottom = b.y + b.height;
168    rc.right = b.x + b.width;
169    return rc;
170}
171
172/*
173 * Map Tk state bitmaps to XP style enumerated values.
174 */
175static Ttk_StateTable null_statemap[] = { {0,0,0} };
176
177/*
178 * Pushbuttons (Tk: "Button")
179 */
180static Ttk_StateTable pushbutton_statemap[] =
181{
182    { PBS_DISABLED, 	TTK_STATE_DISABLED, 0 },
183    { PBS_PRESSED, 	TTK_STATE_PRESSED, 0 },
184    { PBS_HOT,		TTK_STATE_ACTIVE, 0 },
185    { PBS_DEFAULTED,	TTK_STATE_ALTERNATE, 0 },
186    { PBS_NORMAL, 	0, 0 }
187};
188
189/*
190 * Checkboxes (Tk: "Checkbutton")
191 */
192static Ttk_StateTable checkbox_statemap[] =
193{
194{CBS_MIXEDDISABLED, 	TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0},
195{CBS_MIXEDPRESSED, 	TTK_STATE_ALTERNATE|TTK_STATE_PRESSED, 0},
196{CBS_MIXEDHOT,  	TTK_STATE_ALTERNATE|TTK_STATE_ACTIVE, 0},
197{CBS_MIXEDNORMAL, 	TTK_STATE_ALTERNATE, 0},
198{CBS_CHECKEDDISABLED,	TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0},
199{CBS_CHECKEDPRESSED,	TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0},
200{CBS_CHECKEDHOT,	TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0},
201{CBS_CHECKEDNORMAL,	TTK_STATE_SELECTED, 0},
202{CBS_UNCHECKEDDISABLED,	TTK_STATE_DISABLED, 0},
203{CBS_UNCHECKEDPRESSED,	TTK_STATE_PRESSED, 0},
204{CBS_UNCHECKEDHOT,	TTK_STATE_ACTIVE, 0},
205{CBS_UNCHECKEDNORMAL,	0,0 }
206};
207
208/*
209 * Radiobuttons:
210 */
211static Ttk_StateTable radiobutton_statemap[] =
212{
213{RBS_CHECKEDDISABLED,	TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0},
214{RBS_CHECKEDPRESSED,	TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0},
215{RBS_CHECKEDHOT,	TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0},
216{RBS_CHECKEDNORMAL,	TTK_STATE_SELECTED, 0},
217{RBS_UNCHECKEDDISABLED,	TTK_STATE_DISABLED, 0},
218{RBS_UNCHECKEDPRESSED,	TTK_STATE_PRESSED, 0},
219{RBS_UNCHECKEDHOT,	TTK_STATE_ACTIVE, 0},
220{RBS_UNCHECKEDNORMAL,	0,0 }
221};
222
223/*
224 * Groupboxes (tk: "frame")
225 */
226static Ttk_StateTable groupbox_statemap[] =
227{
228{GBS_DISABLED,	TTK_STATE_DISABLED, 0},
229{GBS_NORMAL,	0,0 }
230};
231
232/*
233 * Edit fields (tk: "entry")
234 */
235static Ttk_StateTable edittext_statemap[] =
236{
237    { ETS_DISABLED,	TTK_STATE_DISABLED, 0 },
238    { ETS_READONLY,	TTK_STATE_READONLY, 0 },
239    { ETS_FOCUSED,	TTK_STATE_FOCUS, 0 },
240    { ETS_HOT,		TTK_STATE_ACTIVE, 0 },
241    { ETS_NORMAL,	0, 0 }
242/* NOT USED: ETS_ASSIST, ETS_SELECTED */
243};
244
245/*
246 * Combobox text field statemap:
247 * Same as edittext_statemap, but doesn't use ETS_READONLY
248 * (fixes: #1032409)
249 */
250static Ttk_StateTable combotext_statemap[] =
251{
252    { ETS_DISABLED,	TTK_STATE_DISABLED, 0 },
253    { ETS_FOCUSED,	TTK_STATE_FOCUS, 0 },
254    { ETS_HOT,		TTK_STATE_ACTIVE, 0 },
255    { ETS_NORMAL,	0, 0 }
256};
257
258/*
259 * Combobox button: (CBP_DROPDOWNBUTTON)
260 */
261static Ttk_StateTable combobox_statemap[] = {
262    { CBXS_DISABLED,	TTK_STATE_DISABLED, 0 },
263    { CBXS_PRESSED, 	TTK_STATE_PRESSED, 0 },
264    { CBXS_HOT, 	TTK_STATE_ACTIVE, 0 },
265    { CBXS_HOT, 	TTK_STATE_HOVER, 0 },
266    { CBXS_NORMAL, 	0, 0 }
267};
268
269/*
270 * Toolbar buttons (TP_BUTTON):
271 */
272static Ttk_StateTable toolbutton_statemap[] =  {
273    { TS_DISABLED, 	TTK_STATE_DISABLED, 0 },
274    { TS_PRESSED,	TTK_STATE_PRESSED, 0 },
275    { TS_HOTCHECKED,	TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0 },
276    { TS_CHECKED, 	TTK_STATE_SELECTED, 0 },
277    { TS_HOT,  		TTK_STATE_ACTIVE, 0 },
278    { TS_NORMAL, 	0,0 }
279};
280
281/*
282 * Scrollbars (Tk: "Scrollbar.thumb")
283 */
284static Ttk_StateTable scrollbar_statemap[] =
285{
286    { SCRBS_DISABLED, 	TTK_STATE_DISABLED, 0 },
287    { SCRBS_PRESSED, 	TTK_STATE_PRESSED, 0 },
288    { SCRBS_HOT,	TTK_STATE_ACTIVE, 0 },
289    { SCRBS_NORMAL, 	0, 0 }
290};
291
292static Ttk_StateTable uparrow_statemap[] =
293{
294    { ABS_UPDISABLED,	TTK_STATE_DISABLED, 0 },
295    { ABS_UPPRESSED, 	TTK_STATE_PRESSED, 0 },
296    { ABS_UPHOT,	TTK_STATE_ACTIVE, 0 },
297    { ABS_UPNORMAL, 	0, 0 }
298};
299
300static Ttk_StateTable downarrow_statemap[] =
301{
302    { ABS_DOWNDISABLED,	TTK_STATE_DISABLED, 0 },
303    { ABS_DOWNPRESSED, 	TTK_STATE_PRESSED, 0 },
304    { ABS_DOWNHOT,	TTK_STATE_ACTIVE, 0 },
305    { ABS_DOWNNORMAL, 	0, 0 }
306};
307
308static Ttk_StateTable leftarrow_statemap[] =
309{
310    { ABS_LEFTDISABLED,	TTK_STATE_DISABLED, 0 },
311    { ABS_LEFTPRESSED, 	TTK_STATE_PRESSED, 0 },
312    { ABS_LEFTHOT,	TTK_STATE_ACTIVE, 0 },
313    { ABS_LEFTNORMAL, 	0, 0 }
314};
315
316static Ttk_StateTable rightarrow_statemap[] =
317{
318    { ABS_RIGHTDISABLED,TTK_STATE_DISABLED, 0 },
319    { ABS_RIGHTPRESSED, TTK_STATE_PRESSED, 0 },
320    { ABS_RIGHTHOT,	TTK_STATE_ACTIVE, 0 },
321    { ABS_RIGHTNORMAL, 	0, 0 }
322};
323
324static Ttk_StateTable spinbutton_statemap[] =
325{
326    { DNS_DISABLED,	TTK_STATE_DISABLED, 0 },
327    { DNS_PRESSED,	TTK_STATE_PRESSED,  0 },
328    { DNS_HOT,		TTK_STATE_ACTIVE,   0 },
329    { DNS_NORMAL,	0,		    0 },
330};
331
332/*
333 * Trackbar thumb: (Tk: "scale slider")
334 */
335static Ttk_StateTable scale_statemap[] =
336{
337    { TUS_DISABLED, 	TTK_STATE_DISABLED, 0 },
338    { TUS_PRESSED, 	TTK_STATE_PRESSED, 0 },
339    { TUS_FOCUSED, 	TTK_STATE_FOCUS, 0 },
340    { TUS_HOT,		TTK_STATE_ACTIVE, 0 },
341    { TUS_NORMAL, 	0, 0 }
342};
343
344static Ttk_StateTable tabitem_statemap[] =
345{
346    { TIS_DISABLED,     TTK_STATE_DISABLED, 0 },
347    { TIS_SELECTED,     TTK_STATE_SELECTED, 0 },
348    { TIS_HOT,          TTK_STATE_ACTIVE,   0 },
349    { TIS_FOCUSED,      TTK_STATE_FOCUS,    0 },
350    { TIS_NORMAL,       0,                  0 },
351};
352
353
354/*
355 *----------------------------------------------------------------------
356 * +++ Element data:
357 *
358 * The following structure is passed as the 'clientData' pointer
359 * to most elements in this theme.  It contains data relevant
360 * to a single XP Theme "part".
361 *
362 * <<NOTE-GetThemeMargins>>:
363 *	In theory, we should be call GetThemeMargins(...TMT_CONTENTRECT...)
364 *	to calculate the internal padding.  In practice, this routine
365 *	only seems to work properly for BP_PUSHBUTTON.  So we hardcode
366 *	the required padding at element registration time instead.
367 *
368 *	The PAD_MARGINS flag bit determines whether the padding
369 *	should be added on the inside (0) or outside (1) of the element.
370 *
371 * <<NOTE-GetThemePartSize>>:
372 *	This gives bogus metrics for some parts (in particular,
373 *	BP_PUSHBUTTONS).  Set the IGNORE_THEMESIZE flag to skip this call.
374 */
375
376typedef struct 	/* XP element specifications */
377{
378    const char	*elementName;	/* Tk theme engine element name */
379    Ttk_ElementSpec *elementSpec;
380    				/* Element spec (usually GenericElementSpec) */
381    LPCWSTR	className;	/* Windows window class name */
382    int 	partId;		/* BP_PUSHBUTTON, BP_CHECKBUTTON, etc. */
383    Ttk_StateTable *statemap;	/* Map Tk states to XP states */
384    Ttk_Padding	padding;	/* See NOTE-GetThemeMargins */
385    int  	flags;
386#   define 	IGNORE_THEMESIZE 0x80000000 /* See NOTE-GetThemePartSize */
387#   define 	PAD_MARGINS	 0x40000000 /* See NOTE-GetThemeMargins */
388#   define 	HEAP_ELEMENT	 0x20000000 /* ElementInfo is on heap */
389} ElementInfo;
390
391typedef struct
392{
393    /*
394     * Static data, initialized when element is registered:
395     */
396    ElementInfo	*info;
397    XPThemeProcs *procs;	/* Pointer to theme procedure table */
398
399    /*
400     * Dynamic data, allocated by InitElementData:
401     */
402    HTHEME	hTheme;
403    HDC		hDC;
404    HWND	hwnd;
405
406    /* For TkWinDrawableReleaseDC: */
407    Drawable	drawable;
408    TkWinDCState dcState;
409} ElementData;
410
411static ElementData *
412NewElementData(XPThemeProcs *procs, ElementInfo *info)
413{
414    ElementData *elementData = (ElementData*)ckalloc(sizeof(ElementData));
415
416    elementData->procs = procs;
417    elementData->info = info;
418    elementData->hTheme = elementData->hDC = 0;
419
420    return elementData;
421}
422
423/*
424 * Destroy elements. If the element was created by the element factory
425 * then the info member is dynamically allocated. Otherwise it was
426 * static data from the C object and only the ElementData needs freeing.
427 */
428static void DestroyElementData(void *clientData)
429{
430    ElementData *elementData = clientData;
431    if (elementData->info->flags & HEAP_ELEMENT) {
432	ckfree((char *)elementData->info->statemap);
433	ckfree((char *)elementData->info->className);
434	ckfree((char *)elementData->info->elementName);
435	ckfree((char *)elementData->info);
436    }
437    ckfree(clientData);
438}
439
440/*
441 * InitElementData --
442 * 	Looks up theme handle.  If Drawable argument is non-NULL,
443 * 	also initializes DC.
444 *
445 * Returns:
446 * 	1 on success, 0 on error.
447 * 	Caller must later call FreeElementData() so this element
448 * 	can be reused.
449 */
450
451static int
452InitElementData(ElementData *elementData, Tk_Window tkwin, Drawable d)
453{
454    Window win = Tk_WindowId(tkwin);
455
456    if (win != None) {
457	elementData->hwnd = Tk_GetHWND(win);
458    } else  {
459	elementData->hwnd = elementData->procs->stubWindow;
460    }
461
462    elementData->hTheme = elementData->procs->OpenThemeData(
463	elementData->hwnd, elementData->info->className);
464
465    if (!elementData->hTheme)
466	return 0;
467
468    elementData->drawable = d;
469    if (d != 0) {
470	elementData->hDC = TkWinGetDrawableDC(Tk_Display(tkwin), d,
471	    &elementData->dcState);
472    }
473
474    return 1;
475}
476
477static void
478FreeElementData(ElementData *elementData)
479{
480    elementData->procs->CloseThemeData(elementData->hTheme);
481    if (elementData->drawable != 0) {
482	TkWinReleaseDrawableDC(
483	    elementData->drawable, elementData->hDC, &elementData->dcState);
484    }
485}
486
487/*----------------------------------------------------------------------
488 * +++ Generic element implementation.
489 *
490 * Used for elements which are handled entirely by the XP Theme API,
491 * such as radiobutton and checkbutton indicators, scrollbar arrows, etc.
492 */
493
494static void GenericElementSize(
495    void *clientData, void *elementRecord, Tk_Window tkwin,
496    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
497{
498    ElementData *elementData = clientData;
499    HRESULT result;
500    SIZE size;
501
502    if (!InitElementData(elementData, tkwin, 0))
503	return;
504
505    if (!(elementData->info->flags & IGNORE_THEMESIZE)) {
506	result = elementData->procs->GetThemePartSize(
507	    elementData->hTheme,
508	    elementData->hDC,
509	    elementData->info->partId,
510	    Ttk_StateTableLookup(elementData->info->statemap, 0),
511	    NULL /*RECT *prc*/,
512	    TS_TRUE,
513	    &size);
514
515	if (SUCCEEDED(result)) {
516	    *widthPtr = size.cx;
517	    *heightPtr = size.cy;
518	}
519    }
520
521    /* See NOTE-GetThemeMargins
522     */
523    *paddingPtr = elementData->info->padding;
524    if (elementData->info->flags & PAD_MARGINS) {
525	*widthPtr += Ttk_PaddingWidth(elementData->info->padding);
526	*heightPtr += Ttk_PaddingHeight(elementData->info->padding);
527    }
528}
529
530static void GenericElementDraw(
531    void *clientData, void *elementRecord, Tk_Window tkwin,
532    Drawable d, Ttk_Box b, unsigned int state)
533{
534    ElementData *elementData = clientData;
535    RECT rc;
536
537    if (!InitElementData(elementData, tkwin, d)) {
538	return;
539    }
540
541    if (elementData->info->flags & PAD_MARGINS) {
542    	b = Ttk_PadBox(b, elementData->info->padding);
543    }
544    rc = BoxToRect(b);
545
546    elementData->procs->DrawThemeBackground(
547	elementData->hTheme,
548	elementData->hDC,
549	elementData->info->partId,
550	Ttk_StateTableLookup(elementData->info->statemap, state),
551	&rc,
552	NULL/*pContentRect*/);
553
554    FreeElementData(elementData);
555}
556
557static Ttk_ElementSpec GenericElementSpec =
558{
559    TK_STYLE_VERSION_2,
560    sizeof(NullElement),
561    TtkNullElementOptions,
562    GenericElementSize,
563    GenericElementDraw
564};
565
566/*----------------------------------------------------------------------
567 * +++ Sized element implementation.
568 *
569 * Used for elements which are handled entirely by the XP Theme API,
570 * but that require a fixed size adjustment.
571 * Note that GetThemeSysSize calls through to GetSystemMetrics
572 */
573
574static void
575GenericSizedElementSize(
576    void *clientData, void *elementRecord, Tk_Window tkwin,
577    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
578{
579    ElementData *elementData = clientData;
580
581    if (!InitElementData(elementData, tkwin, 0))
582	return;
583
584    GenericElementSize(clientData, elementRecord, tkwin,
585	widthPtr, heightPtr, paddingPtr);
586
587    *widthPtr = elementData->procs->GetThemeSysSize(NULL,
588	(elementData->info->flags >> 8) & 0xff);
589    *heightPtr = elementData->procs->GetThemeSysSize(NULL,
590	elementData->info->flags & 0xff);
591}
592
593static Ttk_ElementSpec GenericSizedElementSpec = {
594    TK_STYLE_VERSION_2,
595    sizeof(NullElement),
596    TtkNullElementOptions,
597    GenericSizedElementSize,
598    GenericElementDraw
599};
600
601/*----------------------------------------------------------------------
602 * +++ Spinbox arrow element.
603 *     These are half-height scrollbar buttons.
604 */
605
606static void
607SpinboxArrowElementSize(
608    void *clientData, void *elementRecord, Tk_Window tkwin,
609    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
610{
611    ElementData *elementData = clientData;
612
613    if (!InitElementData(elementData, tkwin, 0))
614	return;
615
616    GenericSizedElementSize(clientData, elementRecord, tkwin,
617	widthPtr, heightPtr, paddingPtr);
618
619    /* force the arrow button height to half size */
620    *heightPtr /= 2;
621}
622
623static Ttk_ElementSpec SpinboxArrowElementSpec = {
624    TK_STYLE_VERSION_2,
625    sizeof(NullElement),
626    TtkNullElementOptions,
627    SpinboxArrowElementSize,
628    GenericElementDraw
629};
630
631/*----------------------------------------------------------------------
632 * +++ Scrollbar thumb element.
633 *     Same as a GenericElement, but don't draw in the disabled state.
634 */
635
636static void ThumbElementDraw(
637    void *clientData, void *elementRecord, Tk_Window tkwin,
638    Drawable d, Ttk_Box b, unsigned int state)
639{
640    ElementData *elementData = clientData;
641    unsigned stateId = Ttk_StateTableLookup(elementData->info->statemap, state);
642    RECT rc = BoxToRect(b);
643
644    /*
645     * Don't draw the thumb if we are disabled.
646     */
647    if (state & TTK_STATE_DISABLED)
648	return;
649
650    if (!InitElementData(elementData, tkwin, d))
651	return;
652
653    elementData->procs->DrawThemeBackground(elementData->hTheme,
654	elementData->hDC, elementData->info->partId, stateId,
655	&rc, NULL);
656
657    FreeElementData(elementData);
658}
659
660static Ttk_ElementSpec ThumbElementSpec =
661{
662    TK_STYLE_VERSION_2,
663    sizeof(NullElement),
664    TtkNullElementOptions,
665    GenericElementSize,
666    ThumbElementDraw
667};
668
669/*----------------------------------------------------------------------
670 * +++ Progress bar element.
671 *	Increases the requested length of PP_CHUNK and PP_CHUNKVERT parts
672 *	so that indeterminate progress bars show 3 bars instead of 1.
673 */
674
675static void PbarElementSize(
676    void *clientData, void *elementRecord, Tk_Window tkwin,
677    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
678{
679    ElementData *elementData = clientData;
680    int nBars = 3;
681
682    GenericElementSize(clientData, elementRecord, tkwin,
683    	widthPtr, heightPtr, paddingPtr);
684
685    if (elementData->info->partId == PP_CHUNK) {
686    	*widthPtr *= nBars;
687    } else if (elementData->info->partId == PP_CHUNKVERT) {
688    	*heightPtr *= nBars;
689    }
690}
691
692static Ttk_ElementSpec PbarElementSpec =
693{
694    TK_STYLE_VERSION_2,
695    sizeof(NullElement),
696    TtkNullElementOptions,
697    PbarElementSize,
698    GenericElementDraw
699};
700
701/*----------------------------------------------------------------------
702 * +++  Notebook tab element.
703 *	Same as generic element, with additional logic to select
704 *	proper iPartID for the leftmost tab.
705 *
706 *	Notes: TABP_TABITEMRIGHTEDGE (or TABP_TOPTABITEMRIGHTEDGE,
707 * 	which appears to be identical) should be used if the
708 *	tab is exactly at the right edge of the notebook, but
709 *	not if it's simply the rightmost tab.  This information
710 * 	is not available.
711 *
712 *	The TIS_* and TILES_* definitions are identical, so
713 * 	we can use the same statemap no matter what the partId.
714 */
715static void TabElementDraw(
716    void *clientData, void *elementRecord, Tk_Window tkwin,
717    Drawable d, Ttk_Box b, unsigned int state)
718{
719    ElementData *elementData = clientData;
720    int partId = elementData->info->partId;
721    RECT rc = BoxToRect(b);
722
723    if (!InitElementData(elementData, tkwin, d))
724	return;
725    if (state & TTK_STATE_USER1)
726	partId = TABP_TABITEMLEFTEDGE;
727    elementData->procs->DrawThemeBackground(
728	elementData->hTheme, elementData->hDC, partId,
729	Ttk_StateTableLookup(elementData->info->statemap, state), &rc, NULL);
730    FreeElementData(elementData);
731}
732
733static Ttk_ElementSpec TabElementSpec =
734{
735    TK_STYLE_VERSION_2,
736    sizeof(NullElement),
737    TtkNullElementOptions,
738    GenericElementSize,
739    TabElementDraw
740};
741
742/*----------------------------------------------------------------------
743 * +++  Tree indicator element.
744 *
745 *	Generic element, but don't display at all if TTK_STATE_LEAF (=USER2) set
746 */
747
748#define TTK_STATE_OPEN TTK_STATE_USER1
749#define TTK_STATE_LEAF TTK_STATE_USER2
750
751static Ttk_StateTable header_statemap[] =
752{
753    { HIS_PRESSED, 	TTK_STATE_PRESSED, 0 },
754    { HIS_HOT,  	TTK_STATE_ACTIVE, 0 },
755    { HIS_NORMAL, 	0,0 },
756};
757
758static Ttk_StateTable treeview_statemap[] =
759{
760    { TREIS_DISABLED, 	TTK_STATE_DISABLED, 0 },
761    { TREIS_SELECTED,	TTK_STATE_SELECTED, 0},
762    { TREIS_HOT, 	TTK_STATE_ACTIVE, 0 },
763    { TREIS_NORMAL, 	0,0 },
764};
765
766static Ttk_StateTable tvpglyph_statemap[] =
767{
768    { GLPS_OPENED, 	TTK_STATE_OPEN, 0 },
769    { GLPS_CLOSED, 	0,0 },
770};
771
772static void TreeIndicatorElementDraw(
773    void *clientData, void *elementRecord, Tk_Window tkwin,
774    Drawable d, Ttk_Box b, unsigned int state)
775{
776    if (!(state & TTK_STATE_LEAF)) {
777        GenericElementDraw(clientData,elementRecord,tkwin,d,b,state);
778    }
779}
780
781static Ttk_ElementSpec TreeIndicatorElementSpec =
782{
783    TK_STYLE_VERSION_2,
784    sizeof(NullElement),
785    TtkNullElementOptions,
786    GenericElementSize,
787    TreeIndicatorElementDraw
788};
789
790#if BROKEN_TEXT_ELEMENT
791
792/*
793 *----------------------------------------------------------------------
794 * Text element (does not work yet).
795 *
796 * According to "Using Windows XP Visual Styles",  we need to select
797 * a font into the DC before calling DrawThemeText().
798 * There's just no easy way to get an HFONT out of a Tk_Font.
799 * Maybe GetThemeFont() would work?
800 *
801 */
802
803typedef struct
804{
805    Tcl_Obj *textObj;
806    Tcl_Obj *fontObj;
807} TextElement;
808
809static Ttk_ElementOptionSpec TextElementOptions[] =
810{
811    { "-text", TK_OPTION_STRING,
812	Tk_Offset(TextElement,textObj), "" },
813    { "-font", TK_OPTION_FONT,
814	Tk_Offset(TextElement,fontObj), DEFAULT_FONT },
815    { NULL }
816};
817
818static void TextElementSize(
819    void *clientData, void *elementRecord, Tk_Window tkwin,
820    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
821{
822    TextElement *element = elementRecord;
823    ElementData *elementData = clientData;
824    RECT rc = {0, 0};
825    HRESULT hr = S_OK;
826
827    if (!InitElementData(elementData, tkwin, 0))
828	return;
829
830    hr = elementData->procs->GetThemeTextExtent(
831	    elementData->hTheme,
832	    elementData->hDC,
833	    elementData->info->partId,
834	    Ttk_StateTableLookup(elementData->info->statemap, 0),
835	    Tcl_GetUnicode(element->textObj),
836	    -1,
837	    DT_LEFT,// | DT_BOTTOM | DT_NOPREFIX,
838	    NULL,
839	    &rc);
840
841    if (SUCCEEDED(hr)) {
842	*widthPtr = rc.right - rc.left;
843	*heightPtr = rc.bottom - rc.top;
844    }
845    if (*widthPtr < 80) *widthPtr = 80;
846    if (*heightPtr < 20) *heightPtr = 20;
847
848    FreeElementData(elementData);
849}
850
851static void TextElementDraw(
852    ClientData clientData, void *elementRecord, Tk_Window tkwin,
853    Drawable d, Ttk_Box b, unsigned int state)
854{
855    TextElement *element = elementRecord;
856    ElementData *elementData = clientData;
857    RECT rc = BoxToRect(b);
858    HRESULT hr = S_OK;
859
860    if (!InitElementData(elementData, tkwin, d))
861	return;
862
863    hr = elementData->procs->DrawThemeText(
864	    elementData->hTheme,
865	    elementData->hDC,
866	    elementData->info->partId,
867	    Ttk_StateTableLookup(elementData->info->statemap, state),
868	    Tcl_GetUnicode(element->textObj),
869	    -1,
870	    DT_LEFT,// | DT_BOTTOM | DT_NOPREFIX,
871	    (state & TTK_STATE_DISABLED) ? DTT_GRAYED : 0,
872	    &rc);
873    FreeElementData(elementData);
874}
875
876static Ttk_ElementSpec TextElementSpec =
877{
878    TK_STYLE_VERSION_2,
879    sizeof(TextElement),
880    TextElementOptions,
881    TextElementSize,
882    TextElementDraw
883};
884
885#endif	/* BROKEN_TEXT_ELEMENT */
886
887/*----------------------------------------------------------------------
888 * +++ Widget layouts:
889 */
890
891TTK_BEGIN_LAYOUT_TABLE(LayoutTable)
892
893TTK_LAYOUT("TButton",
894    TTK_GROUP("Button.button", TTK_FILL_BOTH,
895	TTK_GROUP("Button.focus", TTK_FILL_BOTH,
896	    TTK_GROUP("Button.padding", TTK_FILL_BOTH,
897		TTK_NODE("Button.label", TTK_FILL_BOTH)))))
898
899TTK_LAYOUT("TMenubutton",
900    TTK_NODE("Menubutton.dropdown", TTK_PACK_RIGHT|TTK_FILL_Y)
901    TTK_GROUP("Menubutton.button", TTK_PACK_RIGHT|TTK_EXPAND|TTK_FILL_BOTH,
902	    TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X,
903	        TTK_NODE("Menubutton.label", 0))))
904
905TTK_LAYOUT("Horizontal.TScrollbar",
906    TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X,
907	TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT)
908	TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT)
909	TTK_GROUP("Horizontal.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT,
910	    TTK_NODE("Horizontal.Scrollbar.grip", 0))))
911
912TTK_LAYOUT("Vertical.TScrollbar",
913    TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y,
914	TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP)
915	TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM)
916	TTK_GROUP("Vertical.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT,
917	    TTK_NODE("Vertical.Scrollbar.grip", 0))))
918
919TTK_LAYOUT("Horizontal.TScale",
920    TTK_GROUP("Scale.focus", TTK_EXPAND|TTK_FILL_BOTH,
921	TTK_GROUP("Horizontal.Scale.trough", TTK_EXPAND|TTK_FILL_BOTH,
922	    TTK_NODE("Horizontal.Scale.track", TTK_FILL_X)
923	    TTK_NODE("Horizontal.Scale.slider", TTK_PACK_LEFT) )))
924
925TTK_LAYOUT("Vertical.TScale",
926    TTK_GROUP("Scale.focus", TTK_EXPAND|TTK_FILL_BOTH,
927	TTK_GROUP("Vertical.Scale.trough", TTK_EXPAND|TTK_FILL_BOTH,
928	    TTK_NODE("Vertical.Scale.track", TTK_FILL_Y)
929	    TTK_NODE("Vertical.Scale.slider", TTK_PACK_TOP) )))
930
931TTK_END_LAYOUT_TABLE
932
933/*----------------------------------------------------------------------
934 * +++ XP element info table:
935 */
936
937#define PAD(l,t,r,b) {l,t,r,b}
938#define NOPAD {0,0,0,0}
939
940/* name spec className partId statemap padding flags */
941
942static ElementInfo ElementInfoTable[] = {
943    { "Checkbutton.indicator", &GenericElementSpec, L"BUTTON",
944    	BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS },
945    { "Radiobutton.indicator", &GenericElementSpec, L"BUTTON",
946    	BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS },
947    { "Button.button", &GenericElementSpec, L"BUTTON",
948    	BP_PUSHBUTTON, pushbutton_statemap, PAD(3, 3, 3, 3), IGNORE_THEMESIZE },
949    { "Labelframe.border", &GenericElementSpec, L"BUTTON",
950    	BP_GROUPBOX, groupbox_statemap, PAD(2, 2, 2, 2), 0 },
951    { "Entry.field", &GenericElementSpec, L"EDIT", EP_EDITTEXT,
952    	edittext_statemap, PAD(1, 1, 1, 1), 0 },
953    { "Combobox.field", &GenericElementSpec, L"EDIT",
954	EP_EDITTEXT, combotext_statemap, PAD(1, 1, 1, 1), 0 },
955    { "Combobox.downarrow", &GenericSizedElementSpec, L"COMBOBOX",
956	CP_DROPDOWNBUTTON, combobox_statemap, NOPAD,
957	(SM_CXVSCROLL << 8) | SM_CYVSCROLL },
958    { "Vertical.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR",
959    	SBP_UPPERTRACKVERT, scrollbar_statemap, NOPAD, 0 },
960    { "Vertical.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR",
961    	SBP_THUMBBTNVERT, scrollbar_statemap, NOPAD, 0 },
962    { "Vertical.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR",
963    	SBP_GRIPPERVERT, scrollbar_statemap, NOPAD, 0 },
964    { "Horizontal.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR",
965    	SBP_UPPERTRACKHORZ, scrollbar_statemap, NOPAD, 0 },
966    { "Horizontal.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR",
967   	SBP_THUMBBTNHORZ, scrollbar_statemap, NOPAD, 0 },
968    { "Horizontal.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR",
969    	SBP_GRIPPERHORZ, scrollbar_statemap, NOPAD, 0 },
970    { "Scrollbar.uparrow", &GenericSizedElementSpec, L"SCROLLBAR",
971    	SBP_ARROWBTN, uparrow_statemap, NOPAD,
972	(SM_CXVSCROLL << 8) | SM_CYVSCROLL },
973    { "Scrollbar.downarrow", &GenericSizedElementSpec, L"SCROLLBAR",
974    	SBP_ARROWBTN, downarrow_statemap, NOPAD,
975	(SM_CXVSCROLL << 8) | SM_CYVSCROLL },
976    { "Scrollbar.leftarrow", &GenericSizedElementSpec, L"SCROLLBAR",
977    	SBP_ARROWBTN, leftarrow_statemap, NOPAD,
978	(SM_CXHSCROLL << 8) | SM_CYHSCROLL },
979    { "Scrollbar.rightarrow", &GenericSizedElementSpec, L"SCROLLBAR",
980    	SBP_ARROWBTN, rightarrow_statemap, NOPAD,
981	(SM_CXHSCROLL << 8) | SM_CYHSCROLL },
982    { "Horizontal.Scale.slider", &GenericElementSpec, L"TRACKBAR",
983    	TKP_THUMB, scale_statemap, NOPAD, 0 },
984    { "Vertical.Scale.slider", &GenericElementSpec, L"TRACKBAR",
985    	TKP_THUMBVERT, scale_statemap, NOPAD, 0 },
986    { "Horizontal.Scale.track", &GenericElementSpec, L"TRACKBAR",
987    	TKP_TRACK, scale_statemap, NOPAD, 0 },
988    { "Vertical.Scale.track", &GenericElementSpec, L"TRACKBAR",
989    	TKP_TRACKVERT, scale_statemap, NOPAD, 0 },
990    /* ttk::progressbar elements */
991    { "Horizontal.Progressbar.pbar", &PbarElementSpec, L"PROGRESS",
992    	PP_CHUNK, null_statemap, NOPAD, 0 },
993    { "Vertical.Progressbar.pbar", &PbarElementSpec, L"PROGRESS",
994    	PP_CHUNKVERT, null_statemap, NOPAD, 0 },
995    { "Horizontal.Progressbar.trough", &GenericElementSpec, L"PROGRESS",
996    	PP_BAR, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE },
997    { "Vertical.Progressbar.trough", &GenericElementSpec, L"PROGRESS",
998    	PP_BARVERT, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE },
999    /* ttk::notebook */
1000    { "tab", &TabElementSpec, L"TAB",
1001    	TABP_TABITEM, tabitem_statemap, PAD(3,3,3,0), 0 },
1002    { "client", &GenericElementSpec, L"TAB",
1003    	TABP_PANE, null_statemap, PAD(1,1,3,3), 0 },
1004    { "NotebookPane.background", &GenericElementSpec, L"TAB",
1005    	TABP_BODY, null_statemap, NOPAD, 0 },
1006    { "Toolbutton.border", &GenericElementSpec, L"TOOLBAR",
1007    	TP_BUTTON, toolbutton_statemap, NOPAD,0 },
1008    { "Menubutton.button", &GenericElementSpec, L"TOOLBAR",
1009    	TP_SPLITBUTTON,toolbutton_statemap, NOPAD,0 },
1010    { "Menubutton.dropdown", &GenericElementSpec, L"TOOLBAR",
1011    	TP_SPLITBUTTONDROPDOWN,toolbutton_statemap, NOPAD,0 },
1012    { "Treeview.field", &GenericElementSpec, L"TREEVIEW",
1013	TVP_TREEITEM, treeview_statemap, PAD(1, 1, 1, 1), 0 },
1014    { "Treeitem.indicator", &TreeIndicatorElementSpec, L"TREEVIEW",
1015    	TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS },
1016    { "Treeheading.border", &GenericElementSpec, L"HEADER",
1017    	HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 },
1018    { "sizegrip", &GenericElementSpec, L"STATUS",
1019    	SP_GRIPPER, null_statemap, NOPAD,0 },
1020    { "Spinbox.field", &GenericElementSpec, L"EDIT",
1021	EP_EDITTEXT, edittext_statemap, PAD(1, 1, 1, 1), 0 },
1022    { "Spinbox.uparrow", &SpinboxArrowElementSpec, L"SPIN",
1023	SPNP_UP, spinbutton_statemap, NOPAD,
1024	PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
1025    { "Spinbox.downarrow", &SpinboxArrowElementSpec, L"SPIN",
1026	SPNP_DOWN, spinbutton_statemap, NOPAD,
1027	PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) },
1028
1029#if BROKEN_TEXT_ELEMENT
1030    { "Labelframe.text", &TextElementSpec, L"BUTTON",
1031    	BP_GROUPBOX, groupbox_statemap, NOPAD,0 },
1032#endif
1033
1034    { 0,0,0,0,0,NOPAD,0 }
1035};
1036#undef PAD
1037
1038/*----------------------------------------------------------------------
1039 * Windows Visual Styles API Element Factory
1040 *
1041 * The Vista release has shown that the Windows Visual Styles can be
1042 * extended with additional elements. This element factory can permit
1043 * the programmer to create elements for use with script-defined layouts
1044 *
1045 * eg: to create the small close button:
1046 * style element create smallclose vsapi \
1047 *    WINDOW 19 {disabled 4 pressed 3 active 2 {} 1}
1048 */
1049
1050static int
1051Ttk_CreateVsapiElement(
1052    Tcl_Interp *interp,
1053    void *clientData,
1054    Ttk_Theme theme,
1055    const char *elementName,
1056    int objc,
1057    Tcl_Obj *const objv[])
1058{
1059    XPThemeData *themeData = clientData;
1060    ElementInfo *elementPtr = NULL;
1061    ClientData elementData;
1062    Tcl_UniChar *className;
1063    int partId = 0;
1064    Ttk_StateTable *stateTable;
1065    Ttk_Padding pad = {0, 0, 0, 0};
1066    int flags = 0;
1067    int length = 0;
1068    char *name;
1069    LPWSTR wname;
1070
1071    const char *optionStrings[] =
1072	{ "-padding","-width","-height","-margins",NULL };
1073    enum { O_PADDING, O_WIDTH, O_HEIGHT, O_MARGINS };
1074
1075    if (objc < 2) {
1076	Tcl_AppendResult(interp,
1077	    "missing required arguments 'class' and/or 'partId'", NULL);
1078	return TCL_ERROR;
1079    }
1080
1081    if (Tcl_GetIntFromObj(interp, objv[1], &partId) != TCL_OK) {
1082	return TCL_ERROR;
1083    }
1084    className = Tcl_GetUnicodeFromObj(objv[0], &length);
1085
1086    /* flags or padding */
1087    if (objc > 3) {
1088	int i = 3, option = 0;
1089	for (i = 3; i < objc; i += 2) {
1090	    int tmp = 0;
1091	    if (i == objc -1) {
1092		Tcl_AppendResult(interp, "Missing value for \"",
1093			Tcl_GetString(objv[i]), "\".", NULL);
1094		return TCL_ERROR;
1095	    }
1096	    if (Tcl_GetIndexFromObj(interp, objv[i], optionStrings,
1097		    "option", 0, &option) != TCL_OK)
1098		return TCL_ERROR;
1099	    switch (option) {
1100	    case O_PADDING:
1101		if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) {
1102		    return TCL_ERROR;
1103		}
1104		break;
1105	    case O_MARGINS:
1106		if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) {
1107		    return TCL_ERROR;
1108		}
1109		flags |= PAD_MARGINS;
1110		break;
1111	    case O_WIDTH:
1112		if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1113		    return TCL_ERROR;
1114		}
1115		pad.left = pad.right = tmp;
1116		flags |= IGNORE_THEMESIZE;
1117		break;
1118	    case O_HEIGHT:
1119		if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) {
1120		    return TCL_ERROR;
1121		}
1122		pad.top = pad.bottom = tmp;
1123		flags |= IGNORE_THEMESIZE;
1124		break;
1125	    }
1126	}
1127    }
1128
1129    /* convert a statemap into a state table */
1130    if (objc > 2) {
1131	Tcl_Obj **specs;
1132	int n,j,count, status = TCL_OK;
1133	if (Tcl_ListObjGetElements(interp, objv[2], &count, &specs) != TCL_OK)
1134	    return TCL_ERROR;
1135	/* we over-allocate to ensure there is a terminating entry */
1136	stateTable = (Ttk_StateTable *)
1137		ckalloc(sizeof(Ttk_StateTable) * (count + 1));
1138	memset(stateTable, 0, sizeof(Ttk_StateTable) * (count + 1));
1139	for (n = 0, j = 0; status == TCL_OK && n < count; n += 2, ++j) {
1140	    Ttk_StateSpec spec = {0,0};
1141	    status = Ttk_GetStateSpecFromObj(interp, specs[n], &spec);
1142	    if (status == TCL_OK) {
1143		stateTable[j].onBits = spec.onbits;
1144		stateTable[j].offBits = spec.offbits;
1145		status = Tcl_GetIntFromObj(interp, specs[n+1],
1146			&stateTable[j].index);
1147	    }
1148	}
1149	if (status != TCL_OK) {
1150	    ckfree((char *)stateTable);
1151	    return status;
1152	}
1153    } else {
1154	stateTable = (Ttk_StateTable *)ckalloc(sizeof(Ttk_StateTable));
1155	memset(stateTable, 0, sizeof(Ttk_StateTable));
1156    }
1157
1158    elementPtr = (ElementInfo *)ckalloc(sizeof(ElementInfo));
1159    elementPtr->elementSpec = &GenericElementSpec;
1160    elementPtr->partId = partId;
1161    elementPtr->statemap = stateTable;
1162    elementPtr->padding = pad;
1163    elementPtr->flags = HEAP_ELEMENT | flags;
1164
1165    /* set the element name to an allocated copy */
1166    name = ckalloc(strlen(elementName) + 1);
1167    strcpy(name, elementName);
1168    elementPtr->elementName = name;
1169
1170    /* set the class name to an allocated copy */
1171    wname = (LPWSTR) ckalloc(sizeof(WCHAR) * (length + 1));
1172    wcscpy(wname, className);
1173    elementPtr->className = wname;
1174
1175    elementData = NewElementData(themeData->procs, elementPtr);
1176    Ttk_RegisterElementSpec(
1177	theme, elementName, elementPtr->elementSpec, elementData);
1178
1179    Ttk_RegisterCleanup(interp, elementData, DestroyElementData);
1180    Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1));
1181    return TCL_OK;
1182}
1183
1184/*----------------------------------------------------------------------
1185 * +++ Initialization routine:
1186 */
1187
1188MODULE_SCOPE int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd)
1189{
1190    XPThemeData *themeData;
1191    XPThemeProcs *procs;
1192    HINSTANCE hlibrary;
1193    Ttk_Theme themePtr, parentPtr, vistaPtr;
1194    ElementInfo *infoPtr;
1195    OSVERSIONINFO os;
1196
1197    os.dwOSVersionInfoSize = sizeof(os);
1198    GetVersionEx(&os);
1199
1200    procs = LoadXPThemeProcs(&hlibrary);
1201    if (!procs)
1202	return TCL_ERROR;
1203    procs->stubWindow = hwnd;
1204
1205    /*
1206     * Create the new style engine.
1207     */
1208    parentPtr = Ttk_GetTheme(interp, "winnative");
1209    themePtr = Ttk_CreateTheme(interp, "xpnative", parentPtr);
1210
1211    if (!themePtr)
1212        return TCL_ERROR;
1213
1214    /*
1215     * Set theme data and cleanup proc
1216     */
1217
1218    themeData = (XPThemeData *)ckalloc(sizeof(XPThemeData));
1219    themeData->procs = procs;
1220    themeData->hlibrary = hlibrary;
1221
1222    Ttk_SetThemeEnabledProc(themePtr, XPThemeEnabled, themeData);
1223    Ttk_RegisterCleanup(interp, themeData, XPThemeDeleteProc);
1224    Ttk_RegisterElementFactory(interp, "vsapi", Ttk_CreateVsapiElement, themeData);
1225
1226    /*
1227     * Create the vista theme on suitable platform versions and set the theme
1228     * enable function. The theme itself is defined in script.
1229     */
1230
1231    if (os.dwPlatformId == VER_PLATFORM_WIN32_NT && os.dwMajorVersion > 5) {
1232	vistaPtr = Ttk_CreateTheme(interp, "vista", themePtr);
1233	if (vistaPtr) {
1234	    Ttk_SetThemeEnabledProc(vistaPtr, XPThemeEnabled, themeData);
1235	}
1236    }
1237
1238    /*
1239     * New elements:
1240     */
1241    for (infoPtr = ElementInfoTable; infoPtr->elementName != 0; ++infoPtr) {
1242	ClientData clientData = NewElementData(procs, infoPtr);
1243	Ttk_RegisterElementSpec(
1244	    themePtr, infoPtr->elementName, infoPtr->elementSpec, clientData);
1245	Ttk_RegisterCleanup(interp, clientData, DestroyElementData);
1246    }
1247
1248    Ttk_RegisterElementSpec(themePtr, "Scale.trough", &ttkNullElementSpec, 0);
1249
1250    /*
1251     * Layouts:
1252     */
1253    Ttk_RegisterLayouts(themePtr, LayoutTable);
1254
1255    Tcl_PkgProvide(interp, "ttk::theme::xpnative", TTK_VERSION);
1256
1257    return TCL_OK;
1258}
1259
1260#endif /* HAVE_UXTHEME_H */
1261