summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tkTreeTheme.c657
1 files changed, 657 insertions, 0 deletions
diff --git a/generic/tkTreeTheme.c b/generic/tkTreeTheme.c
new file mode 100644
index 0000000..7c1028d
--- /dev/null
+++ b/generic/tkTreeTheme.c
@@ -0,0 +1,657 @@
+/*
+ * tkTreeTheme.c --
+ *
+ * This module implements platform-specific visual themes.
+ *
+ * Copyright (c) 2005 Tim Baker
+ *
+ * RCS: @(#) $Id: tkTreeTheme.c,v 1.1 2005/05/01 01:45:18 treectrl Exp $
+ */
+
+#ifdef WIN32
+
+#define WINVER 0x0501 /* Cygwin */
+#include "tkTreeCtrl.h"
+#include "tkWinInt.h"
+
+#include <uxtheme.h>
+#include <tmschema.h>
+
+#include <basetyps.h> /* Cygwin */
+#ifndef TMT_CONTENTMARGINS
+#define TMT_CONTENTMARGINS 3602
+#endif
+
+typedef HTHEME (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd,
+ LPCWSTR pszClassList);
+typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme);
+typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme,
+ HDC hdc, int iPartId, int iStateId, const RECT *pRect,
+ OPTIONAL const RECT *pClipRect);
+typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundExProc)(HTHEME hTheme,
+ HDC hdc, int iPartId, int iStateId, const RECT *pRect,
+ DTBGOPTS *PDTBGOPTS);
+typedef HRESULT (STDAPICALLTYPE DrawThemeParentBackgroundProc)(HWND hwnd,
+ HDC hdc, OPTIONAL const RECT *prc);
+typedef HRESULT (STDAPICALLTYPE DrawThemeEdgeProc)(HTHEME hTheme, HDC hdc,
+ int iPartId, int iStateId, const RECT *pDestRect,
+ UINT uEdge, UINT uFlags, RECT *pContentRect);
+typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc,
+ int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
+ DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect);
+typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundContentRectProc)(
+ HTHEME hTheme, HDC hdc, int iPartId, int iStateId,
+ const RECT *pBoundingRect, RECT *pContentRect);
+typedef HRESULT (STDAPICALLTYPE GetThemeBackgroundExtentProc)(HTHEME hTheme,
+ HDC hdc, int iPartId, int iStateId, const RECT *pContentRect,
+ RECT *pExtentRect);
+typedef HRESULT (STDAPICALLTYPE GetThemeMarginsProc)(HTHEME, HDC,
+ int iPartId, int iStateId, int iPropId, OPTIONAL RECT *prc,
+ MARGINS *pMargins);
+typedef HRESULT (STDAPICALLTYPE GetThemePartSizeProc)(HTHEME, HDC, int iPartId,
+ int iStateId, RECT *prc, enum THEMESIZE eSize, SIZE *psz);
+typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc,
+ int iPartId, int iStateId, LPCWSTR pszText, int iCharCount,
+ DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtentRect);
+typedef BOOL (STDAPICALLTYPE IsThemeActiveProc)(VOID);
+typedef BOOL (STDAPICALLTYPE IsThemePartDefinedProc)(HTHEME, int, int);
+typedef HRESULT (STDAPICALLTYPE IsThemeBackgroundPartiallyTransparentProc)(
+ HTHEME, int, int);
+
+typedef struct
+{
+ OpenThemeDataProc *OpenThemeData;
+ CloseThemeDataProc *CloseThemeData;
+ DrawThemeBackgroundProc *DrawThemeBackground;
+ DrawThemeBackgroundExProc *DrawThemeBackgroundEx;
+ DrawThemeParentBackgroundProc *DrawThemeParentBackground;
+ DrawThemeEdgeProc *DrawThemeEdge;
+ DrawThemeTextProc *DrawThemeText;
+ GetThemeBackgroundContentRectProc *GetThemeBackgroundContentRect;
+ GetThemeBackgroundExtentProc *GetThemeBackgroundExtent;
+ GetThemeMarginsProc *GetThemeMargins;
+ GetThemePartSizeProc *GetThemePartSize;
+ GetThemeTextExtentProc *GetThemeTextExtent;
+ IsThemeActiveProc *IsThemeActive;
+ IsThemePartDefinedProc *IsThemePartDefined;
+ IsThemeBackgroundPartiallyTransparentProc *IsThemeBackgroundPartiallyTransparent;
+} XPThemeProcs;
+
+typedef struct
+{
+ HINSTANCE hlibrary;
+ XPThemeProcs *procs;
+ int registered;
+ int themeEnabled;
+} XPThemeData;
+
+static XPThemeProcs *procs = NULL;
+static XPThemeData *themeData = NULL;
+TCL_DECLARE_MUTEX(themeMutex)
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LoadXPThemeProcs --
+ * Initialize XP theming support.
+ *
+ * XP theme support is included in UXTHEME.DLL
+ * We dynamically load this DLL at runtime instead of linking
+ * to it at build-time.
+ *
+ * Returns:
+ * A pointer to an XPThemeProcs table if successful, NULL otherwise.
+ */
+
+static XPThemeProcs *
+LoadXPThemeProcs(HINSTANCE *phlib)
+{
+ OSVERSIONINFO os;
+
+ /*
+ * We have to check whether we are running at least on Windows XP.
+ * In order to determine this we call GetVersionEx directly, although
+ * it would be a good idea to wrap it inside a function similar to
+ * TkWinGetPlatformId...
+ */
+ ZeroMemory(&os, sizeof(os));
+ os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&os);
+ if (os.dwMajorVersion >= 5 && os.dwMinorVersion >= 1) {
+ /*
+ * We are running under Windows XP or a newer version.
+ * Load the library "uxtheme.dll", where the native widget
+ * drawing routines are implemented.
+ */
+ HINSTANCE handle;
+ *phlib = handle = LoadLibrary("uxtheme.dll");
+ if (handle != 0)
+ {
+ /*
+ * We have successfully loaded the library. Proceed in storing the
+ * addresses of the functions we want to use.
+ */
+ XPThemeProcs *procs = (XPThemeProcs*)ckalloc(sizeof(XPThemeProcs));
+#define LOADPROC(name) \
+ (0 != (procs->name = (name ## Proc *)GetProcAddress(handle, #name) ))
+
+ if ( LOADPROC(OpenThemeData)
+ && LOADPROC(CloseThemeData)
+ && LOADPROC(DrawThemeBackground)
+ && LOADPROC(DrawThemeBackgroundEx)
+ && LOADPROC(DrawThemeParentBackground)
+ && LOADPROC(DrawThemeEdge)
+ && LOADPROC(DrawThemeText)
+ && LOADPROC(GetThemeBackgroundContentRect)
+ && LOADPROC(GetThemeBackgroundExtent)
+ && LOADPROC(GetThemeMargins)
+ && LOADPROC(GetThemePartSize)
+ && LOADPROC(GetThemeTextExtent)
+ && LOADPROC(IsThemeActive)
+ && LOADPROC(IsThemePartDefined)
+ && LOADPROC(IsThemeBackgroundPartiallyTransparent)
+ )
+ {
+ return procs;
+ }
+#undef LOADPROC
+ ckfree((char*)procs);
+ }
+ }
+ return 0;
+}
+
+int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state,
+ int x, int y, int width, int height)
+{
+ Window win = Tk_WindowId(tree->tkwin);
+ HWND hwnd = Tk_GetHWND(win);
+ HTHEME hTheme;
+ HDC hDC;
+ TkWinDCState dcState;
+ RECT rc;
+ HRESULT hr;
+
+ int iPartId = HP_HEADERITEM;
+ int iStateId = HIS_NORMAL;
+
+ switch (state)
+ {
+ case 1 : iStateId = HIS_HOT; break;
+ case 2 : iStateId = HIS_PRESSED; break;
+ }
+
+ if (!themeData->themeEnabled || !procs)
+ return TCL_ERROR;
+
+ hTheme = procs->OpenThemeData(hwnd, L"HEADER");
+ if (!hTheme)
+ return TCL_ERROR;
+
+#if 0 /* Always returns FALSE */
+ if (!procs->IsThemePartDefined(
+ hTheme,
+ iPartId,
+ iStateId))
+ {
+ procs->CloseThemeData(hTheme);
+ return TCL_ERROR;
+ }
+#endif
+
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+
+ /* Is transparent for the default XP style. */
+ if (procs->IsThemeBackgroundPartiallyTransparent(
+ hTheme,
+ iPartId,
+ iStateId))
+ {
+#if 1
+ /* What color should I use? */
+ Tk_Fill3DRectangle(tree->tkwin, drawable, tree->border, x, y, width, height, 0, TK_RELIEF_FLAT);
+#else
+ /* This draws nothing, maybe because the parent window is not
+ * themed */
+ procs->DrawThemeParentBackground(
+ hwnd,
+ hDC,
+ &rc);
+#endif
+ }
+
+ hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
+
+#if 0
+ {
+ /* Default XP theme gives rect 3 pixels narrower than rc */
+ RECT contentRect, extentRect;
+ hr = procs->GetThemeBackgroundContentRect(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ &rc,
+ &contentRect
+ );
+ dbwin("GetThemeBackgroundContentRect width=%d height=%d\n",
+ contentRect.right - contentRect.left,
+ contentRect.bottom - contentRect.top);
+
+ /* Gives rc */
+ hr = procs->GetThemeBackgroundExtent(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ &contentRect,
+ &extentRect
+ );
+ dbwin("GetThemeBackgroundExtent width=%d height=%d\n",
+ extentRect.right - extentRect.left,
+ extentRect.bottom - extentRect.top);
+ }
+#endif
+
+ hr = procs->DrawThemeBackground(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ &rc,
+ NULL);
+
+ procs->CloseThemeData(hTheme);
+ TkWinReleaseDrawableDC(drawable, hDC, &dcState);
+
+ if (hr != S_OK)
+ return TCL_ERROR;
+
+ return TCL_OK;
+}
+
+int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int bounds[4])
+{
+ Window win = Tk_WindowId(tree->tkwin);
+ HWND hwnd = Tk_GetHWND(win);
+ HTHEME hTheme;
+ HDC hDC;
+ TkWinDCState dcState;
+ HRESULT hr;
+ MARGINS margins;
+
+ int iPartId = HP_HEADERITEM;
+ int iStateId = HIS_NORMAL;
+
+ switch (state)
+ {
+ case 1 : iStateId = HIS_HOT; break;
+ case 2 : iStateId = HIS_PRESSED; break;
+ }
+
+ if (!themeData->themeEnabled || !procs)
+ return TCL_ERROR;
+
+ hTheme = procs->OpenThemeData(hwnd, L"HEADER");
+ if (!hTheme)
+ return TCL_ERROR;
+
+ hDC = TkWinGetDrawableDC(tree->display, win, &dcState);
+
+ /* The default XP themes give 3,0,0,0 which makes little sense since
+ * it is the *right* side that should not be drawn over by text; the
+ * 2-pixel wide header divider is on the right */
+ hr = procs->GetThemeMargins(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ TMT_CONTENTMARGINS,
+ NULL,
+ &margins);
+
+ procs->CloseThemeData(hTheme);
+ TkWinReleaseDrawableDC(win, hDC, &dcState);
+
+ if (hr != S_OK)
+ return TCL_ERROR;
+
+ bounds[0] = margins.cxLeftWidth;
+ bounds[1] = margins.cyTopHeight;
+ bounds[2] = margins.cxRightWidth;
+ bounds[3] = margins.cyBottomHeight;
+/*
+dbwin("margins %d %d %d %d\n", bounds[0], bounds[1], bounds[2], bounds[3]);
+*/
+ return TCL_OK;
+}
+
+int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up,
+ int x, int y, int width, int height)
+{
+ /* Doesn't seem that Microsoft actually implemented this */
+ return TCL_ERROR;
+
+#if 0
+ Window win = Tk_WindowId(tree->tkwin);
+ HWND hwnd = Tk_GetHWND(win);
+ HTHEME hTheme;
+ HDC hDC;
+ TkWinDCState dcState;
+ RECT rc;
+ HRESULT hr;
+
+ int iPartId = HP_HEADERSORTARROW;
+ int iStateId = up ? HSAS_SORTEDUP : HSAS_SORTEDDOWN;
+
+ if (!themeData->themeEnabled || !procs)
+ return TCL_ERROR;
+
+ hTheme = procs->OpenThemeData(hwnd, L"HEADER");
+ if (!hTheme)
+ return TCL_ERROR;
+
+ if (!procs->IsThemePartDefined(
+ hTheme,
+ iPartId,
+ iStateId))
+ {
+ procs->CloseThemeData(hTheme);
+ return TCL_ERROR;
+ }
+
+ hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
+
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+
+ hr = procs->DrawThemeBackground(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ &rc,
+ NULL);
+
+ procs->CloseThemeData(hTheme);
+ TkWinReleaseDrawableDC(drawable, hDC, &dcState);
+ return TCL_OK;
+#endif /* 0 */
+}
+
+int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open,
+ int x, int y, int width, int height)
+{
+ Window win = Tk_WindowId(tree->tkwin);
+ HWND hwnd = Tk_GetHWND(win);
+ HTHEME hTheme;
+ HDC hDC;
+ TkWinDCState dcState;
+ RECT rc;
+ HRESULT hr;
+
+ if (!themeData->themeEnabled || !procs)
+ return TCL_ERROR;
+
+ int iPartId = TVP_GLYPH;
+ int iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
+
+ hTheme = procs->OpenThemeData(hwnd, L"TREEVIEW");
+ if (!hTheme)
+ return TCL_ERROR;
+
+#if 0 /* Always returns FALSE */
+ if (!procs->IsThemePartDefined(
+ hTheme,
+ iPartId,
+ iStateId))
+ {
+ procs->CloseThemeData(hTheme);
+ return TCL_ERROR;
+ }
+#endif
+
+ hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
+
+ rc.left = x;
+ rc.top = y;
+ rc.right = x + width;
+ rc.bottom = y + height;
+ hr = procs->DrawThemeBackground(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ &rc,
+ NULL);
+
+ procs->CloseThemeData(hTheme);
+ TkWinReleaseDrawableDC(drawable, hDC, &dcState);
+
+ if (hr != S_OK)
+ return TCL_ERROR;
+
+ return TCL_OK;
+}
+
+int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open,
+ int *widthPtr, int *heightPtr)
+{
+ Window win = Tk_WindowId(tree->tkwin);
+ HWND hwnd = Tk_GetHWND(win);
+ HTHEME hTheme;
+ HDC hDC;
+ TkWinDCState dcState;
+ HRESULT hr;
+ SIZE size;
+
+ if (!themeData->themeEnabled || !procs)
+ return TCL_ERROR;
+
+ int iPartId = TVP_GLYPH;
+ int iStateId = open ? GLPS_OPENED : GLPS_CLOSED;
+
+ hTheme = procs->OpenThemeData(hwnd, L"TREEVIEW");
+ if (!hTheme)
+ return TCL_ERROR;
+
+#if 0 /* Always returns FALSE */
+ if (!procs->IsThemePartDefined(
+ hTheme,
+ iPartId,
+ iStateId))
+ {
+ procs->CloseThemeData(hTheme);
+ return TCL_ERROR;
+ }
+#endif
+
+ hDC = TkWinGetDrawableDC(tree->display, drawable, &dcState);
+
+ /* Returns 9x9 for default XP style */
+ hr = procs->GetThemePartSize(
+ hTheme,
+ hDC,
+ iPartId,
+ iStateId,
+ NULL,
+ TS_DRAW,
+ &size
+ );
+
+ procs->CloseThemeData(hTheme);
+ TkWinReleaseDrawableDC(drawable, hDC, &dcState);
+
+ if (hr != S_OK)
+ return TCL_ERROR;
+
+ /* Gave me 0,0 for a non-default theme, even though glyph existed */
+ if ((size.cx <= 1) && (size.cy <= 1))
+ return TCL_ERROR;
+
+ *widthPtr = size.cx;
+ *heightPtr = size.cy;
+ return TCL_OK;
+}
+
+#if !defined(WM_THEMECHANGED)
+#define WM_THEMECHANGED 0x031A
+#endif
+
+static LRESULT WINAPI
+WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ Tcl_Interp *interp = (Tcl_Interp *)GetWindowLong(hwnd, GWL_USERDATA);
+
+ switch (msg) {
+ case WM_THEMECHANGED:
+ Tcl_MutexLock(&themeMutex);
+ themeData->themeEnabled = procs->IsThemeActive();
+ Tcl_MutexUnlock(&themeMutex);
+ Tree_TheWorldHasChanged(interp);
+ break;
+ }
+ return DefWindowProc(hwnd, msg, wp, lp);
+}
+
+static CHAR windowClassName[32] = "TreeCtrlMonitorClass";
+
+static BOOL
+RegisterThemeMonitorWindowClass(HINSTANCE hinst)
+{
+ WNDCLASSEX wc;
+
+ wc.cbSize = sizeof(WNDCLASSEX);
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC)WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hinst;
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
+ wc.lpszMenuName = windowClassName;
+ wc.lpszClassName = windowClassName;
+
+ return RegisterClassEx(&wc);
+}
+
+static HWND
+CreateThemeMonitorWindow(HINSTANCE hinst, Tcl_Interp *interp)
+{
+ CHAR title[32] = "TreeCtrlMonitorWindow";
+ HWND hwnd;
+
+ hwnd = CreateWindow(windowClassName, title, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ NULL, NULL, hinst, NULL);
+ if (!hwnd)
+ return NULL;
+
+ SetWindowLong(hwnd, GWL_USERDATA, (LONG)interp);
+ ShowWindow(hwnd, SW_HIDE);
+ UpdateWindow(hwnd);
+
+ return hwnd;
+}
+
+typedef struct PerInterpData PerInterpData;
+struct PerInterpData
+{
+ HWND hwnd;
+};
+
+static void FreeAssocData(ClientData clientData, Tcl_Interp *interp)
+{
+ PerInterpData *data = (PerInterpData *) clientData;
+
+dbwin("FreeAssocData\n");
+ DestroyWindow(data->hwnd);
+ ckfree((char *) data);
+}
+
+int TreeTheme_Init(Tcl_Interp *interp)
+{
+ HWND hwnd;
+ PerInterpData *data;
+
+ Tcl_MutexLock(&themeMutex);
+
+ /* This is done once per-application */
+ if (themeData == NULL)
+ {
+dbwin("alloc themeData\n");
+ themeData = (XPThemeData *) ckalloc(sizeof(XPThemeData));
+ themeData->procs = LoadXPThemeProcs(&themeData->hlibrary);
+ themeData->registered = FALSE;
+ themeData->themeEnabled = FALSE;
+
+ procs = themeData->procs;
+
+ if (themeData->procs) {
+ /* Check this again if WM_THEMECHANGED arrives */
+ themeData->themeEnabled = procs->IsThemeActive();
+
+ themeData->registered =
+ RegisterThemeMonitorWindowClass(Tk_GetHINSTANCE());
+ }
+ }
+
+ Tcl_MutexUnlock(&themeMutex);
+
+ if (!procs || !themeData->registered)
+ return TCL_ERROR;
+
+ /* Per-interp */
+ hwnd = CreateThemeMonitorWindow(Tk_GetHINSTANCE(), interp);
+ if (!hwnd)
+ return TCL_ERROR;
+dbwin("Tcl_SetAssocData TreeTheme\n");
+
+ data = (PerInterpData *) ckalloc(sizeof(PerInterpData));
+ data->hwnd = hwnd;
+ Tcl_SetAssocData(interp, "TreeTheme", FreeAssocData, (ClientData) data);
+
+ return TCL_OK;
+}
+
+#else /* WIN32 */
+
+int TreeTheme_DrawHeaderItem(TreeCtrl *tree, Drawable drawable, int state, int x, int y, int width, int height)
+{
+ return TCL_ERROR;
+}
+
+int TreeTheme_GetHeaderContentMargins(TreeCtrl *tree, int state, int bounds[4])
+{
+ return TCL_ERROR;
+}
+
+int TreeTheme_DrawHeaderArrow(TreeCtrl *tree, Drawable drawable, int up, int x, int y, int width, int height)
+{
+ return TCL_ERROR;
+}
+
+int TreeTheme_DrawButton(TreeCtrl *tree, Drawable drawable, int open, int x, int y, int width, int height)
+{
+ return TCL_ERROR;
+}
+
+int TreeTheme_GetButtonSize(TreeCtrl *tree, Drawable drawable, int open, int *widthPtr, int *heightPtr)
+{
+ return TCL_ERROR;
+}
+
+int TreeTheme_Init(Tcl_Interp *interp)
+{
+ return TCL_ERROR;
+}
+
+#endif /* WIN32 */
+