1/*
2 * tkTreeTheme.c --
3 *
4 *	This module implements platform-specific visual themes.
5 *
6 * Copyright (c) 2006-2009 Tim Baker
7 *
8 * RCS: @(#) $Id: tkTreeTheme.c,v 1.33 2010/03/08 17:04:58 treectrl Exp $
9 */
10
11#if defined(WIN32) || defined(_WIN32)
12#define WINVER 0x0501 /* Cygwin */
13#define _WIN32_WINNT 0x0501 /* ACTCTX stuff */
14#endif
15
16#include "tkTreeCtrl.h"
17#ifdef USE_TTK
18#include "ttk/ttkTheme.h"
19#include "ttk/ttk-extra.h"
20#endif
21
22/* These must agree with tkTreeColumn.c */
23#define COLUMN_STATE_NORMAL 0
24#define COLUMN_STATE_ACTIVE 1
25#define COLUMN_STATE_PRESSED 2
26
27/* These must agree with tkTreeColumn.c */
28#define ARROW_NONE 0
29#define ARROW_UP 1
30#define ARROW_DOWN 2
31
32#ifndef USE_TTK
33#ifdef WIN32
34#include "tkWinInt.h"
35
36#include <uxtheme.h>
37#include <tmschema.h>
38#include <shlwapi.h>
39
40#include <basetyps.h> /* Cygwin */
41#ifndef TMT_CONTENTMARGINS
42#define TMT_CONTENTMARGINS 3602
43#endif
44
45typedef HTHEME (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd,
46    LPCWSTR pszClassList);
47typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme);
48typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme,
49    HDC hdc, int iPartId, int iStateId, const RECT *pRect,
50    OPTIONAL const RECT *pClipRect);
51typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundExProc)(HTHEME hTheme,
52    HDC hdc, int iPartId, int iStateId, const RECT *pRect,
53    DTBGOPTS *PDTBGOPTS);
54typedef HRESULT (STDAPICALLTYPE DrawThemeParentBackgroundProc)(HWND hwnd,
55    HDC hdc, OPTIONAL const RECT *prc);
56typedef HRESULT (STDAPICALLTYPE DrawThemeEdgeProc)(HTHEME hTheme, HDC hdc,
57    int iPartId, int iStateId, const RECT *pDestRect,
58    UINT uEdge, UINT uFlags, RECT *pContentRect);
59typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc,
60    int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
61    DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
62typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundContentRectProc)(
63    HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
64    const RECT *pBoundingRect, RECT *pContentRect);
65typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundExtentProc)(HTHEME hTheme,
66    HDC hdc, int iPartId, int iStateId, const RECT *pContentRect,
67    RECT *pExtentRect);
68typedef HRESULT (STDAPICALLTYPE GetThemeMarginsProc)(HTHEME, HDC,
69    int iPartId, int iStateId, int iPropId, OPTIONAL RECT *prc,
70    MARGINS *pMargins);
71typedef HRESULT (STDAPICALLTYPE GetThemePartSizeProc)(HTHEME, HDC, int iPartId,
72    int iStateId, RECT *prc, enum THEMESIZE eSize, SIZE *psz);
73typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc,
74    int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
75    DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtentRect);
76typedef BOOL (STDAPICALLTYPE IsThemeActiveProc)(VOID);
77typedef BOOL (STDAPICALLTYPE IsAppThemedProc)(VOID);
78typedef BOOL (STDAPICALLTYPE IsThemePartDefinedProc)(HTHEME, int, int);
79typedef HRESULT (STDAPICALLTYPE IsThemeBackgroundPartiallyTransparentProc)(
80    HTHEME, int, int);
81
82typedef struct
83{
84    OpenThemeDataProc				*OpenThemeData;
85    CloseThemeDataProc				*CloseThemeData;
86    DrawThemeBackgroundProc			*DrawThemeBackground;
87    DrawThemeBackgroundExProc			*DrawThemeBackgroundEx;
88    DrawThemeParentBackgroundProc		*DrawThemeParentBackground;
89    DrawThemeEdgeProc				*DrawThemeEdge;
90    DrawThemeTextProc				*DrawThemeText;
91    GetThemeBackgroundContentRectProc		*GetThemeBackgroundContentRect;
92    GetThemeBackgroundExtentProc		*GetThemeBackgroundExtent;
93    GetThemeMarginsProc				*GetThemeMargins;
94    GetThemePartSizeProc			*GetThemePartSize;
95    GetThemeTextExtentProc			*GetThemeTextExtent;
96    IsThemeActiveProc				*IsThemeActive;
97    IsAppThemedProc				*IsAppThemed;
98    IsThemePartDefinedProc			*IsThemePartDefined;
99    IsThemeBackgroundPartiallyTransparentProc 	*IsThemeBackgroundPartiallyTransparent;
100} XPThemeProcs;
101
102typedef struct
103{
104    HINSTANCE hlibrary;
105    XPThemeProcs *procs;
106    int registered;
107    int themeEnabled;
108    SIZE buttonOpen;
109    SIZE buttonClosed;
110} XPThemeData;
111
112typedef struct TreeThemeData_
113{
114    HTHEME hThemeHEADER;
115    HTHEME hThemeTREEVIEW;
116} TreeThemeData_;
117
118static XPThemeProcs *procs = NULL;
119static XPThemeData *appThemeData = NULL;
120TCL_DECLARE_MUTEX(themeMutex)
121
122/* Functions imported from kernel32.dll requiring windows XP or greater. */
123/* But I already link to GetVersionEx so is this importing needed? */
124typedef HANDLE (STDAPICALLTYPE CreateActCtxAProc)(PCACTCTXA pActCtx);
125typedef BOOL (STDAPICALLTYPE ActivateActCtxProc)(HANDLE hActCtx, ULONG_PTR *lpCookie);
126typedef BOOL (STDAPICALLTYPE DeactivateActCtxProc)(DWORD dwFlags, ULONG_PTR ulCookie);
127typedef VOID (STDAPICALLTYPE ReleaseActCtxProc)(HANDLE hActCtx);
128
129typedef struct
130{
131    CreateActCtxAProc *CreateActCtxA;
132    ActivateActCtxProc *ActivateActCtx;
133    DeactivateActCtxProc *DeactivateActCtx;
134    ReleaseActCtxProc *ReleaseActCtx;
135} ActCtxProcs;
136
137static ActCtxProcs *
138GetActCtxProcs(void)
139{
140    HINSTANCE hInst;
141    ActCtxProcs *procs = (ActCtxProcs *) ckalloc(sizeof(ActCtxProcs));
142
143    hInst = LoadLibrary("kernel32.dll"); /* FIXME: leak? */
144    if (hInst != 0)
145    {
146 #define LOADPROC(name) \
147	(0 != (procs->name = (name ## Proc *)GetProcAddress(hInst, #name) ))
148
149	if (LOADPROC(CreateActCtxA) &&
150	    LOADPROC(ActivateActCtx) &&
151	    LOADPROC(DeactivateActCtx) &&
152	    LOADPROC(ReleaseActCtx))
153	{
154	    return procs;
155	}
156
157#undef LOADPROC
158    }
159
160    ckfree((char*)procs);
161    return NULL;
162}
163
164static HMODULE thisModule = NULL;
165
166/* Return the HMODULE for this treectrl.dll. */
167static HMODULE
168GetMyHandle(void)
169{
170#if 1
171    return thisModule;
172#else
173    HMODULE hModule = NULL;
174
175    /* FIXME: Only >=NT so I shouldn't link to it? But I already linked to
176     * GetVersionEx so will it run on 95/98? */
177    GetModuleHandleEx(
178	GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
179	(LPCTSTR)&appThemeData,
180	&hModule);
181    return hModule;
182#endif
183}
184
185BOOL WINAPI
186DllMain(
187    HINSTANCE hInst,	/* Library instance handle. */
188    DWORD reason,	/* Reason this function is being called. */
189    LPVOID reserved)	/* Not used. */
190{
191    if (reason == DLL_PROCESS_ATTACH) {
192	thisModule = (HMODULE) hInst;
193    }
194    return TRUE;
195}
196
197static HANDLE
198ActivateManifestContext(ActCtxProcs *procs, ULONG_PTR *ulpCookie)
199{
200    ACTCTXA actctx;
201    HANDLE hCtx;
202#if 1
203    char myPath[1024];
204    DWORD len;
205
206    if (procs == NULL)
207	return INVALID_HANDLE_VALUE;
208
209    len = GetModuleFileName(GetMyHandle(),myPath,1024);
210    myPath[len] = 0;
211
212    ZeroMemory(&actctx, sizeof(actctx));
213    actctx.cbSize = sizeof(actctx);
214    actctx.lpSource = myPath;
215    actctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
216#else
217
218    if (procs == NULL)
219	return INVALID_HANDLE_VALUE;
220
221    ZeroMemory(&actctx, sizeof(actctx));
222    actctx.cbSize = sizeof(actctx);
223    actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID;
224    actctx.hModule = GetMyHandle();
225#endif
226    actctx.lpResourceName = MAKEINTRESOURCE(2);
227
228    hCtx = procs->CreateActCtxA(&actctx);
229    if (hCtx == INVALID_HANDLE_VALUE)
230    {
231	char msg[1024];
232	DWORD err = GetLastError();
233	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
234		FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)msg,
235		sizeof(msg), 0);
236	return INVALID_HANDLE_VALUE;
237    }
238
239    if (procs->ActivateActCtx(hCtx, ulpCookie))
240	return hCtx;
241
242    return INVALID_HANDLE_VALUE;
243}
244
245static void
246DeactivateManifestContext(ActCtxProcs *procs, HANDLE hCtx, ULONG_PTR ulpCookie)
247{
248    if (procs == NULL)
249	return;
250
251    if (hCtx != INVALID_HANDLE_VALUE)
252    {
253	procs->DeactivateActCtx(0, ulpCookie);
254	procs->ReleaseActCtx(hCtx);
255    }
256
257    ckfree((char*)procs);
258}
259
260/* http://www.manbu.net/Lib/En/Class5/Sub16/1/29.asp */
261static int
262ComCtlVersionOK(void)
263{
264    HINSTANCE handle;
265    typedef HRESULT (STDAPICALLTYPE DllGetVersionProc)(DLLVERSIONINFO *);
266    DllGetVersionProc *pDllGetVersion;
267    int result = FALSE;
268    ActCtxProcs *procs;
269    HANDLE hCtx;
270    ULONG_PTR ulpCookie;
271
272    procs = GetActCtxProcs();
273    hCtx = ActivateManifestContext(procs, &ulpCookie);
274    handle = LoadLibrary("comctl32.dll");
275    DeactivateManifestContext(procs, hCtx, ulpCookie);
276    if (handle == NULL)
277	return FALSE;
278    pDllGetVersion = (DllGetVersionProc *) GetProcAddress(handle,
279	    "DllGetVersion");
280    if (pDllGetVersion != NULL) {
281	DLLVERSIONINFO dvi;
282
283	memset(&dvi, '\0', sizeof(dvi));
284	dvi.cbSize = sizeof(dvi);
285	if ((*pDllGetVersion)(&dvi) == NOERROR)
286	    result = dvi.dwMajorVersion >= 6;
287    }
288    FreeLibrary(handle);
289    return result;
290}
291
292/*
293 *----------------------------------------------------------------------
294 *
295 * LoadXPThemeProcs --
296 *	Initialize XP theming support.
297 *
298 *	XP theme support is included in UXTHEME.DLL
299 *	We dynamically load this DLL at runtime instead of linking
300 *	to it at build-time.
301 *
302 * Returns:
303 *	A pointer to an XPThemeProcs table if successful, NULL otherwise.
304 *----------------------------------------------------------------------
305 */
306
307static XPThemeProcs *
308LoadXPThemeProcs(HINSTANCE *phlib)
309{
310    OSVERSIONINFO os;
311
312    /*
313     * We have to check whether we are running at least on Windows XP.
314     * In order to determine this we call GetVersionEx directly, although
315     * it would be a good idea to wrap it inside a function similar to
316     * TkWinGetPlatformId...
317     */
318    ZeroMemory(&os, sizeof(os));
319    os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
320    GetVersionEx(&os);
321    if ((os.dwMajorVersion >= 5 && os.dwMinorVersion >= 1) ||
322	    (os.dwMajorVersion > 5)) {
323	/*
324	 * We are running under Windows XP or a newer version.
325	 * Load the library "uxtheme.dll", where the native widget
326	 * drawing routines are implemented.
327	 */
328	HINSTANCE handle;
329	*phlib = handle = LoadLibrary("uxtheme.dll");
330	if (handle != 0) {
331	    /*
332	     * We have successfully loaded the library. Proceed in storing the
333	     * addresses of the functions we want to use.
334	     */
335	    XPThemeProcs *procs = (XPThemeProcs*)ckalloc(sizeof(XPThemeProcs));
336#define LOADPROC(name) \
337	(0 != (procs->name = (name ## Proc *)GetProcAddress(handle, #name) ))
338
339	    if (   LOADPROC(OpenThemeData)
340		&& LOADPROC(CloseThemeData)
341		&& LOADPROC(DrawThemeBackground)
342		&& LOADPROC(DrawThemeBackgroundEx)
343		&& LOADPROC(DrawThemeParentBackground)
344		&& LOADPROC(DrawThemeEdge)
345		&& LOADPROC(DrawThemeText)
346		&& LOADPROC(GetThemeBackgroundContentRect)
347		&& LOADPROC(GetThemeBackgroundExtent)
348		&& LOADPROC(GetThemeMargins)
349		&& LOADPROC(GetThemePartSize)
350		&& LOADPROC(GetThemeTextExtent)
351		&& LOADPROC(IsThemeActive)
352		&& LOADPROC(IsAppThemed)
353		&& LOADPROC(IsThemePartDefined)
354		&& LOADPROC(IsThemeBackgroundPartiallyTransparent)
355		&& ComCtlVersionOK()
356	    ) {
357		return procs;
358	    }
359#undef LOADPROC
360	    ckfree((char*)procs);
361	}
362    }
363    return 0;
364}
365
366int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
367    int arrow, int visIndex, int x, int y, int width, int height)
368{
369    HTHEME hTheme;
370    HDC hDC;
371    TkWinDCState dcState;
372    RECT rc;
373    HRESULT hr;
374
375    int iPartId = HP_HEADERITEM;
376    int iStateId = HIS_NORMAL;
377
378    switch (state) {
379	case COLUMN_STATE_ACTIVE:  iStateId = HIS_HOT; break;
380	case COLUMN_STATE_PRESSED: iStateId = HIS_PRESSED; break;
381    }
382
383    if (!appThemeData->themeEnabled || !procs)
384	return TCL_ERROR;
385
386    hTheme = tree->themeData->hThemeHEADER;
387    if (!hTheme)
388	return TCL_ERROR;
389
390#if 0 /* Always returns FALSE */
391    if (!procs->IsThemePartDefined(
392	hTheme,
393	iPartId,
394	iStateId)) {
395	return TCL_ERROR;
396    }
397#endif
398
399    rc.left = x;
400    rc.top = y;
401    rc.right = x + width;
402    rc.bottom = y + height;
403
404    /* Is transparent for the default XP style. */
405    if (procs->IsThemeBackgroundPartiallyTransparent(
406	hTheme,
407	iPartId,
408	iStateId)) {
409#if 1
410	/* What color should I use? */
411	Tk_Fill3DRectangle(tree->tkwin, drawable, tree->border, x, y, width, height, 0, TK_RELIEF_FLAT);
412#else
413	/* This draws nothing, maybe because the parent window is not
414	 * themed */
415	procs->DrawThemeParentBackground(
416	    hwnd,
417	    hDC,
418	    &rc);
419#endif
420    }
421
422    hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
423
424#if 0
425    {
426	/* Default XP theme gives rect 3 pixels narrower than rc */
427	RECT contentRect, extentRect;
428	hr = procs->GetThemeBackgroundContentRect(
429	    hTheme,
430	    hDC,
431	    iPartId,
432	    iStateId,
433	    &rc,
434	    &contentRect
435	);
436	dbwin("GetThemeBackgroundContentRect width=%d height=%d\n",
437	    contentRect.right - contentRect.left,
438	    contentRect.bottom - contentRect.top);
439
440	/* Gives rc */
441	hr = procs->GetThemeBackgroundExtent(
442	    hTheme,
443	    hDC,
444	    iPartId,
445	    iStateId,
446	    &contentRect,
447	    &extentRect
448	);
449	dbwin("GetThemeBackgroundExtent width=%d height=%d\n",
450	    extentRect.right - extentRect.left,
451	    extentRect.bottom - extentRect.top);
452    }
453#endif
454
455    hr = procs->DrawThemeBackground(
456	hTheme,
457	hDC,
458	iPartId,
459	iStateId,
460	&rc,
461	NULL);
462
463    TkWinReleaseDrawableDC(drawable, hDC, &dcState);
464
465    if (hr != S_OK)
466	return TCL_ERROR;
467
468    return TCL_OK;
469}
470
471int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int arrow, int bounds[4])
472{
473    Window win = Tk_WindowId(tree->tkwin);
474    HTHEME hTheme;
475    HDC hDC;
476    TkWinDCState dcState;
477    HRESULT hr;
478    MARGINS margins;
479
480    int iPartId = HP_HEADERITEM;
481    int iStateId = HIS_NORMAL;
482
483    switch (state) {
484	case COLUMN_STATE_ACTIVE:  iStateId = HIS_HOT; break;
485	case COLUMN_STATE_PRESSED: iStateId = HIS_PRESSED; break;
486    }
487
488    if (!appThemeData->themeEnabled || !procs)
489	return TCL_ERROR;
490
491    hTheme = tree->themeData->hThemeHEADER;
492    if (!hTheme)
493	return TCL_ERROR;
494
495    hDC = TkWinGetDrawableDC(tree->display, win, &dcState);
496
497    /* The default XP themes give 3,0,0,0 which makes little sense since
498     * it is the *right* side that should not be drawn over by text; the
499     * 2-pixel wide header divider is on the right */
500    hr = procs->GetThemeMargins(
501	hTheme,
502	hDC,
503	iPartId,
504	iStateId,
505	TMT_CONTENTMARGINS,
506	NULL,
507	&margins);
508
509    TkWinReleaseDrawableDC(win, hDC, &dcState);
510
511    if (hr != S_OK)
512	return TCL_ERROR;
513
514    bounds[0] = margins.cxLeftWidth;
515    bounds[1] = margins.cyTopHeight;
516    bounds[2] = margins.cxRightWidth;
517    bounds[3] = margins.cyBottomHeight;
518/*
519dbwin("margins %d %d %d %d\n", bounds[0], bounds[1], bounds[2], bounds[3]);
520*/
521    return TCL_OK;
522}
523
524int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up,
525    int x, int y, int width, int height)
526{
527#if 1
528    XColor *color;
529    GC gc;
530    int i;
531
532    if (!appThemeData->themeEnabled || !procs)
533	return TCL_ERROR;
534
535    color = Tk_GetColor(tree->interp, tree->tkwin, "#ACA899");
536    gc = Tk_GCForColor(color, drawable);
537
538    if (up) {
539	for (i = 0; i < height; i++) {
540	    XDrawLine(tree->display, drawable, gc,
541		x + width / 2 - i, y + i,
542		x + width / 2 + i + 1, y + i);
543	}
544    } else {
545	for (i = 0; i < height; i++) {
546	    XDrawLine(tree->display, drawable, gc,
547		x + width / 2 - i, y + (height - 1) - i,
548		x + width / 2 + i + 1, y + (height - 1) - i);
549	}
550    }
551
552    Tk_FreeColor(color);
553    return TCL_OK;
554#else
555    /* Doesn't seem that Microsoft actually implemented this */
556    Window win = Tk_WindowId(tree->tkwin);
557    HWND hwnd = Tk_GetHWND(win);
558    HTHEME hTheme;
559    HDC hDC;
560    TkWinDCState dcState;
561    RECT rc;
562    HRESULT hr;
563
564    int iPartId = HP_HEADERSORTARROW;
565    int iStateId = up ? HSAS_SORTEDUP : HSAS_SORTEDDOWN;
566
567    if (!appThemeData->themeEnabled || !procs)
568	return TCL_ERROR;
569
570    hTheme = tree->themeData->hThemeHEADER;
571    if (!hTheme)
572	return TCL_ERROR;
573
574    if (!procs->IsThemePartDefined(
575	hTheme,
576	iPartId,
577	iStateId)) {
578	return TCL_ERROR;
579    }
580
581    hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
582
583    rc.left = x;
584    rc.top = y;
585    rc.right = x + width;
586    rc.bottom = y + height;
587
588    hr = procs->DrawThemeBackground(
589	hTheme,
590	hDC,
591	iPartId,
592	iStateId,
593	&rc,
594	NULL);
595
596    TkWinReleaseDrawableDC(drawable, hDC, &dcState);
597    return TCL_OK;
598#endif /* 0 */
599}
600
601int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open,
602    int x, int y, int width, int height)
603{
604    HTHEME hTheme;
605    HDC hDC;
606    TkWinDCState dcState;
607    RECT rc;
608    HRESULT hr;
609    int iPartId, iStateId;
610
611    if (!appThemeData->themeEnabled || !procs)
612	return TCL_ERROR;
613
614    iPartId  = TVP_GLYPH;
615    iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
616
617    hTheme = tree->themeData->hThemeTREEVIEW;
618    if (!hTheme)
619	return TCL_ERROR;
620
621#if 0 /* Always returns FALSE */
622    if (!procs->IsThemePartDefined(
623	hTheme,
624	iPartId,
625	iStateId)) {
626	return TCL_ERROR;
627    }
628#endif
629
630    hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
631
632    rc.left = x;
633    rc.top = y;
634    rc.right = x + width;
635    rc.bottom = y + height;
636    hr = procs->DrawThemeBackground(
637	hTheme,
638	hDC,
639	iPartId,
640	iStateId,
641	&rc,
642	NULL);
643
644    TkWinReleaseDrawableDC(drawable, hDC, &dcState);
645
646    if (hr != S_OK)
647	return TCL_ERROR;
648
649    return TCL_OK;
650}
651
652int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open,
653    int *widthPtr, int *heightPtr)
654{
655    HTHEME hTheme;
656    HDC hDC;
657    TkWinDCState dcState;
658    HRESULT hr;
659    SIZE size;
660    int iPartId, iStateId;
661
662    if (!appThemeData->themeEnabled || !procs)
663	return TCL_ERROR;
664
665    /* Use cached values */
666    size = open ? appThemeData->buttonOpen : appThemeData->buttonClosed;
667    if (size.cx > 1) {
668	*widthPtr = size.cx;
669	*heightPtr = size.cy;
670	return TCL_OK;
671    }
672
673    iPartId  = TVP_GLYPH;
674    iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
675
676    hTheme = tree->themeData->hThemeTREEVIEW;
677    if (!hTheme)
678	return TCL_ERROR;
679
680#if 0 /* Always returns FALSE */
681    if (!procs->IsThemePartDefined(
682	hTheme,
683	iPartId,
684	iStateId)) {
685	return TCL_ERROR;
686    }
687#endif
688
689    hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
690
691    /* Returns 9x9 for default XP style */
692    hr = procs->GetThemePartSize(
693	hTheme,
694	hDC,
695	iPartId,
696	iStateId,
697	NULL,
698	TS_DRAW,
699	&size
700    );
701
702    TkWinReleaseDrawableDC(drawable, hDC, &dcState);
703
704    /* With RandomN of 10000, I eventually get hr=E_HANDLE, invalid handle */
705    /* Not any longer since I don't call OpenThemeData/CloseThemeData for
706     * every call. */
707    if (hr != S_OK)
708	return TCL_ERROR;
709
710    /* Gave me 0,0 for a non-default theme, even though glyph existed */
711    if ((size.cx <= 1) && (size.cy <= 1))
712	return TCL_ERROR;
713
714    /* Cache the values */
715    if (open)
716	appThemeData->buttonOpen = size;
717    else
718	appThemeData->buttonClosed = size;
719
720    *widthPtr = size.cx;
721    *heightPtr = size.cy;
722    return TCL_OK;
723}
724
725int TreeTheme_GetArrowSize(TreeCtrl *tree, Drawable drawable, int up, int *widthPtr, int *heightPtr)
726{
727    if (!appThemeData->themeEnabled || !procs)
728	return TCL_ERROR;
729
730    *widthPtr = 9;
731    *heightPtr = 5;
732    return TCL_OK;
733}
734
735int TreeTheme_SetBorders(TreeCtrl *tree)
736{
737    return TCL_ERROR;
738}
739
740int
741TreeTheme_DrawBorders(
742    TreeCtrl *tree,
743    Drawable drawable
744    )
745{
746    return TCL_ERROR;
747}
748
749void
750TreeTheme_Relayout(
751    TreeCtrl *tree
752    )
753{
754}
755
756int
757TreeTheme_IsDesktopComposited(
758    TreeCtrl *tree
759    )
760{
761    /* TODO:
762	Detect Vista/Win7 use of Desktop Window Manager using
763	  DwmIsCompositionEnabled().
764	WndProc should listen for WM_DWMCOMPOSITIONCHANGED.
765    */
766#if 1
767    /* On Win7 I see lots of flickering with the dragimage in the demo
768     * "Explorer (Large Icons)", so Composition must not work quite how I
769     * expected. */
770    return FALSE;
771#elif 0
772    HMODULE library = LoadLibrary("dwmapi.dll");
773    int result = FALSE;
774
775    if (0 != library) {
776	typedef BOOL (STDAPICALLTYPE DwmIsCompositionEnabledProc)(BOOL *pfEnabled);
777	DwmIsCompositionEnabledProc *proc;
778
779	if (0 != (proc = GetProcAddress(library, "DwmIsCompositionEnabled"))) {
780	    BOOL enabled = FALSE;
781	    result = SUCCEEDED(proc(&enabled)) && enabled;
782	}
783
784	FreeLibrary(library);
785    }
786
787    return result;
788#else
789/* http://weblogs.asp.net/kennykerr/archive/2006/08/10/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager.aspx */
790bool IsCompositionEnabled()
791{
792    HMODULE library = ::LoadLibrary(L"dwmapi.dll");
793    bool result = false;
794
795    if (0 != library)
796    {
797        if (0 != ::GetProcAddress(library,
798                                  "DwmIsCompositionEnabled"))
799        {
800            BOOL enabled = FALSE;
801            result = SUCCEEDED(::DwmIsCompositionEnabled(&enabled)) && enabled;
802        }
803
804        VERIFY(::FreeLibrary(library));
805    }
806
807    return result;
808}
809#endif
810    return FALSE;
811}
812
813#if !defined(WM_THEMECHANGED)
814#define WM_THEMECHANGED 0x031A
815#endif
816
817static LRESULT WINAPI
818WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
819{
820    Tcl_Interp *interp = (Tcl_Interp *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
821
822    switch (msg) {
823	case WM_THEMECHANGED:
824	    Tcl_MutexLock(&themeMutex);
825	    appThemeData->themeEnabled = procs->IsThemeActive() &&
826		    procs->IsAppThemed();
827	    appThemeData->buttonClosed.cx = appThemeData->buttonOpen.cx = -1;
828	    Tcl_MutexUnlock(&themeMutex);
829	    Tree_TheWorldHasChanged(interp);
830	    break;
831    }
832    return DefWindowProc(hwnd, msg, wp, lp);
833}
834
835static CHAR windowClassName[32] = "TreeCtrlMonitorClass";
836
837static BOOL
838RegisterThemeMonitorWindowClass(HINSTANCE hinst)
839{
840    WNDCLASSEX wc;
841
842    wc.cbSize        = sizeof(WNDCLASSEX);
843    wc.style         = CS_HREDRAW | CS_VREDRAW;
844    wc.lpfnWndProc   = (WNDPROC)WndProc;
845    wc.cbClsExtra    = 0;
846    wc.cbWndExtra    = 0;
847    wc.hInstance     = hinst;
848    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
849    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
850    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
851    wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
852    wc.lpszMenuName  = windowClassName;
853    wc.lpszClassName = windowClassName;
854
855    return RegisterClassEx(&wc);
856}
857
858static HWND
859CreateThemeMonitorWindow(HINSTANCE hinst, Tcl_Interp *interp)
860{
861    CHAR title[32] = "TreeCtrlMonitorWindow";
862    HWND hwnd;
863
864    hwnd = CreateWindow(windowClassName, title, WS_OVERLAPPEDWINDOW,
865	CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
866	NULL, NULL, hinst, NULL);
867    if (!hwnd)
868	return NULL;
869
870    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG)interp);
871    ShowWindow(hwnd, SW_HIDE);
872    UpdateWindow(hwnd);
873
874    return hwnd;
875}
876
877typedef struct PerInterpData PerInterpData;
878struct PerInterpData
879{
880    HWND hwnd;
881};
882
883static void FreeAssocData(ClientData clientData, Tcl_Interp *interp)
884{
885    PerInterpData *data = (PerInterpData *) clientData;
886
887    DestroyWindow(data->hwnd);
888    ckfree((char *) data);
889}
890
891void TreeTheme_ThemeChanged(TreeCtrl *tree)
892{
893    Window win = Tk_WindowId(tree->tkwin);
894    HWND hwnd = Tk_GetHWND(win);
895
896    if (tree->themeData != NULL) {
897	if (tree->themeData->hThemeHEADER != NULL) {
898	    procs->CloseThemeData(tree->themeData->hThemeHEADER);
899	    tree->themeData->hThemeHEADER = NULL;
900	}
901	if (tree->themeData->hThemeTREEVIEW != NULL) {
902	    procs->CloseThemeData(tree->themeData->hThemeTREEVIEW);
903	    tree->themeData->hThemeTREEVIEW = NULL;
904	}
905    }
906
907    if (!appThemeData->themeEnabled || !procs)
908	return;
909
910    if (tree->themeData == NULL)
911	tree->themeData = (TreeThemeData) ckalloc(sizeof(TreeThemeData_));
912
913    tree->themeData->hThemeHEADER = procs->OpenThemeData(hwnd, L"HEADER");
914    tree->themeData->hThemeTREEVIEW = procs->OpenThemeData(hwnd, L"TREEVIEW");
915}
916
917int TreeTheme_Init(TreeCtrl *tree)
918{
919    Window win = Tk_WindowId(tree->tkwin);
920    HWND hwnd = Tk_GetHWND(win);
921
922    if (!appThemeData->themeEnabled || !procs)
923	return TCL_ERROR;
924
925    tree->themeData = (TreeThemeData) ckalloc(sizeof(TreeThemeData_));
926
927    /* http://www.codeproject.com/cs/miscctrl/themedtabpage.asp?msg=1445385#xx1445385xx */
928    /* http://msdn2.microsoft.com/en-us/library/ms649781.aspx */
929
930    tree->themeData->hThemeHEADER = procs->OpenThemeData(hwnd, L"HEADER");
931    tree->themeData->hThemeTREEVIEW = procs->OpenThemeData(hwnd, L"TREEVIEW");
932    return TCL_OK;
933}
934
935int TreeTheme_Free(TreeCtrl *tree)
936{
937    if (tree->themeData != NULL) {
938	if (tree->themeData->hThemeHEADER != NULL)
939	    procs->CloseThemeData(tree->themeData->hThemeHEADER);
940	if (tree->themeData->hThemeTREEVIEW != NULL)
941	    procs->CloseThemeData(tree->themeData->hThemeTREEVIEW);
942	ckfree((char *) tree->themeData);
943    }
944    return TCL_OK;
945}
946
947int TreeTheme_InitInterp(Tcl_Interp *interp)
948{
949    HWND hwnd;
950    PerInterpData *data;
951
952    Tcl_MutexLock(&themeMutex);
953
954    /* This is done once per-application */
955    if (appThemeData == NULL) {
956	appThemeData = (XPThemeData *) ckalloc(sizeof(XPThemeData));
957	appThemeData->procs = LoadXPThemeProcs(&appThemeData->hlibrary);
958	appThemeData->registered = FALSE;
959	appThemeData->themeEnabled = FALSE;
960	appThemeData->buttonClosed.cx = appThemeData->buttonOpen.cx = -1;
961
962	procs = appThemeData->procs;
963
964	if (appThemeData->procs) {
965	    /* Check this again if WM_THEMECHANGED arrives */
966	    appThemeData->themeEnabled = procs->IsThemeActive() &&
967		    procs->IsAppThemed();
968
969	    appThemeData->registered =
970		RegisterThemeMonitorWindowClass(Tk_GetHINSTANCE());
971	}
972    }
973
974    Tcl_MutexUnlock(&themeMutex);
975
976    if (!procs || !appThemeData->registered)
977	return TCL_ERROR;
978
979    /* Per-interp */
980    hwnd = CreateThemeMonitorWindow(Tk_GetHINSTANCE(), interp);
981    if (!hwnd)
982	return TCL_ERROR;
983
984    data = (PerInterpData *) ckalloc(sizeof(PerInterpData));
985    data->hwnd = hwnd;
986    Tcl_SetAssocData(interp, "TreeCtrlTheme", FreeAssocData, (ClientData) data);
987
988    return TCL_OK;
989}
990
991#elif defined(MAC_TK_COCOA)
992
993#import <Cocoa/Cocoa.h>
994#import <Carbon/Carbon.h>
995#include "tkMacOSXInt.h"
996
997/*
998 * Since TkMacOSXSetupDrawingContext() isn't in the stubs table I call
999 * XFillRectangle which will create the Pixmap context if it doesn't
1000 * exist.  THIS WON'T WORK FOR DRAWING IN A WINDOW.
1001 */
1002static CGContextRef
1003GetCGContextForDrawable(
1004    TreeCtrl *tree,
1005    Drawable d,
1006    int x, int y, int width, int height)
1007{
1008    MacDrawable *macDraw = (MacDrawable *) d;
1009
1010    if (macDraw->context == NULL) {
1011	GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC);
1012	XFillRectangle(tree->display, d, gc, x, y, width, height);
1013    }
1014
1015    return macDraw->context;
1016}
1017
1018static HIThemeButtonDrawInfo
1019GetThemeButtonDrawInfo(
1020    TreeCtrl *tree,
1021    int state,
1022    int arrow)
1023{
1024    HIThemeButtonDrawInfo info;
1025
1026    info.version = 0;
1027    switch (state) {
1028	case COLUMN_STATE_ACTIVE:  info.state = kThemeStateActive /* kThemeStateRollover */; break;
1029	case COLUMN_STATE_PRESSED: info.state = kThemeStatePressed; break;
1030	default:		   info.state = kThemeStateActive; break;
1031    }
1032    /* Background window */
1033    if (!tree->isActive)
1034	info.state = kThemeStateInactive;
1035    info.kind = kThemeListHeaderButton;
1036    info.value = (arrow != ARROW_NONE) ? kThemeButtonOn : kThemeButtonOff;
1037    switch (arrow) {
1038	case ARROW_NONE: info.adornment = kThemeAdornmentHeaderButtonNoSortArrow; break;
1039	case ARROW_UP: info.adornment = kThemeAdornmentHeaderButtonSortUp; break;
1040	case ARROW_DOWN: info.adornment = kThemeAdornmentDefault; break;
1041    }
1042
1043    return info;
1044}
1045
1046int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
1047    int arrow, int visIndex, int x, int y, int width, int height)
1048{
1049    MacDrawable *macDraw = (MacDrawable *) drawable;
1050    CGRect bounds;
1051    HIThemeButtonDrawInfo info;
1052    CGContextRef context;
1053    HIShapeRef boundsRgn;
1054    CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0,
1055	.ty = macDraw->size.height};
1056    int leftEdgeOffset;
1057
1058    if (!(macDraw->flags & TK_IS_PIXMAP))
1059	return TCL_ERROR;
1060
1061    info = GetThemeButtonDrawInfo(tree, state, arrow);
1062
1063    /* Really want TkMacOSXSetupDrawingContext here. */
1064    context = GetCGContextForDrawable(tree, drawable, x, y, width, height);
1065    if (context == NULL)
1066	return TCL_ERROR;
1067    CGContextSaveGState(context);
1068    CGContextConcatCTM(context, t);
1069
1070    /* See SF patch 'aqua header drawing - ID: 1356447' */
1071    /* The left edge overlaps the right edge of the previous column. */
1072    /* Only show the left edge if this is the first column or the
1073     * "blue" column (with a sort arrow). */
1074    if (visIndex == 0 || arrow == ARROW_NONE)
1075	leftEdgeOffset = 0;
1076    else
1077	leftEdgeOffset = -1;
1078
1079    /* Create a clipping region as big as the header. */
1080    bounds.origin.x = macDraw->xOff + x + leftEdgeOffset;
1081    bounds.origin.y = macDraw->yOff + y;
1082    bounds.size.width = width - leftEdgeOffset;
1083    bounds.size.height = height;
1084    boundsRgn = HIShapeCreateWithRect(&bounds);
1085
1086    /* Set the clipping region */
1087    HIShapeReplacePathInCGContext(boundsRgn, context);
1088    CGContextEOClip(context);
1089
1090    /* See SF patch 'aqua header drawing - ID: 1356447' */
1091    if (visIndex == 0)
1092	leftEdgeOffset = 0;
1093    else
1094	leftEdgeOffset = -1;
1095    bounds.origin.x = macDraw->xOff + x + leftEdgeOffset;
1096    bounds.size.width = width - leftEdgeOffset;
1097
1098    (void) HIThemeDrawButton(&bounds, &info, context,
1099	kHIThemeOrientationNormal, NULL);
1100
1101    CGContextRestoreGState(context);
1102    CFRelease(boundsRgn);
1103
1104    return TCL_OK;
1105}
1106
1107/* List headers are a fixed height on Aqua */
1108int TreeTheme_GetHeaderFixedHeight(TreeCtrl *tree, int *heightPtr)
1109{
1110    SInt32 metric;
1111
1112    GetThemeMetric(kThemeMetricListHeaderHeight, &metric);
1113    *heightPtr = metric;
1114    return TCL_OK;
1115}
1116
1117int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int arrow,
1118    int bounds[4])
1119{
1120    CGRect inBounds, outBounds;
1121    HIThemeButtonDrawInfo info;
1122    SInt32 metric;
1123
1124    inBounds.origin.x = 0;
1125    inBounds.origin.y = 0;
1126    inBounds.size.width = 100;
1127    GetThemeMetric(kThemeMetricListHeaderHeight, &metric);
1128    inBounds.size.height = metric;
1129
1130    info = GetThemeButtonDrawInfo(tree, state, arrow);
1131
1132    (void) HIThemeGetButtonContentBounds(
1133	&inBounds,
1134	&info,
1135	&outBounds);
1136
1137    bounds[0] = CGRectGetMinX(outBounds) - CGRectGetMinX(inBounds);
1138    bounds[1] = CGRectGetMinY(outBounds) - CGRectGetMinY(inBounds);
1139    bounds[2] = CGRectGetMaxX(inBounds) - CGRectGetMaxX(outBounds);
1140    bounds[3] = CGRectGetMaxY(inBounds) - CGRectGetMaxY(outBounds);
1141
1142    return TCL_OK;
1143}
1144
1145int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up, int x, int y, int width, int height)
1146{
1147    return TCL_ERROR;
1148}
1149
1150int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open,
1151    int x, int y, int width, int height)
1152{
1153    MacDrawable *macDraw = (MacDrawable *) drawable;
1154    CGRect bounds;
1155    HIThemeButtonDrawInfo info;
1156    CGContextRef context;
1157    CGAffineTransform t = { .a = 1, .b = 0, .c = 0, .d = -1, .tx = 0,
1158	.ty = macDraw->size.height};
1159    HIShapeRef clipRgn;
1160
1161    if (!(macDraw->flags & TK_IS_PIXMAP))
1162	return TCL_ERROR;
1163
1164    bounds.origin.x = macDraw->xOff + x;
1165    bounds.origin.y = macDraw->yOff + y;
1166    bounds.size.width = width;
1167    bounds.size.height = height;
1168
1169    info.version = 0;
1170    info.state = kThemeStateActive;
1171    info.kind = kThemeDisclosureButton;
1172    info.value = open ? kThemeDisclosureDown : kThemeDisclosureRight;
1173    info.adornment = kThemeAdornmentDrawIndicatorOnly;
1174
1175    /* Really want TkMacOSXSetupDrawingContext here. */
1176    /* FIXME: If the context doesn't exist, this will draw a box under
1177     * the button. But the item background will already have been drawn
1178     * into the Pixmap, creating the context, so this should work. */
1179    context = GetCGContextForDrawable(tree, drawable, x, y, width, height);
1180    if (context == NULL)
1181	return TCL_ERROR;
1182    CGContextSaveGState(context);
1183    CGContextConcatCTM(context, t);
1184
1185    /* Set the clipping region */
1186    clipRgn = HIShapeCreateWithRect(&bounds);
1187    HIShapeReplacePathInCGContext(clipRgn, context);
1188    CGContextEOClip(context);
1189
1190    (void) HIThemeDrawButton(&bounds, &info, context,
1191	kHIThemeOrientationNormal, NULL);
1192
1193    CGContextRestoreGState(context);
1194    CFRelease(clipRgn);
1195
1196    return TCL_OK;
1197}
1198
1199int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open, int *widthPtr, int *heightPtr)
1200{
1201    SInt32 metric;
1202
1203    (void) GetThemeMetric(
1204	kThemeMetricDisclosureTriangleWidth,
1205	&metric);
1206    *widthPtr = metric;
1207
1208    (void) GetThemeMetric(
1209	kThemeMetricDisclosureTriangleHeight,
1210	&metric);
1211    *heightPtr = metric;
1212
1213    return TCL_OK;
1214}
1215
1216int TreeTheme_GetArrowSize(TreeCtrl *tree, Drawable drawable, int up, int *widthPtr, int *heightPtr)
1217{
1218    return TCL_ERROR;
1219}
1220
1221int TreeTheme_SetBorders(TreeCtrl *tree)
1222{
1223    return TCL_ERROR;
1224}
1225
1226int
1227TreeTheme_DrawBorders(
1228    TreeCtrl *tree,
1229    Drawable drawable
1230    )
1231{
1232    return TCL_ERROR;
1233}
1234
1235void
1236TreeTheme_Relayout(
1237    TreeCtrl *tree
1238    )
1239{
1240}
1241
1242int
1243TreeTheme_IsDesktopComposited(
1244    TreeCtrl *tree
1245    )
1246{
1247    return TRUE;
1248}
1249
1250void TreeTheme_ThemeChanged(TreeCtrl *tree)
1251{
1252}
1253
1254int TreeTheme_Init(TreeCtrl *tree)
1255{
1256    return TCL_OK;
1257}
1258
1259int TreeTheme_Free(TreeCtrl *tree)
1260{
1261    return TCL_OK;
1262}
1263
1264int TreeTheme_InitInterp(Tcl_Interp *interp)
1265{
1266    return TCL_OK;
1267}
1268
1269#elif defined(MAC_TK_CARBON)
1270
1271#include <Carbon/Carbon.h>
1272#include "tkMacOSXInt.h"
1273
1274static RgnHandle oldClip = NULL, boundsRgn = NULL;
1275
1276int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
1277    int arrow, int visIndex, int x, int y, int width, int height)
1278{
1279    MacDrawable *macWin = (MacDrawable *) drawable;
1280    Rect bounds;
1281    ThemeButtonDrawInfo info;
1282    CGrafPtr saveWorld;
1283    GDHandle saveDevice;
1284    GWorldPtr destPort;
1285
1286    bounds.left = macWin->xOff + x;
1287    bounds.top = macWin->yOff + y;
1288    bounds.right = bounds.left + width;
1289    bounds.bottom = bounds.top + height;
1290
1291    switch (state) {
1292	case COLUMN_STATE_ACTIVE:  info.state = kThemeStateActive /* kThemeStateRollover */; break;
1293	case COLUMN_STATE_PRESSED: info.state = kThemeStatePressed; break;
1294	default:		   info.state = kThemeStateActive; break;
1295    }
1296    /* Background window */
1297    if (!tree->isActive)
1298	info.state = kThemeStateInactive;
1299    info.value = (arrow != 0) ? kThemeButtonOn : kThemeButtonOff;
1300    info.adornment = (arrow == 1) ? kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone;
1301
1302    destPort = TkMacOSXGetDrawablePort(drawable);
1303    GetGWorld(&saveWorld, &saveDevice);
1304    SetGWorld(destPort, 0);
1305    TkMacOSXSetUpClippingRgn(drawable);
1306
1307    /* Save the old clipping region because we are going to modify it. */
1308    if (oldClip == NULL)
1309	oldClip = NewRgn();
1310    GetClip(oldClip);
1311
1312    /* Create a clipping region as big as the header. */
1313    if (boundsRgn == NULL)
1314	boundsRgn = NewRgn();
1315    RectRgn(boundsRgn, &bounds);
1316
1317    /* Set the clipping region to the intersection of the two regions. */
1318    SectRgn(oldClip, boundsRgn, boundsRgn);
1319    SetClip(boundsRgn);
1320
1321    /* Draw the left edge outside of the clipping region. */
1322    bounds.left -= 1;
1323
1324    (void) DrawThemeButton(&bounds, kThemeListHeaderButton, &info,
1325	NULL,	/*prevInfo*/
1326	NULL,	/*eraseProc*/
1327	NULL,	/*labelProc*/
1328	NULL);	/*userData*/
1329
1330    SetClip(oldClip);
1331    SetGWorld(saveWorld,saveDevice);
1332
1333    return TCL_OK;
1334}
1335
1336/* List headers are a fixed height on Aqua */
1337int TreeTheme_GetHeaderFixedHeight(TreeCtrl *tree, int *heightPtr)
1338{
1339    SInt32 metric;
1340
1341    GetThemeMetric(kThemeMetricListHeaderHeight, &metric);
1342    *heightPtr = metric;
1343    return TCL_OK;
1344}
1345
1346int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int arrow, int bounds[4])
1347{
1348    Rect inBounds, outBounds;
1349    ThemeButtonDrawInfo info;
1350    SInt32 metric;
1351
1352    inBounds.left = 0;
1353    inBounds.top = 0;
1354    inBounds.right = 100;
1355    GetThemeMetric(kThemeMetricListHeaderHeight, &metric);
1356    inBounds.bottom = metric;
1357
1358    switch (state) {
1359	case COLUMN_STATE_ACTIVE:  info.state = kThemeStateActive /* kThemeStateRollover */; break;
1360	case COLUMN_STATE_PRESSED: info.state = kThemeStatePressed; break;
1361	default:		   info.state = kThemeStateActive; break;
1362    }
1363    /* Background window */
1364    if (!tree->isActive)
1365	info.state = kThemeStateInactive;
1366    info.value = (arrow != 0) ? kThemeButtonOn : kThemeButtonOff;
1367    info.adornment = (arrow == 1) ? kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone;
1368
1369    (void) GetThemeButtonContentBounds(
1370	&inBounds,
1371	kThemeListHeaderButton,
1372	&info,
1373	&outBounds);
1374
1375    bounds[0] = outBounds.left - inBounds.left;
1376    bounds[1] = outBounds.top - inBounds.top;
1377    bounds[2] = inBounds.right - outBounds.right;
1378    bounds[3] = inBounds.bottom - outBounds.bottom;
1379
1380    return TCL_OK;
1381}
1382
1383int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up, int x, int y, int width, int height)
1384{
1385    return TCL_ERROR;
1386}
1387
1388int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open, int x, int y, int width, int height)
1389{
1390    MacDrawable *macWin = (MacDrawable *) drawable;
1391    Rect bounds;
1392    ThemeButtonDrawInfo info;
1393    CGrafPtr saveWorld;
1394    GDHandle saveDevice;
1395    GWorldPtr destPort;
1396
1397    bounds.left = macWin->xOff + x;
1398    bounds.top = macWin->yOff + y;
1399    bounds.right = bounds.left + width;
1400    bounds.bottom = bounds.top + height;
1401
1402    info.state = kThemeStateActive;
1403    info.value = open ? kThemeDisclosureDown : kThemeDisclosureRight;
1404    info.adornment = kThemeAdornmentNone;
1405
1406    destPort = TkMacOSXGetDrawablePort(drawable);
1407    GetGWorld(&saveWorld, &saveDevice);
1408    SetGWorld(destPort, 0);
1409    TkMacOSXSetUpClippingRgn(drawable);
1410
1411    /* Drawing the disclosure triangles produces a white background.
1412     * To avoid this, set the clipping region to the exact area where
1413     * pixels are drawn. */
1414
1415    /* Save the old clipping region because we are going to modify it. */
1416    if (oldClip == NULL)
1417	oldClip = NewRgn();
1418    GetClip(oldClip);
1419
1420    /* Create a clipping region containing the pixels of the button. */
1421    if (boundsRgn == NULL)
1422	boundsRgn = NewRgn();
1423    (void) GetThemeButtonRegion(&bounds, kThemeDisclosureButton, &info,
1424	boundsRgn);
1425
1426    /* Set the clipping region to the intersection of the two regions. */
1427    SectRgn(oldClip, boundsRgn, boundsRgn);
1428    SetClip(boundsRgn);
1429
1430    (void) DrawThemeButton(&bounds, kThemeDisclosureButton, &info,
1431	NULL,	/*prevInfo*/
1432	NULL,	/*eraseProc*/
1433	NULL,	/*labelProc*/
1434	NULL);	/*userData*/
1435
1436    /* Restore the original clipping region. */
1437    SetClip(oldClip);
1438
1439    SetGWorld(saveWorld,saveDevice);
1440
1441    return TCL_OK;
1442}
1443
1444int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open, int *widthPtr, int *heightPtr)
1445{
1446    SInt32 metric;
1447
1448    (void) GetThemeMetric(
1449	kThemeMetricDisclosureTriangleWidth,
1450	&metric);
1451    *widthPtr = metric;
1452
1453    (void) GetThemeMetric(
1454	kThemeMetricDisclosureTriangleHeight,
1455	&metric);
1456    *heightPtr = metric;
1457
1458    return TCL_OK;
1459}
1460
1461int TreeTheme_GetArrowSize(TreeCtrl *tree, Drawable drawable, int up, int *widthPtr, int *heightPtr)
1462{
1463    return TCL_ERROR;
1464}
1465
1466
1467int TreeTheme_SetBorders(TreeCtrl *tree)
1468{
1469    return TCL_ERROR;
1470}
1471
1472int
1473TreeTheme_DrawBorders(
1474    TreeCtrl *tree,
1475    Drawable drawable
1476    )
1477{
1478    return TCL_ERROR;
1479}
1480
1481void
1482TreeTheme_Relayout(
1483    TreeCtrl *tree
1484    )
1485{
1486}
1487
1488int
1489TreeTheme_IsDesktopComposited(
1490    TreeCtrl *tree
1491    )
1492{
1493    return TRUE;
1494}
1495
1496void TreeTheme_ThemeChanged(TreeCtrl *tree)
1497{
1498}
1499
1500int TreeTheme_Init(TreeCtrl *tree)
1501{
1502    return TCL_OK;
1503}
1504
1505int TreeTheme_Free(TreeCtrl *tree)
1506{
1507    return TCL_OK;
1508}
1509
1510int TreeTheme_InitInterp(Tcl_Interp *interp)
1511{
1512    return TCL_OK;
1513}
1514
1515#else /* MAC_TK_CARBON */
1516
1517int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
1518    int arrow, int visIndex, int x, int y, int width, int height)
1519{
1520    return TCL_ERROR;
1521}
1522
1523int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int arrow, int bounds[4])
1524{
1525    return TCL_ERROR;
1526}
1527
1528int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up, int x, int y, int width, int height)
1529{
1530    return TCL_ERROR;
1531}
1532
1533int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open, int x, int y, int width, int height)
1534{
1535    return TCL_ERROR;
1536}
1537
1538int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open, int *widthPtr, int *heightPtr)
1539{
1540    return TCL_ERROR;
1541}
1542
1543int TreeTheme_GetArrowSize(TreeCtrl *tree, Drawable drawable, int up, int *widthPtr, int *heightPtr)
1544{
1545    return TCL_ERROR;
1546}
1547
1548
1549int TreeTheme_SetBorders(TreeCtrl *tree)
1550{
1551    return TCL_ERROR;
1552}
1553
1554int
1555TreeTheme_DrawBorders(
1556    TreeCtrl *tree,
1557    Drawable drawable
1558    )
1559{
1560    return TCL_ERROR;
1561}
1562
1563void
1564TreeTheme_Relayout(
1565    TreeCtrl *tree
1566    )
1567{
1568}
1569
1570int
1571TreeTheme_IsDesktopComposited(
1572    TreeCtrl *tree
1573    )
1574{
1575    return FALSE;
1576}
1577
1578void TreeTheme_ThemeChanged(TreeCtrl *tree)
1579{
1580}
1581
1582int TreeTheme_Init(TreeCtrl *tree)
1583{
1584    return TCL_OK;
1585}
1586
1587int TreeTheme_Free(TreeCtrl *tree)
1588{
1589    return TCL_OK;
1590}
1591
1592int TreeTheme_InitInterp(Tcl_Interp *interp)
1593{
1594    return TCL_OK;
1595}
1596
1597#endif /* !WIN32 && !MAC_OSX_TK */
1598
1599#else /* !USE_TTK */
1600
1601typedef struct TreeThemeData_
1602{
1603    Ttk_Layout layout;
1604    Ttk_Layout buttonLayout;
1605    Ttk_Layout headingLayout;
1606    Tk_OptionTable buttonOptionTable;
1607    Tk_OptionTable headingOptionTable;
1608    Ttk_Box clientBox;
1609    int buttonWidth[2], buttonHeight[2];
1610    Ttk_Padding buttonPadding[2];
1611} TreeThemeData_;
1612
1613int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
1614    int arrow, int visIndex, int x, int y, int width, int height)
1615{
1616    TreeThemeData themeData = tree->themeData;
1617    Ttk_Layout layout = themeData->headingLayout;
1618    Ttk_State ttk_state = 0;
1619    Ttk_Box box;
1620
1621    if (layout == NULL)
1622	return TCL_ERROR;
1623
1624    box = Ttk_MakeBox(x, y, width, height);
1625
1626    switch (state) {
1627	case COLUMN_STATE_ACTIVE:  ttk_state = TTK_STATE_ACTIVE; break;
1628	case COLUMN_STATE_PRESSED: ttk_state = TTK_STATE_PRESSED; break;
1629    }
1630
1631    eTtk_RebindSublayout(layout, NULL); /* !!! rebind to column */
1632    eTtk_PlaceLayout(layout, ttk_state, box);
1633    eTtk_DrawLayout(layout, ttk_state, drawable);
1634
1635    return TCL_OK;
1636}
1637
1638int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int arrow, int bounds[4])
1639{
1640    return TCL_ERROR;
1641}
1642
1643int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up, int x, int y, int width, int height)
1644{
1645    return TCL_ERROR;
1646}
1647
1648/* From ttkTreeview.c */
1649#define TTK_STATE_OPEN TTK_STATE_USER1
1650
1651int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open, int x, int y, int width, int height)
1652{
1653    TreeThemeData themeData = tree->themeData;
1654    Ttk_Layout layout = themeData->buttonLayout;
1655    Ttk_State ttk_state = 0;
1656    Ttk_Box box;
1657    Ttk_Padding padding;
1658
1659    if (layout == NULL)
1660	return TCL_ERROR;
1661
1662    open = open ? 1 : 0;
1663    padding = themeData->buttonPadding[open];
1664    x -= padding.left;
1665    y -= padding.top;
1666    width = themeData->buttonWidth[open];
1667    height = themeData->buttonHeight[open];
1668    box = Ttk_MakeBox(x, y, width, height);
1669
1670    ttk_state = open ? TTK_STATE_OPEN : 0;
1671
1672    eTtk_RebindSublayout(layout, NULL); /* !!! rebind to item */
1673    eTtk_PlaceLayout(layout, ttk_state, box);
1674    eTtk_DrawLayout(layout, ttk_state, drawable);
1675
1676    return TCL_OK;
1677}
1678
1679int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open, int *widthPtr, int *heightPtr)
1680{
1681    TreeThemeData themeData = tree->themeData;
1682    Ttk_Padding padding;
1683
1684    if (themeData->buttonLayout == NULL)
1685	return TCL_ERROR;
1686
1687    open = open ? 1 : 0;
1688    padding = themeData->buttonPadding[open];
1689    *widthPtr = themeData->buttonWidth[open] - padding.left - padding.right;
1690    *heightPtr = themeData->buttonHeight[open] - padding.top - padding.bottom;
1691    return TCL_OK;
1692}
1693
1694int TreeTheme_GetArrowSize(TreeCtrl *tree, Drawable drawable, int up, int *widthPtr, int *heightPtr)
1695{
1696    return TCL_ERROR;
1697}
1698
1699int TreeTheme_SetBorders(TreeCtrl *tree)
1700{
1701    TreeThemeData themeData = tree->themeData;
1702    Tk_Window tkwin = tree->tkwin;
1703    Ttk_Box clientBox = themeData->clientBox;
1704
1705    tree->inset.left = clientBox.x;
1706    tree->inset.top = clientBox.y;
1707    tree->inset.right = Tk_Width(tkwin) - (clientBox.x + clientBox.width);
1708    tree->inset.bottom = Tk_Height(tkwin) - (clientBox.y + clientBox.height);
1709
1710    return TCL_OK;
1711}
1712
1713/*
1714 * This routine is a big hack so that the "field" element (of the TreeCtrl
1715 * layout) doesn't erase the entire background of the window. This routine
1716 * draws each edge of the layout into a pixmap and copies the pixmap to the
1717 * window.
1718 */
1719int
1720TreeTheme_DrawBorders(
1721    TreeCtrl *tree,
1722    Drawable drawable
1723    )
1724{
1725    TreeThemeData themeData = tree->themeData;
1726    Tk_Window tkwin = tree->tkwin;
1727    Ttk_Box winBox = Ttk_WinBox(tree->tkwin);
1728    Ttk_State state = 0; /* ??? */
1729    int left, top, right, bottom;
1730    Drawable pixmapLR = None, pixmapTB = None;
1731
1732    left = tree->inset.left;
1733    top = tree->inset.top;
1734    right = tree->inset.right;
1735    bottom = tree->inset.bottom;
1736
1737    /* If the Ttk layout doesn't specify any borders or padding, then
1738     * draw nothing. */
1739    if (left < 1 && top < 1 && right < 1 && bottom < 1)
1740	return TCL_OK;
1741
1742    if (left > 0 || top > 0)
1743	eTtk_PlaceLayout(themeData->layout, state, winBox);
1744
1745    if (left > 0 || right > 0) {
1746	pixmapLR = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin),
1747		MAX(left, right), Tk_Height(tkwin), Tk_Depth(tkwin));
1748    }
1749
1750    if (top > 0 || bottom > 0) {
1751	pixmapTB = Tk_GetPixmap(tree->display, Tk_WindowId(tkwin),
1752		Tk_Width(tkwin), MAX(top, bottom), Tk_Depth(tkwin));
1753    }
1754
1755/*    DebugDrawBorder(tree, 0, left, top, right, bottom);*/
1756
1757    if (left > 0) {
1758	eTtk_DrawLayout(themeData->layout, state, pixmapLR);
1759	XCopyArea(tree->display, pixmapLR, drawable,
1760		tree->copyGC, 0, 0,
1761		left, Tk_Height(tkwin),
1762		0, 0);
1763    }
1764
1765    if (top > 0) {
1766	eTtk_DrawLayout(themeData->layout, state, pixmapTB);
1767	XCopyArea(tree->display, pixmapTB, drawable,
1768		tree->copyGC, 0, 0,
1769		Tk_Width(tkwin), top,
1770		0, 0);
1771    }
1772
1773    if (right > 0) {
1774	winBox.x -= winBox.width - right;
1775	eTtk_PlaceLayout(themeData->layout, state, winBox);
1776
1777	eTtk_DrawLayout(themeData->layout, state, pixmapLR);
1778	XCopyArea(tree->display, pixmapLR, drawable,
1779		tree->copyGC, 0, 0,
1780		right, Tk_Height(tkwin),
1781		Tree_BorderRight(tree), 0);
1782    }
1783
1784    if (bottom > 0) {
1785	winBox.x = 0;
1786	winBox.y -= winBox.height - bottom;
1787	eTtk_PlaceLayout(themeData->layout, state, winBox);
1788
1789	eTtk_DrawLayout(themeData->layout, state, pixmapTB);
1790	XCopyArea(tree->display, pixmapTB, drawable,
1791		tree->copyGC, 0, 0,
1792		Tk_Width(tkwin), bottom,
1793		0, Tree_BorderBottom(tree));
1794    }
1795
1796    if (pixmapLR != None)
1797	Tk_FreePixmap(tree->display, pixmapLR);
1798    if (pixmapTB != None)
1799	Tk_FreePixmap(tree->display, pixmapTB);
1800
1801    return TCL_OK;
1802}
1803
1804static Tk_OptionSpec NullOptionSpecs[] =
1805{
1806    {TK_OPTION_END, 0,0,0, NULL, -1,-1, 0,0,0}
1807};
1808
1809/* from ttkTreeview.c */
1810static Ttk_Layout
1811GetSublayout(
1812    Tcl_Interp *interp,
1813    Ttk_Theme themePtr,
1814    Ttk_Layout parentLayout,
1815    const char *layoutName,
1816    Tk_OptionTable optionTable,
1817    Ttk_Layout *layoutPtr)
1818{
1819    Ttk_Layout newLayout = eTtk_CreateSublayout(
1820	    interp, themePtr, parentLayout, layoutName, optionTable);
1821
1822    if (newLayout) {
1823	if (*layoutPtr)
1824	    eTtk_FreeLayout(*layoutPtr);
1825	*layoutPtr = newLayout;
1826    }
1827    return newLayout;
1828}
1829
1830Ttk_Layout
1831TreeCtrlGetLayout(
1832    Tcl_Interp *interp,
1833    Ttk_Theme themePtr,
1834    void *recordPtr
1835    )
1836{
1837    TreeCtrl *tree = recordPtr;
1838    TreeThemeData themeData = tree->themeData;
1839    Ttk_Layout treeLayout, newLayout;
1840
1841    if (themeData->headingOptionTable == NULL)
1842	themeData->headingOptionTable = Tk_CreateOptionTable(interp, NullOptionSpecs);
1843    if (themeData->buttonOptionTable == NULL)
1844	themeData->buttonOptionTable = Tk_CreateOptionTable(interp, NullOptionSpecs);
1845
1846    /* Create a new layout record based on widget -style or class */
1847    treeLayout = eTtk_CreateLayout(interp, themePtr, "TreeCtrl", tree,
1848	    tree->optionTable, tree->tkwin);
1849
1850    /* Create a sublayout for drawing the column headers. The sublayout is
1851     * called "TreeCtrl.TreeCtrlHeading" by default. The actual layout specification
1852     * was defined by Ttk_RegisterLayout("TreeCtrlHeading") below. */
1853    newLayout = GetSublayout(interp, themePtr, treeLayout,
1854	    ".TreeCtrlHeading", themeData->headingOptionTable,
1855	    &themeData->headingLayout);
1856    if (newLayout == NULL)
1857	return NULL;
1858
1859    newLayout = GetSublayout(interp, themePtr, treeLayout,
1860	    ".TreeCtrlButton", themeData->buttonOptionTable,
1861	    &themeData->buttonLayout);
1862    if (newLayout == NULL)
1863	return NULL;
1864
1865    return treeLayout;
1866}
1867
1868void
1869TreeCtrlDoLayout(
1870    void *recordPtr
1871    )
1872{
1873    TreeCtrl *tree = recordPtr;
1874    TreeThemeData themeData = tree->themeData;
1875    Ttk_LayoutNode *node;
1876    Ttk_Box winBox = Ttk_WinBox(tree->tkwin);
1877    Ttk_State state = 0; /* ??? */
1878
1879    eTtk_PlaceLayout(themeData->layout, state, winBox);
1880    node = eTtk_LayoutFindNode(themeData->layout, "client");
1881    if (node != NULL)
1882	themeData->clientBox = eTtk_LayoutNodeInternalParcel(themeData->layout, node);
1883    else
1884	themeData->clientBox = winBox;
1885
1886    /* Size of opened and closed buttons. */
1887    eTtk_LayoutSize(themeData->buttonLayout, TTK_STATE_OPEN,
1888	    &themeData->buttonWidth[1], &themeData->buttonHeight[1]);
1889    eTtk_LayoutSize(themeData->buttonLayout, 0,
1890	    &themeData->buttonWidth[0], &themeData->buttonHeight[0]);
1891
1892    node = eTtk_LayoutFindNode(themeData->buttonLayout, "indicator");
1893    if (node != NULL) {
1894	Ttk_Box box1, box2;
1895
1896	box1 = Ttk_MakeBox(0, 0, themeData->buttonWidth[1], themeData->buttonHeight[1]);
1897	eTtk_PlaceLayout(themeData->buttonLayout, TTK_STATE_OPEN, box1);
1898	box2 = eTtk_LayoutNodeInternalParcel(themeData->buttonLayout, node);
1899	themeData->buttonPadding[1] = Ttk_MakePadding(box2.x, box2.y,
1900		(box1.x + box1.width) - (box2.x + box2.width),
1901		(box1.y + box1.height) - (box2.y + box2.height));
1902
1903	box1 = Ttk_MakeBox(0, 0, themeData->buttonWidth[0], themeData->buttonHeight[0]);
1904	eTtk_PlaceLayout(themeData->buttonLayout, 0, box1);
1905	box2 = eTtk_LayoutNodeInternalParcel(themeData->buttonLayout, node);
1906	themeData->buttonPadding[0] = Ttk_MakePadding(box2.x, box2.y,
1907		(box1.x + box1.width) - (box2.x + box2.width),
1908		(box1.y + box1.height) - (box2.y + box2.height));
1909
1910    } else {
1911	themeData->buttonPadding[1] = Ttk_MakePadding(0,0,0,0);
1912	themeData->buttonPadding[0] = Ttk_MakePadding(0,0,0,0);
1913    }
1914}
1915
1916void
1917TreeTheme_Relayout(
1918    TreeCtrl *tree
1919    )
1920{
1921    TreeThemeData themeData = tree->themeData;
1922    Ttk_Theme themePtr = Ttk_GetCurrentTheme(tree->interp);
1923    Ttk_Layout newLayout = TreeCtrlGetLayout(tree->interp, themePtr, tree);
1924
1925    if (newLayout) {
1926	if (themeData->layout) {
1927	    eTtk_FreeLayout(themeData->layout);
1928	}
1929	themeData->layout = newLayout;
1930	TreeCtrlDoLayout(tree);
1931    }
1932}
1933
1934/* HeaderElement is used for Treeheading.cell. The platform-specific code
1935 * will draw the native heading. */
1936typedef struct
1937{
1938    Tcl_Obj *backgroundObj;
1939} HeaderElement;
1940
1941static Ttk_ElementOptionSpec HeaderElementOptions[] =
1942{
1943    { "-background", TK_OPTION_COLOR,
1944	Tk_Offset(HeaderElement, backgroundObj), DEFAULT_BACKGROUND },
1945    {NULL}
1946};
1947
1948static void HeaderElementDraw(
1949    void *clientData, void *elementRecord, Tk_Window tkwin,
1950    Drawable d, Ttk_Box b, Ttk_State state)
1951{
1952    HeaderElement *e = elementRecord;
1953    XColor *color = Tk_GetColorFromObj(tkwin, e->backgroundObj);
1954    GC gc = Tk_GCForColor(color, d);
1955    XFillRectangle(Tk_Display(tkwin), d, gc,
1956	    b.x, b.y, b.width, b.height);
1957}
1958
1959static Ttk_ElementSpec HeaderElementSpec =
1960{
1961    TK_STYLE_VERSION_2,
1962    sizeof(HeaderElement),
1963    HeaderElementOptions,
1964    Ttk_NullElementGeometry,
1965    HeaderElementDraw
1966};
1967
1968/* Default button element (aka Treeitem.indicator). */
1969typedef struct
1970{
1971    Tcl_Obj *backgroundObj;
1972    Tcl_Obj *colorObj;
1973    Tcl_Obj *sizeObj;
1974    Tcl_Obj *thicknessObj;
1975} TreeitemIndicator;
1976
1977static Ttk_ElementOptionSpec TreeitemIndicatorOptions[] =
1978{
1979    { "-buttonbackground", TK_OPTION_COLOR,
1980	Tk_Offset(TreeitemIndicator, backgroundObj), "white" },
1981    { "-buttoncolor", TK_OPTION_COLOR,
1982	Tk_Offset(TreeitemIndicator, colorObj), "#808080" },
1983    { "-buttonsize", TK_OPTION_PIXELS,
1984	Tk_Offset(TreeitemIndicator, sizeObj), "9" },
1985    { "-buttonthickness", TK_OPTION_PIXELS,
1986	Tk_Offset(TreeitemIndicator, thicknessObj), "1" },
1987    {NULL}
1988};
1989
1990static void TreeitemIndicatorSize(
1991    void *clientData, void *elementRecord, Tk_Window tkwin,
1992    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
1993{
1994    TreeitemIndicator *indicator = elementRecord;
1995    int size = 0;
1996
1997    Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &size);
1998
1999    *widthPtr = *heightPtr = size;
2000    *paddingPtr = Ttk_MakePadding(0,0,0,0);
2001}
2002
2003static void TreeitemIndicatorDraw(
2004    void *clientData, void *elementRecord, Tk_Window tkwin,
2005    Drawable d, Ttk_Box b, Ttk_State state)
2006{
2007    TreeitemIndicator *indicator = elementRecord;
2008    int w1, lineLeft, lineTop, buttonLeft, buttonTop, buttonThickness, buttonSize;
2009    XColor *bgColor = Tk_GetColorFromObj(tkwin, indicator->backgroundObj);
2010    XColor *buttonColor = Tk_GetColorFromObj(tkwin, indicator->colorObj);
2011    XGCValues gcValues;
2012    unsigned long gcMask;
2013    GC buttonGC;
2014    Ttk_Padding padding = Ttk_MakePadding(0,0,0,0);
2015
2016    b = Ttk_PadBox(b, padding);
2017
2018    Tk_GetPixelsFromObj(NULL, tkwin, indicator->sizeObj, &buttonSize);
2019    Tk_GetPixelsFromObj(NULL, tkwin, indicator->thicknessObj, &buttonThickness);
2020
2021    w1 = buttonThickness / 2;
2022
2023    /* Left edge of vertical line */
2024    /* Make sure this matches TreeItem_DrawLines() */
2025    lineLeft = b.x + (b.width - buttonThickness) / 2;
2026
2027    /* Top edge of horizontal line */
2028    /* Make sure this matches TreeItem_DrawLines() */
2029    lineTop = b.y + (b.height - buttonThickness) / 2;
2030
2031    buttonLeft = b.x;
2032    buttonTop = b.y;
2033
2034    /* Erase button background */
2035    XFillRectangle(Tk_Display(tkwin), d,
2036	    Tk_GCForColor(bgColor, d),
2037	    buttonLeft + buttonThickness,
2038	    buttonTop + buttonThickness,
2039	    buttonSize - buttonThickness,
2040	    buttonSize - buttonThickness);
2041
2042    gcValues.foreground = buttonColor->pixel;
2043    gcValues.line_width = buttonThickness;
2044    gcMask = GCForeground | GCLineWidth;
2045    buttonGC = Tk_GetGC(tkwin, gcMask, &gcValues);
2046
2047    /* Draw button outline */
2048    XDrawRectangle(Tk_Display(tkwin), d, buttonGC,
2049	    buttonLeft + w1,
2050	    buttonTop + w1,
2051	    buttonSize - buttonThickness,
2052	    buttonSize - buttonThickness);
2053
2054    /* Horizontal '-' */
2055    XFillRectangle(Tk_Display(tkwin), d, buttonGC,
2056	    buttonLeft + buttonThickness * 2,
2057	    lineTop,
2058	    buttonSize - buttonThickness * 4,
2059	    buttonThickness);
2060
2061    if (!(state & TTK_STATE_OPEN)) {
2062	/* Finish '+' */
2063	XFillRectangle(Tk_Display(tkwin), d, buttonGC,
2064		lineLeft,
2065		buttonTop + buttonThickness * 2,
2066		buttonThickness,
2067		buttonSize - buttonThickness * 4);
2068    }
2069
2070    Tk_FreeGC(Tk_Display(tkwin), buttonGC);
2071}
2072
2073static Ttk_ElementSpec TreeitemIndicatorElementSpec =
2074{
2075    TK_STYLE_VERSION_2,
2076    sizeof(TreeitemIndicator),
2077    TreeitemIndicatorOptions,
2078    TreeitemIndicatorSize,
2079    TreeitemIndicatorDraw
2080};
2081
2082TTK_BEGIN_LAYOUT(HeadingLayout)
2083    TTK_NODE("Treeheading.cell", TTK_FILL_BOTH)
2084    TTK_NODE("Treeheading.border", TTK_FILL_BOTH)
2085TTK_END_LAYOUT
2086
2087TTK_BEGIN_LAYOUT(ButtonLayout)
2088    TTK_NODE("Treeitem.indicator", TTK_PACK_LEFT)
2089TTK_END_LAYOUT
2090
2091TTK_BEGIN_LAYOUT(TreeCtrlLayout)
2092    TTK_GROUP("TreeCtrl.field", TTK_FILL_BOTH|TTK_BORDER,
2093	TTK_GROUP("TreeCtrl.padding", TTK_FILL_BOTH,
2094	    TTK_NODE("TreeCtrl.client", TTK_FILL_BOTH)))
2095TTK_END_LAYOUT
2096
2097void TreeTheme_ThemeChanged(TreeCtrl *tree)
2098{
2099}
2100
2101int TreeTheme_Init(TreeCtrl *tree)
2102{
2103    tree->themeData = (TreeThemeData) ckalloc(sizeof(TreeThemeData_));
2104    memset(tree->themeData, '\0', sizeof(TreeThemeData_));
2105
2106    return TCL_OK;
2107}
2108
2109int TreeTheme_Free(TreeCtrl *tree)
2110{
2111    TreeThemeData themeData = tree->themeData;
2112
2113    if (themeData != NULL) {
2114	if (themeData->layout != NULL)
2115	    eTtk_FreeLayout(themeData->layout);
2116	if (themeData->buttonLayout != NULL)
2117	    eTtk_FreeLayout(themeData->buttonLayout);
2118	if (themeData->headingLayout != NULL)
2119	    eTtk_FreeLayout(themeData->headingLayout);
2120	ckfree((char *) themeData);
2121    }
2122    return TCL_OK;
2123}
2124
2125int TreeTheme_InitInterp(Tcl_Interp *interp)
2126{
2127    Ttk_Theme theme = Ttk_GetDefaultTheme(interp);
2128
2129    Ttk_RegisterLayout(theme, "TreeCtrl", TreeCtrlLayout);
2130
2131    /* Problem: what if Treeview also defines this? */
2132    Ttk_RegisterElement(interp, theme, "Treeheading.cell", &HeaderElementSpec, 0);
2133
2134    /* Problem: what if Treeview also defines this? */
2135    Ttk_RegisterElement(interp, theme, "Treeitem.indicator", &TreeitemIndicatorElementSpec, 0);
2136
2137    Ttk_RegisterLayout(theme, "TreeCtrlHeading", HeadingLayout);
2138    Ttk_RegisterLayout(theme, "TreeCtrlButton", ButtonLayout);
2139
2140    return TCL_OK;
2141}
2142
2143#endif /* USE_TTK */
2144
2145