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