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