1/* 2 * shellicon.c -- 3 * 4 * This is a Tk extension that adds a new element type to TkTreeCtrl. 5 * The element type's name is "shellicon". A shellicon element can 6 * display the icon for a file or folder using Win32 Shell API calls. 7 * 8 * Copyright (c) 2005 Tim Baker 9 * 10 * RCS: @(#) $Id: shellicon.c,v 1.4 2007/01/31 23:20:15 treectrl Exp $ 11 */ 12 13#include "tkTreeCtrl.h" 14#include "tkTreeElem.h" 15#include "tkWinInt.h" 16 17#ifndef _WIN32_IE 18#define _WIN32_IE 0x0501 19#endif 20 21#include <basetyps.h> 22#include <commctrl.h> 23#include <objbase.h> 24#include <shellapi.h> 25 26#ifndef SHGFI_ADDOVERLAYS 27#if (_WIN32_IE >= 0x0500) 28#define SHGFI_ADDOVERLAYS 0x000000020 29#define SHGFI_OVERLAYINDEX 0x000000040 30#endif 31#endif 32 33#ifndef SHIL_LARGE 34#define SHIL_LARGE 0 35#define SHIL_SMALL 1 36#define SHIL_EXTRALARGE 2 37#if 0 38const GUID IID_IImageList = {0x2C247F21, 0x8591, 0x11D1,{ 0xB1, 0x6A, 0x00,0xC0, 0xF0, 0x28,0x36, 0x28} }; 39HRESULT (*SHGetImageListProc)(int iImageList, REFIID riid, void **ppv); 40#endif 41#endif 42 43HIMAGELIST gImgListSmall = NULL; 44HIMAGELIST gImgListLarge = NULL; 45 46TreeCtrlStubs *stubs; 47#define TreeCtrl_RegisterElementType(i,t) \ 48 stubs->TreeCtrl_RegisterElementType(i,t) 49#define Tree_RedrawElement(t,i,e) \ 50 stubs->Tree_RedrawElement(t,i,e) 51#define Tree_ElementIterateBegin(t,et) \ 52 stubs->Tree_ElementIterateBegin(t,et) 53#define Tree_ElementIterateNext(i) \ 54 stubs->Tree_ElementIterateNext(i) 55#define Tree_ElementIterateGet(i) \ 56 stubs->Tree_ElementIterateGet(i) 57#define Tree_ElementIterateChanged(i,m) \ 58 stubs->Tree_ElementIterateChanged(i,m) 59#define PerStateInfo_Free(t,ty,in) \ 60 stubs->PerStateInfo_Free(t,ty,in) 61#define PerStateInfo_FromObj(t,pr,ty,in) \ 62 stubs->PerStateInfo_FromObj(t,pr,ty,in) 63#define PerStateInfo_ForState(t,ty,in,st,ma) \ 64 stubs->PerStateInfo_ForState(t,ty,in,st,ma) 65#define PerStateInfo_ObjForState(t,ty,in,st,ma) \ 66 stubs->PerStateInfo_ObjForState(t,ty,in,st,ma) 67#define PerStateInfo_Undefine(t,ty,in,st) \ 68 stubs->PerStateInfo_Undefine(t,ty,in,st) 69#undef pstBoolean 70#define pstBoolean \ 71 (*stubs->TreeCtrl_pstBoolean) 72#define PerStateBoolean_ForState(t,in,st,ma) \ 73 stubs->PerStateBoolean_ForState(t,in,st,ma) 74#define PSTSave(in,sa) \ 75 stubs->PSTSave(in,sa) 76#define PSTRestore(t,ty,in,sa) \ 77 stubs->PSTRestore(t,ty,in,sa) 78#define TreeStateFromObj \ 79 stubs->TreeStateFromObj 80#define BooleanCO_Init(ot,on) \ 81 stubs->BooleanCO_Init(ot,on) 82#define StringTableCO_Init(ot,on,ta) \ 83 stubs->StringTableCO_Init(ot,on,ta) 84#define PerStateCO_Init(a,b,c,d) \ 85 stubs->PerStateCO_Init(a,b,c,d) 86 87static void AdjustForSticky(int sticky, int cavityWidth, int cavityHeight, 88 int expandX, int expandY, 89 int *xPtr, int *yPtr, int *widthPtr, int *heightPtr) 90{ 91 int dx = 0; 92 int dy = 0; 93 94 if (cavityWidth > *widthPtr) { 95 dx = cavityWidth - *widthPtr; 96 } 97 98 if (cavityHeight > *heightPtr) { 99 dy = cavityHeight - *heightPtr; 100 } 101 102 if ((sticky & STICKY_W) && (sticky & STICKY_E)) { 103 if (expandX) 104 *widthPtr += dx; 105 else 106 sticky &= ~(STICKY_W | STICKY_E); 107 } 108 if ((sticky & STICKY_N) && (sticky & STICKY_S)) { 109 if (expandY) 110 *heightPtr += dy; 111 else 112 sticky &= ~(STICKY_N | STICKY_S); 113 } 114 if (!(sticky & STICKY_W)) { 115 *xPtr += (sticky & STICKY_E) ? dx : dx / 2; 116 } 117 if (!(sticky & STICKY_N)) { 118 *yPtr += (sticky & STICKY_S) ? dy : dy / 2; 119 } 120} 121 122/* This macro gets the value of a per-state option for an element, then 123 * looks for a better match from the master element if it exists */ 124#define OPTION_FOR_STATE(xFUNC,xTYPE,xVAR,xFIELD,xSTATE) \ 125 xVAR = xFUNC(tree, &elemX->xFIELD, xSTATE, &match); \ 126 if ((match != MATCH_EXACT) && (masterX != NULL)) { \ 127 xTYPE varM = xFUNC(tree, &masterX->xFIELD, xSTATE, &match2); \ 128 if (match2 > match) \ 129 xVAR = varM; \ 130 } 131#define BOOLEAN_FOR_STATE(xVAR,xFIELD,xSTATE) \ 132 OPTION_FOR_STATE(PerStateBoolean_ForState,int,xVAR,xFIELD,xSTATE) 133 134/* This macro gets the object for a per-state option for an element, then 135 * looks for a better match from the master element if it exists */ 136#define OBJECT_FOR_STATE(xVAR,xTYPE,xFIELD,xSTATE) \ 137 xVAR = PerStateInfo_ObjForState(tree, &xTYPE, &elemX->xFIELD, xSTATE, &match); \ 138 if ((match != MATCH_EXACT) && (masterX != NULL)) { \ 139 Tcl_Obj *objM = PerStateInfo_ObjForState(tree, &xTYPE, &masterX->xFIELD, xSTATE, &matchM); \ 140 if (matchM > match) \ 141 xVAR = objM; \ 142 } 143 144typedef struct ElementShellIcon ElementShellIcon; 145 146struct ElementShellIcon 147{ 148 TreeElement_ header; 149 PerStateInfo draw; 150 Tcl_Obj *pathObj; /* path of file or directory */ 151 char *path; 152 Tcl_Obj *widthObj; 153 int width; 154 Tcl_Obj *heightObj; 155 int height; 156#define TYPE_DIRECTORY 0 157#define TYPE_FILE 1 158 int type; /* If specified, 'path' is assumed to exist */ 159#define SIZE_LARGE 0 160#define SIZE_SMALL 1 161 int size; /* SIZE_LARGE if unspecified */ 162 HIMAGELIST hImgList; /* the system image list */ 163 int iIcon; /* index into hImgList */ 164 int addOverlays; /* only when useImgList is FALSE */ 165 int useImgList; /* if false, create icons */ 166 HICON hIcon; /* icon */ 167 HICON hIconSel; /* selected icon */ 168}; 169 170#define SHELLICON_CONF_ICON 0x0001 171#define SHELLICON_CONF_SIZE 0x0002 172#define SHELLICON_CONF_DRAW 0x0004 173 174static CONST char *sizeST[] = { 175 "large", "small", (char *) NULL 176}; 177static CONST char *typeST[] = { 178 "directory", "file", (char *) NULL 179}; 180 181static Tk_OptionSpec shellIconOptionSpecs[] = { 182 {TK_OPTION_CUSTOM, "-addoverlays", (char *) NULL, (char *) NULL, 183 (char *) NULL, -1, Tk_Offset(ElementShellIcon, addOverlays), 184 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_ICON}, 185 {TK_OPTION_CUSTOM, "-draw", (char *) NULL, (char *) NULL, 186 (char *) NULL, Tk_Offset(ElementShellIcon, draw.obj), 187 Tk_Offset(ElementShellIcon, draw), 188 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_DRAW}, 189 {TK_OPTION_PIXELS, "-height", (char *) NULL, (char *) NULL, 190 (char *) NULL, Tk_Offset(ElementShellIcon, heightObj), 191 Tk_Offset(ElementShellIcon, height), 192 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_SIZE}, 193 {TK_OPTION_STRING, "-path", (char *) NULL, (char *) NULL, 194 (char *) NULL, Tk_Offset(ElementShellIcon, pathObj), 195 Tk_Offset(ElementShellIcon, path), 196 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_ICON}, 197 {TK_OPTION_CUSTOM, "-size",(char *) NULL, (char *) NULL, 198 (char *) NULL, -1, Tk_Offset(ElementShellIcon, size), 199 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_ICON}, 200 {TK_OPTION_CUSTOM, "-type", (char *) NULL, (char *) NULL, 201 (char *) NULL, -1, Tk_Offset(ElementShellIcon, type), 202 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_ICON}, 203 {TK_OPTION_CUSTOM, "-useimagelist", (char *) NULL, (char *) NULL, 204 (char *) NULL, -1, Tk_Offset(ElementShellIcon, useImgList), 205 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_ICON}, 206 {TK_OPTION_PIXELS, "-width", (char *) NULL, (char *) NULL, 207 (char *) NULL, Tk_Offset(ElementShellIcon, widthObj), 208 Tk_Offset(ElementShellIcon, width), 209 TK_OPTION_NULL_OK, (ClientData) NULL, SHELLICON_CONF_SIZE}, 210 {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL, 211 (char *) NULL, 0, -1, 0, (ClientData) NULL, 0} 212}; 213 214static void LoadIconIfNeeded(TreeElementArgs *args) 215{ 216 TreeCtrl *tree = args->tree; 217 TreeElement elem = args->elem; 218 ElementShellIcon *elemX = (ElementShellIcon *) elem; 219 ElementShellIcon *masterX = (ElementShellIcon *) elem->master; 220 SHFILEINFO sfi; 221 Tcl_DString dString1, dString2; 222 UINT uFlags = SHGFI_SYSICONINDEX; 223 DWORD dwFileAttributes = 0; 224 char *nativePath; 225 int size = SIZE_LARGE; 226 int overlays = 1; 227 int type = -1; 228 int useImgList = TRUE; 229 HRESULT result; 230 231 /* -useimagelist boolean */ 232 if (elemX->useImgList != -1) 233 useImgList = elemX->useImgList; 234 else if (masterX != NULL && masterX->useImgList != -1) 235 useImgList = masterX->useImgList; 236 237 /* Not using the system image list. */ 238 if (!useImgList) { 239 240 /* Already have an icon, or no path is given so no icon used */ 241 if ((elemX->hIcon != NULL) || (elemX->path == NULL)) 242 return; 243 244 /* Equivalent to "file nativename $path" */ 245 nativePath = Tcl_TranslateFileName(tree->interp, elemX->path, &dString1); 246 if (nativePath == NULL) 247 return; 248 249 /* This will be passed to system calls, so convert from UTF8 */ 250 nativePath = Tcl_UtfToExternalDString(NULL, nativePath, -1, &dString2); 251 252 uFlags = SHGFI_ICON; 253 254 /* -addoverlays boolean */ 255 if (elemX->addOverlays != -1) 256 overlays = elemX->addOverlays; 257 else if (masterX != NULL && masterX->addOverlays != -1) 258 overlays = masterX->addOverlays; 259 if (overlays) 260 uFlags |= SHGFI_ADDOVERLAYS; 261 262 /* -size small or large */ 263 if (elemX->size != -1) 264 size = elemX->size; 265 else if (masterX != NULL && masterX->size != -1) 266 size = masterX->size; 267 switch (size) { 268 case SIZE_LARGE: uFlags |= SHGFI_LARGEICON | SHGFI_SHELLICONSIZE; break; 269 case SIZE_SMALL: uFlags |= SHGFI_SMALLICON | SHGFI_SHELLICONSIZE; break; 270 } 271 272 /* -type file or -type directory */ 273 if (elemX->type != -1) 274 type = elemX->type; 275 else if (masterX != NULL && masterX->type != -1) 276 type = masterX->type; 277 /* If SHGFI_USEFILEATTRIBUTES is set, SHGetFileInfo is supposed to 278 * assume that the file is real but not look for it on disk. This 279 * can be used to get the icon for a certain type of file, ex *.exe. 280 * In practice, lots of files get a non-generic icon when a 281 * valid file path is given. */ 282 if (type != -1) { 283 dwFileAttributes = (type == TYPE_FILE) ? 284 FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY; 285 uFlags |= SHGFI_USEFILEATTRIBUTES; 286 } 287 288 /* MSDN says SHGFI_OPENICON returns the image list containing the 289 * small open icon. In practice the large open icon gets returned 290 * for SHGFI_LARGEICON, so we support it. */ 291 if (/*(size == SIZE_SMALL) && */(args->state & STATE_OPEN)) 292 uFlags |= SHGFI_OPENICON; 293 294 CoInitialize(NULL); 295 296 result = SHGetFileInfo( 297 nativePath, 298 dwFileAttributes, 299 &sfi, 300 sizeof(sfi), 301 uFlags); 302 if (result) { 303 elemX->hIcon = sfi.hIcon; 304 305 /* Remember the image list so we can get the icon size */ 306 elemX->hImgList = (size == SIZE_LARGE) ? gImgListLarge : gImgListSmall; 307 } 308 309 result = SHGetFileInfo( 310 nativePath, 311 dwFileAttributes, 312 &sfi, 313 sizeof(sfi), 314 uFlags | SHGFI_SELECTED); 315 if (result) 316 elemX->hIconSel = sfi.hIcon; 317 318 319 CoUninitialize(); 320 321 Tcl_DStringFree(&dString1); 322 Tcl_DStringFree(&dString2); 323 324 return; 325 } 326 327 /* Using the system image list */ 328 if ((elemX->hImgList == NULL) && (elemX->path != NULL)) { 329 330 /* Equivalent to "file nativename $path" */ 331 nativePath = Tcl_TranslateFileName(tree->interp, elemX->path, &dString1); 332 if (nativePath == NULL) 333 return; 334 335 /* This will be passed to system calls, so convert from UTF8 */ 336 nativePath = Tcl_UtfToExternalDString(NULL, nativePath, -1, &dString2); 337 338 /* -size small or large */ 339 if (elemX->size != -1) 340 size = elemX->size; 341 else if (masterX != NULL && masterX->size != -1) 342 size = masterX->size; 343 344 switch (size) { 345 case SIZE_SMALL: uFlags |= SHGFI_SMALLICON | SHGFI_SHELLICONSIZE; break; 346 case SIZE_LARGE: uFlags |= SHGFI_LARGEICON | SHGFI_SHELLICONSIZE; break; 347 } 348 349 /* -type file or -type directory */ 350 if (elemX->type != -1) 351 type = elemX->type; 352 else if (masterX != NULL && masterX->type != -1) 353 type = masterX->type; 354 /* If SHGFI_USEFILEATTRIBUTES is set, SHGetFileInfo is supposed to 355 * assume that the file is real but not look for it on disk. This 356 * can be used to get the icon for a certain type of file, ex *.exe. 357 * In practice, lots of files get a non-generic icon when a 358 * valid file path is given. */ 359 if (type != -1) { 360 dwFileAttributes = (type == TYPE_FILE) ? 361 FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY; 362 uFlags |= SHGFI_USEFILEATTRIBUTES; 363 } 364 365 /* MSDN says SHGFI_OPENICON returns the image list containing the 366 * small open icon. In practice the large open icon gets returned 367 * for SHGFI_LARGEICON. */ 368 if (/*(size == SIZE_SMALL) && */(args->state & STATE_OPEN)) 369 uFlags |= SHGFI_OPENICON; 370 371 CoInitialize(NULL); 372 373 elemX->hImgList = (HIMAGELIST) SHGetFileInfo( 374 nativePath, 375 dwFileAttributes, 376 &sfi, 377 sizeof(sfi), 378 uFlags); 379 if (elemX->hImgList != NULL) { 380 elemX->iIcon = sfi.iIcon; 381 } 382 383 CoUninitialize(); 384 385 Tcl_DStringFree(&dString1); 386 Tcl_DStringFree(&dString2); 387 } 388} 389 390static void ForgetIcon(ElementShellIcon *elemX) 391{ 392 if (elemX->hIcon != NULL) 393 DestroyIcon(elemX->hIcon); 394 if (elemX->hIconSel != NULL) 395 DestroyIcon(elemX->hIconSel); 396 elemX->hImgList = NULL; 397 elemX->hIcon = elemX->hIconSel = NULL; 398} 399 400static void DeleteProcShellIcon(TreeElementArgs *args) 401{ 402 TreeElement elem = args->elem; 403 ElementShellIcon *elemX = (ElementShellIcon *) elem; 404 405 ForgetIcon(elemX); 406} 407 408static int WorldChangedProcShellIcon(TreeElementArgs *args) 409{ 410 ElementShellIcon *elemX = (ElementShellIcon *) args->elem; 411 int flagM = args->change.flagMaster; 412 int flagS = args->change.flagSelf; 413 int mask = 0; 414 415 if ((flagS | flagM) & (SHELLICON_CONF_ICON | SHELLICON_CONF_SIZE)) 416 mask |= CS_DISPLAY | CS_LAYOUT; 417 if ((flagS | flagM) & (SHELLICON_CONF_DRAW)) 418 mask |= CS_DISPLAY; 419 420 if ((flagS | flagM) & SHELLICON_CONF_ICON) { 421 ForgetIcon(elemX); 422 } 423 424 return mask; 425} 426 427static int ConfigProcShellIcon(TreeElementArgs *args) 428{ 429 TreeCtrl *tree = args->tree; 430 TreeElement elem = args->elem; 431 ElementShellIcon *elemX = (ElementShellIcon *) elem; 432 Tk_SavedOptions savedOptions; 433 int error; 434 Tcl_Obj *errorResult = NULL; 435 436 for (error = 0; error <= 1; error++) { 437 if (error == 0) { 438 if (Tk_SetOptions(tree->interp, (char *) elemX, 439 elem->typePtr->optionTable, 440 args->config.objc, args->config.objv, tree->tkwin, 441 &savedOptions, &args->config.flagSelf) != TCL_OK) { 442 args->config.flagSelf = 0; 443 continue; 444 } 445 446 /* xxx */ 447 448 Tk_FreeSavedOptions(&savedOptions); 449 break; 450 } else { 451 errorResult = Tcl_GetObjResult(tree->interp); 452 Tcl_IncrRefCount(errorResult); 453 Tk_RestoreSavedOptions(&savedOptions); 454 455 /* xxx */ 456 457 Tcl_SetObjResult(tree->interp, errorResult); 458 Tcl_DecrRefCount(errorResult); 459 return TCL_ERROR; 460 } 461 } 462 463 return TCL_OK; 464} 465 466static int CreateProcShellIcon(TreeElementArgs *args) 467{ 468 ElementShellIcon *elemX = (ElementShellIcon *) args->elem; 469 470 elemX->type = -1; 471 elemX->size = -1; 472 elemX->addOverlays = -1; 473 elemX->useImgList = -1; 474 475 return TCL_OK; 476} 477 478static void DisplayProcShellIcon(TreeElementArgs *args) 479{ 480 TreeCtrl *tree = args->tree; 481 TreeElement elem = args->elem; 482 ElementShellIcon *elemX = (ElementShellIcon *) elem; 483 ElementShellIcon *masterX = (ElementShellIcon *) elem->master; 484 int state = args->state; 485 int x = args->display.x, y = args->display.y; 486 int width, height; 487 int match, match2; 488 int draw; 489 HDC hDC; 490 TkWinDCState dcState; 491 UINT fStyle = ILD_TRANSPARENT; 492 HICON hIcon; 493 494 BOOLEAN_FOR_STATE(draw, draw, state) 495 if (!draw) 496 return; 497 498 LoadIconIfNeeded(args); 499 500 if (elemX->hImgList == NULL) 501 return; 502 503 (void) ImageList_GetIconSize(elemX->hImgList, &width, &height); 504 AdjustForSticky(args->display.sticky, 505 args->display.width, args->display.height, 506 FALSE, FALSE, 507 &x, &y, &width, &height); 508 509 hDC = TkWinGetDrawableDC(tree->display, args->display.drawable, &dcState); 510 hIcon = (state & STATE_SELECTED) ? elemX->hIconSel : elemX->hIcon; 511 if (hIcon != NULL) { 512#if 1 513 /* If DI_DEFAULTSIZE is used, small icons get stretched */ 514 DrawIconEx(hDC, x, y, hIcon, 0, 0, FALSE, NULL, DI_IMAGE | DI_MASK /*| DI_DEFAULTSIZE*/); 515#else 516 /* Works fine for large, but not for small (they get stretched) */ 517 DrawIcon(hDC, x, y, hIcon); 518#endif 519 } else { 520 if (state & STATE_SELECTED) 521 fStyle |= ILD_SELECTED; 522 ImageList_Draw(elemX->hImgList, elemX->iIcon, hDC, x, y, fStyle); 523 } 524 TkWinReleaseDrawableDC(args->display.drawable, hDC, &dcState); 525 526 /* If this is a master element, forget the icon being used because the 527 * appearance may change between items based on the open state */ 528 if (masterX == NULL) { 529 ForgetIcon(elemX); 530 } 531} 532 533static void NeededProcShellIcon(TreeElementArgs *args) 534{ 535 TreeElement elem = args->elem; 536 ElementShellIcon *elemX = (ElementShellIcon *) elem; 537 ElementShellIcon *masterX = (ElementShellIcon *) elem->master; 538 int width = 0, height = 0; 539 int size = SIZE_LARGE; 540 HIMAGELIST hImgList = NULL; 541 542 /* The icon is not loaded until it is actually going to be displayed. 543 * This is a good thing since loading the icons can take a while */ 544/* LoadIconIfNeeded(args);*/ 545 546 if (elemX->hImgList != NULL) { 547 hImgList = elemX->hImgList; 548 549 } else if (elemX->path != NULL) { 550 if (elemX->size != -1) 551 size = elemX->size; 552 else if (masterX != NULL && masterX->size != -1) 553 size = masterX->size; 554 switch (size) { 555 case SIZE_SMALL: hImgList = gImgListSmall; break; 556 case SIZE_LARGE: hImgList = gImgListLarge; break; 557 } 558 } 559 560 if (hImgList != NULL) { 561 (void) ImageList_GetIconSize(hImgList, &width, &height); 562 } 563 564 if (elemX->widthObj != NULL) 565 width = elemX->width; 566 else if ((masterX != NULL) && (masterX->widthObj != NULL)) 567 width = masterX->width; 568 569 if (elemX->heightObj != NULL) 570 height = elemX->height; 571 else if ((masterX != NULL) && (masterX->heightObj != NULL)) 572 height = masterX->height; 573 574 args->needed.width = width; 575 args->needed.height = height; 576} 577 578static int StateProcShellIcon(TreeElementArgs *args) 579{ 580 TreeCtrl *tree = args->tree; 581 TreeElement elem = args->elem; 582 ElementShellIcon *elemX = (ElementShellIcon *) elem; 583 ElementShellIcon *masterX = (ElementShellIcon *) elem->master; 584 int match, match2; 585 int draw1, draw2; 586 int sel1, sel2; 587 int open1, open2; 588 int mask = 0; 589 590 BOOLEAN_FOR_STATE(draw1, draw, args->states.state1) 591 if (draw1 == -1) 592 draw1 = 1; 593 open1 = (args->states.state1 & STATE_OPEN) != 0; 594 sel1 = (args->states.state1 & STATE_SELECTED) != 0; 595 596 BOOLEAN_FOR_STATE(draw2, draw, args->states.state2) 597 if (draw2 == -1) 598 draw2 = 1; 599 open2 = (args->states.state2 & STATE_OPEN) != 0; 600 sel2 = (args->states.state2 & STATE_SELECTED) != 0; 601 602 if (elemX->path == NULL) 603 open1 = open2 = sel1 = sel2 = 0; 604 605 if ((draw1 != draw2) || (sel1 != sel2) || (open1 != open2)) 606 mask |= CS_DISPLAY; 607 608 /* Directories may have an open and closed icon. */ 609 if (open1 != open2) { 610 ForgetIcon(elemX); 611 } 612 613 return mask; 614} 615 616static int UndefProcShellIcon(TreeElementArgs *args) 617{ 618 TreeCtrl *tree = args->tree; 619 ElementShellIcon *elemX = (ElementShellIcon *) args->elem; 620 int modified = 0; 621 622 modified |= PerStateInfo_Undefine(tree, &pstBoolean, &elemX->draw, args->state); 623 return modified; 624} 625 626static int ActualProcShellIcon(TreeElementArgs *args) 627{ 628 TreeCtrl *tree = args->tree; 629 ElementShellIcon *elemX = (ElementShellIcon *) args->elem; 630 ElementShellIcon *masterX = (ElementShellIcon *) args->elem->master; 631 static CONST char *optionName[] = { 632 "-draw", 633 (char *) NULL }; 634 int index, match, matchM; 635 Tcl_Obj *obj = NULL; 636 637 if (Tcl_GetIndexFromObj(tree->interp, args->actual.obj, optionName, 638 "option", 0, &index) != TCL_OK) 639 return TCL_ERROR; 640 641 switch (index) { 642 case 0: 643 { 644 OBJECT_FOR_STATE(obj, pstBoolean, draw, args->state) 645 break; 646 } 647 } 648 if (obj != NULL) 649 Tcl_SetObjResult(tree->interp, obj); 650 return TCL_OK; 651} 652 653TreeElementType elemTypeShellIcon = { 654 "shellicon", 655 sizeof(ElementShellIcon), 656 shellIconOptionSpecs, 657 NULL, 658 CreateProcShellIcon, 659 DeleteProcShellIcon, 660 ConfigProcShellIcon, 661 DisplayProcShellIcon, 662 NeededProcShellIcon, 663 NULL, /* heightProc */ 664 WorldChangedProcShellIcon, 665 StateProcShellIcon, 666 UndefProcShellIcon, 667 ActualProcShellIcon, 668 NULL /* onScreenProc */ 669}; 670 671DLLEXPORT int Shellicon_Init(Tcl_Interp *interp) 672{ 673#if 1 674 SHFILEINFO sfi; 675#else 676 HMODULE hLib; 677#endif 678 679#ifdef USE_TCL_STUBS 680 if (Tcl_InitStubs(interp, "8.4", 0) == NULL) { 681 return TCL_ERROR; 682 } 683#endif 684#ifdef USE_TK_STUBS 685 if (Tk_InitStubs(interp, "8.4", 0) == NULL) { 686 return TCL_ERROR; 687 } 688#endif 689 690 /* InitCommonControlsEx must be called to use the ImageList functions */ 691 /* This is already done by Tk on NT */ 692 if (TkWinGetPlatformId() != VER_PLATFORM_WIN32_NT) { 693 INITCOMMONCONTROLSEX comctl; 694 ZeroMemory(&comctl, sizeof(comctl)); 695 (void) InitCommonControlsEx(&comctl); 696 } 697 698#if 1 699 /* Get the sytem image lists (small and large) */ 700 CoInitialize(NULL); 701 gImgListSmall = (HIMAGELIST) SHGetFileInfo(".exe", FILE_ATTRIBUTE_NORMAL, &sfi, 702 sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_SMALLICON); 703 gImgListLarge = (HIMAGELIST) SHGetFileInfo(".exe", FILE_ATTRIBUTE_NORMAL, &sfi, 704 sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_LARGEICON); 705 CoUninitialize(); 706#else 707 /* FIXME: WinXP only */ 708 /* This is broken somewhere */ 709 hLib = LoadLibraryA("shell32"); 710 SHGetImageListProc = (FARPROC) GetProcAddress(hLib, (LPCSTR) 727); 711 SHGetImageListProc(SHIL_SMALL, &IID_IImageList, &gImgListSmall); 712 SHGetImageListProc(SHIL_LARGE, &IID_IImageList, &gImgListLarge); 713 SHGetImageListProc(SHIL_EXTRALARGE, &IID_IImageList, &gImgListXtraLarge); 714 dbwin("small %p large %p xtralarge %p\n", gImgListSmall, gImgListLarge, gImgListXtraLarge); 715 FreeLibrary(hLib); 716#endif 717 718 /* Load TkTreeCtrl */ 719 if (Tcl_PkgRequire(interp, "treectrl", PACKAGE_PATCHLEVEL, TRUE) == NULL) 720 return TCL_ERROR; 721 722 /* Get the stubs table from TkTreeCtrl */ 723 stubs = Tcl_GetAssocData(interp, "TreeCtrlStubs", NULL); 724 if (stubs == NULL) 725 return TCL_ERROR; 726 727 /* Initialize the options table */ 728 BooleanCO_Init(shellIconOptionSpecs, "-addoverlays"); 729 BooleanCO_Init(shellIconOptionSpecs, "-useimagelist"); 730 PerStateCO_Init(shellIconOptionSpecs, "-draw", &pstBoolean, 731 TreeStateFromObj); 732 StringTableCO_Init(shellIconOptionSpecs, "-size", sizeST); 733 StringTableCO_Init(shellIconOptionSpecs, "-type", typeST); 734 735 /* Add the "shellicon" element type */ 736 if (TreeCtrl_RegisterElementType(interp, &elemTypeShellIcon) != TCL_OK) 737 return TCL_ERROR; 738 739 if (Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_PATCHLEVEL) != TCL_OK) { 740 return TCL_ERROR; 741 } 742 743 return TCL_OK; 744} 745 746DLLEXPORT int Shellicon_SafeInit(Tcl_Interp *interp) 747{ 748 return Shellicon_Init(interp); 749} 750 751