Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1182 lines
40 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "defview.h"
  4. #include "defviewp.h"
  5. #include "dvtasks.h"
  6. #include "guids.h"
  7. #include "prop.h"
  8. #include "CommonControls.h"
  9. #include "thumbutil.h"
  10. // Thumbnail support
  11. HRESULT CDefView::_SafeAddImage(BOOL fQuick, IMAGECACHEINFO* prgInfo, UINT* piImageIndex, int iListID)
  12. {
  13. HRESULT hr = S_FALSE;
  14. UINT uCacheSize = 0;
  15. _pImageCache->GetCacheSize(&uCacheSize);
  16. ASSERT(_iMaxCacheSize>0);
  17. BOOL bSpaceOpen = (uCacheSize < (UINT)_iMaxCacheSize);
  18. if (!bSpaceOpen)
  19. {
  20. BOOL bMakeSpace = TRUE;
  21. int iListIndex = -1;
  22. // Check to see if we are visible and need to make space
  23. if (-1 != iListID)
  24. {
  25. iListIndex = _MapIDToIndex(iListID);
  26. if (-1 == iListIndex) // Someone removed our item
  27. {
  28. hr = E_INVALIDARG;
  29. bMakeSpace = FALSE;
  30. }
  31. else if (!ListView_IsItemVisible(_hwndListview, iListIndex))
  32. {
  33. hr = S_FALSE;
  34. bMakeSpace = FALSE;
  35. }
  36. }
  37. if (bMakeSpace)
  38. {
  39. // item is visible... try and make a space
  40. UINT uCacheIndex = 0;
  41. do
  42. {
  43. UINT uImageIndex;
  44. int iUsage;
  45. if (FAILED(_pImageCache->GetImageIndexFromCacheIndex(uCacheIndex, &uImageIndex)) ||
  46. FAILED(_pImageCache->GetUsage(uImageIndex, (UINT*) &iUsage)))
  47. {
  48. break;
  49. }
  50. if (iUsage != ICD_USAGE_SYSTEM) // Magic number for System Image
  51. {
  52. TraceMsg(TF_DEFVIEW, "CDefView::_SafeAddImage -- FreeImage (CI::%d II::%d)", uCacheIndex, uImageIndex);
  53. _pImageCache->FreeImage(uImageIndex);
  54. _UpdateImage(uImageIndex);
  55. bSpaceOpen = TRUE;
  56. ASSERT((LONG)(uCacheSize - uCacheIndex) > (LONG)_ApproxItemsPerView());
  57. }
  58. uCacheIndex++;
  59. }
  60. while (!bSpaceOpen);
  61. // If we repeatedly fail to add images to the list and are still decoding more images this means
  62. // we will have to re-walk the list view every time we finish decoding another image, only to then
  63. // throw away the result because we have no where to save it. This could lead to sluggish response
  64. // from the UI. In short, if the following Trace is common then we have a problem that needs to be
  65. // fixed (which might required considerable rearchitecting).
  66. if (!bSpaceOpen)
  67. {
  68. TraceMsg(TF_WARNING, "CDefView::_SafeAddImage failed to make room in cache!!");
  69. hr = E_FAIL;
  70. }
  71. }
  72. }
  73. *piImageIndex = I_IMAGECALLBACK;
  74. if (bSpaceOpen) // There is space in the cache for this image
  75. {
  76. hr = _pImageCache->AddImage(prgInfo, piImageIndex);
  77. TraceMsg(TF_DEFVIEW, "CDefView::_SafeAddImage -- AddImage (HR:0x%08x name:%s,index:%u)", hr, prgInfo->pszName, *piImageIndex);
  78. }
  79. return hr;
  80. }
  81. COLORREF CDefView::_GetBackColor()
  82. {
  83. // SendMessage traffic is greatly reduced if we don't ask for the bkcolor
  84. // every time we need it...
  85. if (_rgbBackColor == CLR_INVALID)
  86. {
  87. _rgbBackColor = ListView_GetBkColor(_hwndListview);
  88. if (_rgbBackColor == CLR_NONE)
  89. _rgbBackColor = GetSysColor(COLOR_WINDOW);
  90. }
  91. return _rgbBackColor;
  92. }
  93. HRESULT CDefView::TaskUpdateItem(LPCITEMIDLIST pidl, int iItem, DWORD dwMask, LPCWSTR pszPath,
  94. FILETIME ftDateStamp, int iThumbnail, HBITMAP hBmp, DWORD dwItemID)
  95. {
  96. // check the size of the bitmap to make sure it is big enough, if it is not, then
  97. // we must center it on a background...
  98. BITMAP rgBitmap;
  99. HBITMAP hBmpCleanup = NULL;
  100. HRESULT hr = E_FAIL;
  101. if (::GetObject((HGDIOBJ)hBmp, sizeof(rgBitmap), &rgBitmap))
  102. {
  103. // if the image is the wrong size, or the wrong colour depth, then do the funky stuff on it..
  104. SIZE sizeThumbnail;
  105. _GetThumbnailSize(&sizeThumbnail);
  106. if (rgBitmap.bmWidth != sizeThumbnail.cx ||
  107. rgBitmap.bmHeight != sizeThumbnail.cy ||
  108. rgBitmap.bmBitsPixel > _dwRecClrDepth)
  109. {
  110. // alloc the colour table just incase....
  111. BITMAPINFO *pInfo = (BITMAPINFO *)LocalAlloc(LPTR, sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256);
  112. if (pInfo)
  113. {
  114. // get a DC for this operation...
  115. HDC hdcMem = CreateCompatibleDC(NULL);
  116. if (hdcMem)
  117. {
  118. pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  119. if (GetDIBits(hdcMem, hBmp, 0, 0, NULL, pInfo, DIB_RGB_COLORS))
  120. {
  121. // we have the header, now get the data....
  122. void *pBits = LocalAlloc(LPTR, pInfo->bmiHeader.biSizeImage);
  123. if (pBits)
  124. {
  125. if (GetDIBits(hdcMem, hBmp, 0, pInfo->bmiHeader.biHeight, pBits, pInfo, DIB_RGB_COLORS))
  126. {
  127. RECT rgRect = {0, 0, rgBitmap.bmWidth, rgBitmap.bmHeight};
  128. CalculateAspectRatio(&sizeThumbnail, &rgRect);
  129. HPALETTE hpal = NULL;
  130. HRESULT hrPalette = _dwRecClrDepth <= 8 ? _GetBrowserPalette(&hpal) : S_OK;
  131. if (SUCCEEDED(hrPalette))
  132. {
  133. if (FactorAspectRatio(pInfo, pBits, &sizeThumbnail, rgRect, _dwRecClrDepth, hpal, FALSE, _GetBackColor(), &hBmpCleanup))
  134. {
  135. // finally success :-) we have the new image we can abandon the old one...
  136. hBmp = hBmpCleanup;
  137. hr = S_OK;
  138. }
  139. }
  140. }
  141. LocalFree(pBits);
  142. }
  143. }
  144. DeleteDC(hdcMem);
  145. }
  146. LocalFree(pInfo);
  147. }
  148. }
  149. else
  150. {
  151. // the original bitmap is fine
  152. hr = S_OK;
  153. }
  154. }
  155. UINT iImage;
  156. if (SUCCEEDED(hr))
  157. {
  158. // check if we are going away, if so, then don't use Sendmessage because it will block the
  159. // destructor of the scheduler...
  160. if (_fDestroying)
  161. {
  162. hr = E_FAIL;
  163. }
  164. else
  165. {
  166. // copy thumbnail into the cache.
  167. IMAGECACHEINFO rgInfo = {0};
  168. rgInfo.cbSize = sizeof(rgInfo);
  169. rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX | ICIFLAG_LARGE | ICIFLAG_BITMAP;
  170. rgInfo.pszName = pszPath;
  171. rgInfo.dwFlags = dwMask;
  172. rgInfo.iIndex = (int) iThumbnail;
  173. rgInfo.hBitmapLarge = hBmp;
  174. rgInfo.ftDateStamp = ftDateStamp;
  175. if (!IsNullTime(&ftDateStamp))
  176. rgInfo.dwMask |= ICIFLAG_DATESTAMP;
  177. if (IS_WINDOW_RTL_MIRRORED(_hwndListview))
  178. rgInfo.dwMask |= ICIFLAG_MIRROR;
  179. hr = _SafeAddImage(FALSE, &rgInfo, &iImage, (int) dwItemID);
  180. }
  181. }
  182. if (hBmpCleanup)
  183. {
  184. DeleteObject(hBmpCleanup);
  185. }
  186. #ifdef USEMASK
  187. DeleteObject(hbmMask);
  188. #endif
  189. if (SUCCEEDED(hr))
  190. {
  191. LPITEMIDLIST pidlToSend = ILClone(pidl);
  192. if (pidlToSend)
  193. {
  194. DSV_UPDATETHUMBNAIL* putn = (DSV_UPDATETHUMBNAIL*)LocalAlloc(LPTR, sizeof(DSV_UPDATETHUMBNAIL));
  195. if (putn)
  196. {
  197. putn->iImage = (hr == S_OK) ? iImage : I_IMAGECALLBACK;
  198. putn->iItem = iItem;
  199. putn->pidl = pidlToSend;
  200. // post to the main thread so we don't deadlock
  201. if (!::PostMessage(_hwndView, WM_DSV_UPDATETHUMBNAIL, 0, (LPARAM)putn))
  202. _CleanupUpdateThumbnail(putn);
  203. }
  204. else
  205. {
  206. ILFree(pidlToSend);
  207. }
  208. }
  209. }
  210. return hr;
  211. }
  212. int CDefView::_IncrementWriteTaskCount()
  213. {
  214. return InterlockedIncrement((PLONG)&_iWriteTaskCount);
  215. }
  216. int CDefView::_DecrementWriteTaskCount()
  217. {
  218. return InterlockedDecrement((PLONG)&_iWriteTaskCount);
  219. }
  220. HRESULT CDefView::UpdateImageForItem(DWORD dwTaskID, HBITMAP hImage, int iItem, LPCITEMIDLIST pidl,
  221. LPCWSTR pszPath, FILETIME ftDateStamp, BOOL fCache, DWORD dwPriority)
  222. {
  223. HRESULT hr = S_OK;
  224. TaskUpdateItem(pidl, iItem, _GetOverlayMask(pidl), pszPath, ftDateStamp, 0, hImage, dwTaskID);
  225. if (_pDiskCache && fCache && (_iWriteTaskCount < MAX_WRITECACHE_TASKS))
  226. {
  227. // REVIEW: if pidl is an encrypted file but isn't in an encrytped folder, should avoid writing it's thumbnail?
  228. // If we don't, other users could otherwise view the thumbnail and thus know the contents of the encrypted file.
  229. // Add a cache write test
  230. IRunnableTask *pTask;
  231. if (SUCCEEDED(CWriteCacheTask_Create(dwTaskID, this, pszPath, ftDateStamp, hImage, &pTask)))
  232. {
  233. _AddTask(pTask, TOID_WriteCacheHandler, dwTaskID, dwPriority - PRIORITY_DELTA_WRITE, ADDTASK_ONLYONCE | ADDTASK_ATEND);
  234. pTask->Release();
  235. hr = S_FALSE;
  236. }
  237. }
  238. return hr;
  239. }
  240. DWORD CDefView::_GetOverlayMask(LPCITEMIDLIST pidl)
  241. {
  242. DWORD dwLink = SFGAO_GHOSTED; // SFGAO_LINK | SFGAO_SHARE
  243. _pshf->GetAttributesOf(1, &pidl, &dwLink);
  244. return dwLink;
  245. }
  246. void CDefView::_UpdateThumbnail(int iItem, int iImage, LPCITEMIDLIST pidl)
  247. {
  248. if (!_IsOwnerData())
  249. {
  250. if (_hwndListview)
  251. {
  252. int iFoundItem = _FindItemHint(pidl, iItem);
  253. if (-1 != iFoundItem)
  254. {
  255. LV_ITEM rgItem = {0};
  256. rgItem.mask = LVIF_IMAGE;
  257. rgItem.iItem = iFoundItem;
  258. rgItem.iImage = iImage;
  259. // We are about to change the given item for purely internal reasons, we should not treat
  260. // this change as a "real change". As such we set a flag so that we ignore the LVN_ITEMCHANGED
  261. // notification that is generated by this LVM_SETITEM message. If we don't ingore this
  262. // next message then we would fire another DISPID_SELECTIONCHANGED every time we finish
  263. // extracting an image (if the image is selected).
  264. _fIgnoreItemChanged = TRUE;
  265. ListView_SetItem(_hwndListview, &rgItem);
  266. _fIgnoreItemChanged = FALSE;
  267. }
  268. }
  269. }
  270. else
  271. {
  272. RECT rc;
  273. ListView_GetItemRect(_hwndListview, iItem, &rc, LVIR_BOUNDS);
  274. InvalidateRect(_hwndListview, &rc, FALSE);
  275. }
  276. }
  277. void CDefView::_CleanupUpdateThumbnail(DSV_UPDATETHUMBNAIL* putn)
  278. {
  279. ILFree(putn->pidl);
  280. LocalFree((HLOCAL)putn);
  281. }
  282. int CDefView::ViewGetIconIndex(LPCITEMIDLIST pidl)
  283. {
  284. int iIndex = -1;
  285. if (_psi)
  286. {
  287. // check to see if we succeeded and we weren't told to extract the icon
  288. // ourselves ...
  289. if ((S_OK == _psi->GetIconOf(pidl, 0, &iIndex)) && _psio)
  290. {
  291. int iOverlay;
  292. if (SUCCEEDED(_psio->GetOverlayIndex(pidl, &iOverlay)))
  293. {
  294. iIndex |= iOverlay << 24;
  295. }
  296. }
  297. }
  298. if (-1 == iIndex)
  299. {
  300. iIndex = SHMapPIDLToSystemImageListIndex(_pshf, pidl, NULL);
  301. }
  302. return (iIndex >= 0) ? iIndex : II_DOCNOASSOC;
  303. }
  304. HRESULT CDefView::CreateDefaultThumbnail(int iIndex, HBITMAP *phBmpThumbnail, BOOL fCorner)
  305. {
  306. HRESULT hr = E_FAIL;
  307. // get the background for the default thumbnail.
  308. HDC hdc = GetDC(NULL);
  309. HDC hMemDC = CreateCompatibleDC(hdc);
  310. if (hMemDC)
  311. {
  312. SIZE sizeThumbnail;
  313. _GetThumbnailSize(&sizeThumbnail);
  314. *phBmpThumbnail = CreateCompatibleBitmap(hdc, sizeThumbnail.cx, sizeThumbnail.cy);
  315. if (*phBmpThumbnail)
  316. {
  317. HGDIOBJ hTmp = SelectObject(hMemDC, *phBmpThumbnail);
  318. RECT rc = {0, 0, sizeThumbnail.cx, sizeThumbnail.cy};
  319. SHFillRectClr(hMemDC, &rc, _GetBackColor());
  320. IImageList* piml;
  321. if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml))))
  322. {
  323. int cxIcon, cyIcon, x, y, dx, dy;
  324. // calculate position and width of icon.
  325. piml->GetIconSize(&cxIcon, &cyIcon);
  326. if (cxIcon < sizeThumbnail.cx)
  327. {
  328. if (fCorner)
  329. {
  330. x = 0;
  331. }
  332. else
  333. {
  334. x = (sizeThumbnail.cx - cxIcon) / 2;
  335. }
  336. dx = cxIcon;
  337. }
  338. else
  339. {
  340. // in case icon size is larger than thumbnail size.
  341. x = 0;
  342. dx = sizeThumbnail.cx;
  343. }
  344. if (cyIcon < sizeThumbnail.cy)
  345. {
  346. if (fCorner)
  347. {
  348. y = sizeThumbnail.cy - cyIcon;
  349. }
  350. else
  351. {
  352. y = (sizeThumbnail.cy - cyIcon) / 2;
  353. }
  354. dy = cyIcon;
  355. }
  356. else
  357. {
  358. // in case icon size is larger than thumbnail size.
  359. y = 0;
  360. dy = sizeThumbnail.cy;
  361. }
  362. IMAGELISTDRAWPARAMS idp = {sizeof(idp)};
  363. idp.i = (iIndex & 0x00ffffff);
  364. idp.hdcDst = hMemDC;
  365. idp.x = x;
  366. idp.y = y;
  367. idp.cx = dx;
  368. idp.cy = dy;
  369. idp.rgbBk = CLR_DEFAULT;
  370. idp.rgbFg = CLR_DEFAULT;
  371. idp.fStyle = ILD_TRANSPARENT;
  372. piml->Draw(&idp);
  373. piml->Release();
  374. }
  375. // get the bitmap produced so that it will be returned.
  376. *phBmpThumbnail = (HBITMAP) SelectObject(hMemDC, hTmp);
  377. hr = S_OK;
  378. }
  379. }
  380. if (hMemDC)
  381. DeleteDC(hMemDC);
  382. ReleaseDC(NULL, hdc);
  383. return hr;
  384. }
  385. void CDefView::_CacheDefaultThumbnail(LPCITEMIDLIST pidl, int* piIcon)
  386. {
  387. // create the default one for that file type,
  388. // the index into the sys image list is used to detect items of the
  389. // same type, thus we only generate one default thumbnail for each
  390. // particular icon needed
  391. UINT iIndex = (UINT) ViewGetIconIndex(pidl);
  392. if (iIndex == (UINT) I_IMAGECALLBACK)
  393. {
  394. iIndex = II_DOCNOASSOC;
  395. }
  396. if (_pImageCache)
  397. {
  398. // check if the image is already in the image cache.
  399. IMAGECACHEINFO rgInfo;
  400. rgInfo.cbSize = sizeof(rgInfo);
  401. rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX;
  402. rgInfo.pszName = L"Default";
  403. rgInfo.dwFlags = _GetOverlayMask(pidl);
  404. rgInfo.iIndex = (int) iIndex;
  405. HRESULT hr = _pImageCache->FindImage(&rgInfo, (UINT*)piIcon);
  406. if (hr != S_OK)
  407. {
  408. HBITMAP hBmpThumb = NULL;
  409. hr = CreateDefaultThumbnail(iIndex, &hBmpThumb, FALSE);
  410. if (SUCCEEDED(hr))
  411. {
  412. // we are creating a new one, so we shouldn't have an index yet ..
  413. Assert(*piIcon == I_IMAGECALLBACK);
  414. // copy thumbnail into the imagelist.
  415. rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX | ICIFLAG_LARGE | ICIFLAG_BITMAP;
  416. rgInfo.hBitmapLarge = hBmpThumb;
  417. rgInfo.hMaskLarge = NULL;
  418. if (IS_WINDOW_RTL_MIRRORED(_hwndListview))
  419. rgInfo.dwMask |= ICIFLAG_MIRROR;
  420. hr = _SafeAddImage(TRUE, &rgInfo, (UINT*)piIcon, -1);
  421. DeleteObject(hBmpThumb);
  422. }
  423. else
  424. {
  425. *piIcon = (UINT) I_IMAGECALLBACK;
  426. }
  427. }
  428. }
  429. else
  430. {
  431. *piIcon = II_DOCNOASSOC;
  432. }
  433. }
  434. //
  435. // Creates an thumbnail overlay based on the system index
  436. //
  437. HRESULT CDefView::_CreateOverlayThumbnail(int iIndex, HBITMAP* phbmOverlay, HBITMAP* phbmMask)
  438. {
  439. HRESULT hr = CreateDefaultThumbnail(iIndex, phbmOverlay, TRUE);
  440. if (SUCCEEDED(hr))
  441. {
  442. HDC hdc = GetDC(NULL);
  443. BITMAP bm;
  444. hr = E_FAIL;
  445. if (::GetObject(*phbmOverlay, sizeof(bm), &bm) == sizeof(bm))
  446. {
  447. HDC hdcImg = ::CreateCompatibleDC(hdc);
  448. HDC hdcMask = ::CreateCompatibleDC(hdc);
  449. if (hdcImg && hdcMask)
  450. {
  451. *phbmMask = ::CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL);
  452. if (*phbmMask)
  453. {
  454. HBITMAP hbmpOldImg = (HBITMAP) ::SelectObject(hdcImg, *phbmOverlay);
  455. HBITMAP hbmpOldMsk = (HBITMAP) ::SelectObject(hdcMask, *phbmMask);
  456. COLORREF clrTransparent = ::GetPixel(hdcImg, 0, 0);
  457. ::SetBkColor(hdcImg, clrTransparent);
  458. ::BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcImg, 0, 0, SRCCOPY);
  459. ::SelectObject(hdcImg, hbmpOldImg);
  460. ::SelectObject(hdcMask, hbmpOldMsk);
  461. hr = S_OK;
  462. }
  463. }
  464. if (hdcImg)
  465. {
  466. DeleteDC(hdcImg);
  467. }
  468. if (hdcMask)
  469. {
  470. DeleteDC(hdcMask);
  471. }
  472. }
  473. ReleaseDC(NULL, hdc);
  474. }
  475. return hr;
  476. }
  477. void CDefView::_DoThumbnailReadAhead()
  478. {
  479. // Start up the ReadAheadHandler if:
  480. // 1) view requires thumbnails
  481. // 2) we have items in view (handle delayed enum)
  482. // 3) we haven't kicked it off already
  483. // 4) If we're not ownerdata
  484. if (_IsImageMode())
  485. {
  486. UINT cItems = ListView_GetItemCount(_hwndListview);
  487. if (cItems && !_fReadAhead && !_IsOwnerData())
  488. {
  489. // Start the read-ahead task
  490. _fReadAhead = TRUE;
  491. IRunnableTask *pTask;
  492. if (SUCCEEDED(CReadAheadTask_Create(this, &pTask)))
  493. {
  494. // add with a low prority, but higher than HTML extraction...
  495. _AddTask(pTask, TOID_ReadAheadHandler, 0, PRIORITY_READAHEAD, ADDTASK_ATEND);
  496. pTask->Release();
  497. }
  498. }
  499. }
  500. }
  501. HRESULT CDefView::ExtractItem(UINT *puIndex, int iItem, LPCITEMIDLIST pidl, BOOL fBackground, BOOL fForce, DWORD dwMaxPriority)
  502. {
  503. if (!_pImageCache || _fDestroying)
  504. return S_FALSE;
  505. if (iItem == -1 && !pidl)
  506. {
  507. return S_FALSE; // failure....
  508. }
  509. if (iItem == -1)
  510. {
  511. // LISTVIEW
  512. iItem = _FindItem(pidl, NULL, FALSE);
  513. if (iItem == -1)
  514. {
  515. return S_FALSE;
  516. }
  517. }
  518. IExtractImage *pExtract;
  519. HRESULT hr = _pshf->GetUIObjectOf(_hwndMain, 1, &pidl, IID_X_PPV_ARG(IExtractImage, 0, &pExtract));
  520. if (FAILED(hr))
  521. {
  522. hr = _GetDefaultTypeExtractor(pidl, &pExtract);
  523. }
  524. if (SUCCEEDED(hr))
  525. {
  526. FILETIME ftImageTimeStamp = {0,0};
  527. // do they support date stamps....
  528. IExtractImage2 *pei2;
  529. if (SUCCEEDED(pExtract->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2))))
  530. {
  531. pei2->GetDateStamp(&ftImageTimeStamp);
  532. pei2->Release();
  533. }
  534. if (IsNullTime(&ftImageTimeStamp) && _pshf2)
  535. {
  536. // fall back to this (most common case)
  537. GetDateProperty(_pshf2, pidl, &SCID_WRITETIME, &ftImageTimeStamp);
  538. }
  539. // always extract at 24 bit incase we have to cache it ...
  540. WCHAR szPath[MAX_PATH];
  541. DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_ORIGSIZE;
  542. if (fForce)
  543. {
  544. dwFlags |= IEIFLAG_QUALITY; // Force means give me the high-quality thumbnail, if possible
  545. }
  546. // Let this run at a slightly higher priority so that we can get the eventual
  547. // cache read or extract task scheduled sooner
  548. DWORD dwPriority = PRIORITY_EXTRACT_NORMAL;
  549. SIZE sizeThumbnail;
  550. _GetThumbnailSize(&sizeThumbnail);
  551. hr = pExtract->GetLocation(szPath, ARRAYSIZE(szPath), &dwPriority, &sizeThumbnail, 24, &dwFlags);
  552. if (dwPriority == PRIORITY_EXTRACT_NORMAL)
  553. {
  554. dwPriority = dwMaxPriority;
  555. }
  556. else if (dwPriority > PRIORITY_EXTRACT_NORMAL)
  557. {
  558. dwPriority = dwMaxPriority + PRIORITY_DELTA_FAST;
  559. }
  560. else
  561. {
  562. dwPriority = dwMaxPriority - PRIORITY_DELTA_SLOW;
  563. }
  564. if (SUCCEEDED(hr) || (hr == E_PENDING))
  565. {
  566. BOOL fAsync = (hr == E_PENDING);
  567. hr = E_FAIL;
  568. // use the name of the item in defview as the key for the caches
  569. DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
  570. if (!fForce)
  571. {
  572. // check if the image is already in the in memory cache
  573. IMAGECACHEINFO rgInfo = {0};
  574. rgInfo.cbSize = sizeof(rgInfo);
  575. rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS;
  576. rgInfo.pszName = szPath;
  577. rgInfo.dwFlags = _GetOverlayMask(pidl);
  578. rgInfo.ftDateStamp = ftImageTimeStamp;
  579. if (!IsNullTime(&ftImageTimeStamp))
  580. rgInfo.dwMask |= ICIFLAG_DATESTAMP;
  581. hr = _pImageCache->FindImage(&rgInfo, puIndex);
  582. }
  583. if (hr != S_OK)
  584. {
  585. DWORD dwTaskID = _MapIndexPIDLToID(iItem, pidl);
  586. if (dwTaskID != (DWORD) -1)
  587. {
  588. // create a task for a disk cache
  589. CTestCacheTask *pTask;
  590. hr = CTestCacheTask_Create(dwTaskID, this, pExtract, szPath, ftImageTimeStamp, pidl,
  591. iItem, dwFlags, dwPriority, fAsync, fBackground, fForce, &pTask);
  592. if (SUCCEEDED(hr))
  593. {
  594. // does it not support Async, or were we told to run it forground ?
  595. if (!fAsync || !fBackground)
  596. {
  597. if (!fBackground)
  598. {
  599. // make sure there is no extract task already underway as we
  600. // are not adding this to the queue...
  601. _pScheduler->RemoveTasks(TOID_ExtractImageTask, dwTaskID, TRUE);
  602. }
  603. // NOTE: We must call RunInitRT, not Run, for CTestCacheTask. The reason is that RunInitRT
  604. // will return S_FALSE if it needs the default icon to be displayed but we would loose that
  605. // extra data if we call Run directly.
  606. hr = pTask->RunInitRT();
  607. // If RunInitRT returns S_OK then the correct image index was generated, however we don't know what
  608. // that index is at this time. We will return S_OK and I_IMAGECALLBACK in this case because we
  609. // know that a WM_UPDATEITEMIMAGE message should have been posted
  610. }
  611. else
  612. {
  613. // add the task to the scheduler...
  614. TraceMsg(TF_DEFVIEW, "ExtractItem *ADDING* CCheckCacheTask (szPath=%s priority=%x index=%d ID=%d)", szPath, dwPriority, iItem, dwTaskID);
  615. hr = _AddTask((IRunnableTask *)pTask, TOID_CheckCacheTask, dwTaskID, dwPriority, ADDTASK_ONLYONCE);
  616. // signify we want a default icon for now....
  617. hr = S_FALSE;
  618. }
  619. pTask->Release();
  620. }
  621. }
  622. }
  623. }
  624. pExtract->Release();
  625. }
  626. return hr;
  627. }
  628. DWORD GetCurrentColorFlags(UINT * puBytesPerPixel)
  629. {
  630. DWORD dwFlags = 0;
  631. UINT uBytesPerPix = 1;
  632. int res = (int)GetCurColorRes();
  633. switch (res)
  634. {
  635. case 16 : dwFlags = ILC_COLOR16;
  636. uBytesPerPix = 2;
  637. break;
  638. case 24 :
  639. case 32 : dwFlags = ILC_COLOR24;
  640. uBytesPerPix = 3;
  641. break;
  642. default : dwFlags = ILC_COLOR8;
  643. uBytesPerPix = 1;
  644. }
  645. if (puBytesPerPixel)
  646. {
  647. *puBytesPerPixel = uBytesPerPix;
  648. }
  649. return dwFlags;
  650. }
  651. UINT CalcCacheMaxSize(const SIZE * psizeThumbnail, UINT uBytesPerPix)
  652. {
  653. // the minimum in the cache is the number of thumbnails visible on the screen at once.
  654. HDC hdc = GetDC(NULL);
  655. int iWidth = GetDeviceCaps(hdc, HORZRES);
  656. int iHeight = GetDeviceCaps(hdc, VERTRES);
  657. ReleaseDC(NULL, hdc);
  658. // the minimum number of thumbnails in the cache, is set to the maximum amount
  659. // of thumbnails that can be diplayed by a single view at once.
  660. int iRow = iWidth / (psizeThumbnail->cx + DEFSIZE_BORDER);
  661. int iCol = iHeight / (psizeThumbnail->cy + DEFSIZE_VERTBDR);
  662. UINT iMinThumbs = iRow * iCol + NUM_OVERLAY_IMAGES;
  663. // calculate the maximum number of thumbnails in the cache based on available memory
  664. MEMORYSTATUS ms;
  665. ms.dwLength = sizeof(ms);
  666. GlobalMemoryStatus(&ms);
  667. // set the thumbnail maximum by calculating the memory required for a single thumbnail.
  668. // then use no more than 1/3 the available memory.
  669. // Say you had 80x80x32bpp thumbnails, this would be 13 images per MB of available memory.
  670. int iMemReqThumb = psizeThumbnail->cx * psizeThumbnail->cy * uBytesPerPix;
  671. UINT iMaxThumbs = UINT((ms.dwAvailPhys / 3) / iMemReqThumb);
  672. #ifdef DEBUG
  673. return iMinThumbs;
  674. #else
  675. return __max(iMaxThumbs, iMinThumbs);
  676. #endif
  677. }
  678. void ListView_InvalidateImageIndexes(HWND hwndList)
  679. {
  680. int iItem = -1;
  681. while ((iItem = ListView_GetNextItem(hwndList, iItem, 0)) != -1)
  682. {
  683. LV_ITEM lvi = {0};
  684. lvi.mask = LVIF_IMAGE;
  685. lvi.iItem = iItem;
  686. lvi.iImage = I_IMAGECALLBACK;
  687. ListView_SetItem(hwndList, &lvi);
  688. }
  689. }
  690. ULONG CDefView::_ApproxItemsPerView()
  691. {
  692. RECT rcClient;
  693. ULONG ulItemsPerView = 0;
  694. if (_hwndView && GetClientRect(_hwndView, &rcClient))
  695. {
  696. SIZE sizeThumbnail;
  697. _GetThumbnailSize(&sizeThumbnail);
  698. ULONG ulItemWidth = sizeThumbnail.cx + DEFSIZE_BORDER;
  699. ULONG ulItemHeight = sizeThumbnail.cy + DEFSIZE_VERTBDR;
  700. ulItemsPerView = (rcClient.right - rcClient.left + ulItemWidth / 2) / ulItemWidth;
  701. ulItemsPerView *= (rcClient.bottom - rcClient.top + ulItemHeight / 2) / ulItemHeight;
  702. }
  703. return ulItemsPerView;
  704. }
  705. void CDefView::_SetThumbview()
  706. {
  707. // Since we are switching into thumbnail view, remove any icon tasks
  708. if (_pScheduler)
  709. _pScheduler->RemoveTasks(TOID_DVIconExtract, ITSAT_DEFAULT_LPARAM, TRUE);
  710. if (_pImageCache == NULL)
  711. {
  712. // create the image cache (before we do the CreateWindow)....
  713. CoCreateInstance(CLSID_ImageListCache, NULL, CLSCTX_INPROC,
  714. IID_PPV_ARG(IImageCache3, &_pImageCache));
  715. }
  716. if (_pDiskCache == NULL &&
  717. !SHRestricted(REST_NOTHUMBNAILCACHE) &&
  718. !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE))
  719. {
  720. LPITEMIDLIST pidlFolder = _GetViewPidl();
  721. if (pidlFolder)
  722. {
  723. LoadFromIDList(CLSID_ShellThumbnailDiskCache, pidlFolder, IID_PPV_ARG(IShellImageStore, &_pDiskCache));
  724. ILFree(pidlFolder);
  725. }
  726. }
  727. if (_IsOwnerData())
  728. _ThumbnailMapInit();
  729. if (_pImageCache)
  730. {
  731. HRESULT hrInit = E_FAIL;
  732. UINT uBytesPerPix;
  733. IMAGECACHEINITINFO rgInitInfo;
  734. rgInitInfo.cbSize = sizeof(rgInitInfo);
  735. rgInitInfo.dwMask = ICIIFLAG_LARGE | ICIIFLAG_SORTBYUSED;
  736. _GetThumbnailSize(&rgInitInfo.rgSizeLarge);
  737. rgInitInfo.iStart = 0;
  738. rgInitInfo.iGrow = 5;
  739. _dwRecClrDepth = rgInitInfo.dwFlags = GetCurrentColorFlags(&uBytesPerPix);
  740. rgInitInfo.dwFlags |= ILC_MASK;
  741. _iMaxCacheSize = CalcCacheMaxSize(&rgInitInfo.rgSizeLarge, uBytesPerPix);
  742. hrInit = _pImageCache->GetImageList(&rgInitInfo);
  743. if (SUCCEEDED(hrInit))
  744. {
  745. // GetImageList() will return S_FALSE if it was already created...
  746. if (_dwRecClrDepth <= 8)
  747. {
  748. HPALETTE hpal = NULL;
  749. HRESULT hrPalette = _GetBrowserPalette(&hpal);
  750. if (SUCCEEDED(_GetBrowserPalette(&hpal)))
  751. {
  752. PALETTEENTRY rgColours[256];
  753. RGBQUAD rgDIBColours[256];
  754. int nColours = GetPaletteEntries(hpal, 0, ARRAYSIZE(rgColours), rgColours);
  755. // translate from the LOGPALETTE structure to the RGBQUAD structure ...
  756. for (int iColour = 0; iColour < nColours; iColour ++)
  757. {
  758. rgDIBColours[iColour].rgbRed = rgColours[iColour].peRed;
  759. rgDIBColours[iColour].rgbBlue = rgColours[iColour].peBlue;
  760. rgDIBColours[iColour].rgbGreen = rgColours[iColour].peGreen;
  761. rgDIBColours[iColour].rgbReserved = 0;
  762. }
  763. ImageList_SetColorTable(rgInitInfo.himlLarge, 0, nColours, rgDIBColours);
  764. }
  765. // Make sure we are not using the double buffer stuff...
  766. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_DOUBLEBUFFER, 0);
  767. }
  768. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_BORDERSELECT, LVS_EX_BORDERSELECT);
  769. if (_fs.fFlags & FWF_OWNERDATA)
  770. {
  771. InvalidateRect(_hwndListview, NULL, TRUE);
  772. }
  773. else
  774. {
  775. ListView_InvalidateImageIndexes(_hwndListview);
  776. }
  777. ListView_SetImageList(_hwndListview, rgInitInfo.himlLarge, LVSIL_NORMAL);
  778. HIMAGELIST himlLarge;
  779. Shell_GetImageLists(&himlLarge, NULL);
  780. int cxIcon, cyIcon;
  781. ImageList_GetIconSize(himlLarge, &cxIcon, &cyIcon);
  782. int cySpacing = (_fs.fFlags & FWF_HIDEFILENAMES) ? cyIcon / 4 + rgInitInfo.rgSizeLarge.cy + 3 : 0;
  783. int cxSpacing = cxIcon / 4 + rgInitInfo.rgSizeLarge.cx + 1;
  784. // Usability issue: people have trouble unselecting, marquee selecting, and dropping
  785. // since they can't find the background. Add an extra 20 pixels between the thumbnails
  786. // to avoid this problem.
  787. //
  788. ListView_SetIconSpacing(_hwndListview, cxSpacing + 20, cySpacing);
  789. // NOTE: if you need to adjust cySpacing above, you can't do it directly since we
  790. // can't calculate the proper size of the icons. Do it this way:
  791. // DWORD dwOld = ListView_SetIconSpacing(_hwndListview, cxSpacing, cySpacing);
  792. // ListView_SetIconSpacing(_hwndListview, LOWORD(dwOld)+20, HIWORD(dwOld)+20);
  793. if (_fs.fFlags & FWF_HIDEFILENAMES)
  794. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_HIDELABELS, LVS_EX_HIDELABELS);
  795. // We need to pre-populate the ImageList controled by _pImageCache
  796. // to contain the default system overlays so that our overlays will
  797. // work. We are going to get them from the already created shell image
  798. // lists as they are in hard-coded locations
  799. UINT uCacheSize = 0;
  800. _pImageCache->GetCacheSize(&uCacheSize);
  801. if (!uCacheSize) // If there are images in the cache the overlays are already there
  802. {
  803. IImageList* piml;
  804. if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml))))
  805. {
  806. struct _OverlayMap
  807. {
  808. int iSystemImage;
  809. int iThumbnailImage;
  810. } rgOverlay[NUM_OVERLAY_IMAGES];
  811. // For whatever reason Overlays are one-based
  812. for (int i = 1; i <= NUM_OVERLAY_IMAGES; i++)
  813. {
  814. int iSysImageIndex;
  815. if (SUCCEEDED(piml->GetOverlayImage(i, &iSysImageIndex)) && (iSysImageIndex != -1))
  816. {
  817. int iMap;
  818. for (iMap = 0; iMap < i - 1; iMap++)
  819. {
  820. if (rgOverlay[iMap].iSystemImage == iSysImageIndex)
  821. break;
  822. }
  823. if (iMap == (i - 1)) // We haven't used this System Image yet
  824. {
  825. HBITMAP hbmOverlay = NULL;
  826. HBITMAP hbmMask = NULL;
  827. if (SUCCEEDED(_CreateOverlayThumbnail(iSysImageIndex, &hbmOverlay, &hbmMask)) && hbmOverlay && hbmMask)
  828. {
  829. IMAGECACHEINFO rgInfo = {0};
  830. int iThumbImageIndex;
  831. rgInfo.cbSize = sizeof(rgInfo);
  832. rgInfo.dwMask = ICIFLAG_SYSTEM | ICIFLAG_LARGE | ICIFLAG_BITMAP;
  833. rgInfo.hBitmapLarge = hbmOverlay;
  834. rgInfo.hMaskLarge = hbmMask;
  835. if (IS_WINDOW_RTL_MIRRORED(_hwndListview))
  836. rgInfo.dwMask |= ICIFLAG_MIRROR;
  837. if (SUCCEEDED(_SafeAddImage(TRUE, &rgInfo, (UINT*)&iThumbImageIndex, -1)))
  838. {
  839. ImageList_SetOverlayImage(rgInitInfo.himlLarge, iThumbImageIndex, i);
  840. rgOverlay[iMap].iSystemImage = iSysImageIndex;
  841. rgOverlay[iMap].iThumbnailImage = iThumbImageIndex;
  842. }
  843. else
  844. {
  845. rgOverlay[i - 1].iSystemImage = -1; // failed to add the image
  846. ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i);
  847. }
  848. }
  849. else
  850. {
  851. rgOverlay[i - 1].iSystemImage = -1; // failed to import htis image
  852. ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i);
  853. }
  854. if (hbmOverlay)
  855. {
  856. DeleteObject(hbmOverlay);
  857. }
  858. if (hbmMask)
  859. {
  860. DeleteObject(hbmMask);
  861. }
  862. }
  863. else
  864. {
  865. ImageList_SetOverlayImage(rgInitInfo.himlLarge, rgOverlay[iMap].iThumbnailImage, i);
  866. rgOverlay[i - 1].iSystemImage = -1; // image already shows up in list
  867. }
  868. }
  869. else
  870. {
  871. rgOverlay[i - 1].iSystemImage = -1; // Didn't find a system image
  872. ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i);
  873. }
  874. }
  875. }
  876. }
  877. }
  878. }
  879. }
  880. void CDefView::_ResetThumbview()
  881. {
  882. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_BORDERSELECT, 0);
  883. if (_fs.fFlags & FWF_HIDEFILENAMES)
  884. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_HIDELABELS, 0);
  885. if (_dwRecClrDepth <= 8)
  886. {
  887. ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER);
  888. }
  889. ListView_SetIconSpacing(_hwndListview, -1, -1);
  890. _SetSysImageList();
  891. if (_IsOwnerData())
  892. _ThumbnailMapClear();
  893. }
  894. HRESULT CDefView::_GetDefaultTypeExtractor(LPCITEMIDLIST pidl, IExtractImage **ppExt)
  895. {
  896. IAssociationArray * paa;
  897. HRESULT hr = _pshf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IAssociationArray, NULL, &paa));
  898. if (SUCCEEDED(hr))
  899. {
  900. LPWSTR psz;
  901. hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"Thumbnail", &psz);
  902. if (SUCCEEDED(hr))
  903. {
  904. LPITEMIDLIST pidlThumb;
  905. hr = SHILCreateFromPath(psz, &pidlThumb, NULL);
  906. if (SUCCEEDED(hr))
  907. {
  908. SHGetUIObjectFromFullPIDL(pidlThumb, NULL, IID_PPV_ARG(IExtractImage, ppExt));
  909. ILFree(pidlThumb);
  910. }
  911. CoTaskMemFree(psz);
  912. }
  913. paa->Release();
  914. }
  915. return hr;
  916. }
  917. struct ThumbMapNode
  918. {
  919. int iIndex;
  920. LPITEMIDLIST pidl;
  921. ~ThumbMapNode() { ILFree(pidl); }
  922. };
  923. int CDefView::_MapIndexPIDLToID(int iIndex, LPCITEMIDLIST pidl)
  924. {
  925. int ret = -1;
  926. if (_IsOwnerData())
  927. {
  928. int cNodes = DPA_GetPtrCount(_dpaThumbnailMap);
  929. int iNode = 0;
  930. for (; iNode < cNodes; iNode++)
  931. {
  932. ThumbMapNode* pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iNode);
  933. ASSERT(pNode);
  934. if (pNode->iIndex == iIndex)
  935. {
  936. if (!(_pshf->CompareIDs(0, pidl, pNode->pidl))) // 99 percent of the time we are good
  937. {
  938. ret = iNode;
  939. }
  940. else // Someone moved our pidl!
  941. {
  942. int iNodeStop = iNode;
  943. for (iNode = (iNode + 1) % cNodes; iNode != iNodeStop; iNode = (iNode + 1) % cNodes)
  944. {
  945. pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iNode);
  946. if (!(_pshf->CompareIDs(0, pidl, pNode->pidl)))
  947. {
  948. ret = iNode;
  949. pNode->iIndex = iIndex; // Newer index for pidl
  950. break;
  951. }
  952. }
  953. }
  954. break;
  955. }
  956. }
  957. if (ret == -1)
  958. {
  959. ThumbMapNode* pNode = new ThumbMapNode;
  960. if (pNode)
  961. {
  962. pNode->iIndex = iIndex;
  963. pNode->pidl = ILClone(pidl);
  964. ret = DPA_AppendPtr(_dpaThumbnailMap, pNode);
  965. if (ret == -1)
  966. {
  967. delete pNode;
  968. }
  969. }
  970. }
  971. }
  972. else
  973. {
  974. ret = ListView_MapIndexToID(_hwndListview, iIndex);
  975. }
  976. return ret;
  977. }
  978. int CDefView::_MapIDToIndex(int iID)
  979. {
  980. int ret = -1;
  981. if (_IsOwnerData())
  982. {
  983. ThumbMapNode* pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iID);
  984. if (pNode)
  985. {
  986. ret = pNode->iIndex;
  987. }
  988. }
  989. else
  990. {
  991. ret = ListView_MapIDToIndex(_hwndListview, iID);
  992. }
  993. return ret;
  994. }
  995. void CDefView::_ThumbnailMapInit()
  996. {
  997. if (_dpaThumbnailMap)
  998. {
  999. _ThumbnailMapClear();
  1000. }
  1001. else
  1002. {
  1003. _dpaThumbnailMap = DPA_Create(1);
  1004. }
  1005. }
  1006. void CDefView::_ThumbnailMapClear()
  1007. {
  1008. if (_dpaThumbnailMap)
  1009. {
  1010. int i = DPA_GetPtrCount(_dpaThumbnailMap);
  1011. while (--i >= 0)
  1012. {
  1013. ThumbMapNode* pNode = (ThumbMapNode*) DPA_FastGetPtr(_dpaThumbnailMap, i);
  1014. delete pNode;
  1015. }
  1016. DPA_DeleteAllPtrs(_dpaThumbnailMap);
  1017. }
  1018. }
  1019. HRESULT CDefView::_GetBrowserPalette(HPALETTE* phpal)
  1020. {
  1021. HRESULT hr = E_UNEXPECTED;
  1022. if (_psb)
  1023. {
  1024. IBrowserService *pbs;
  1025. hr = _psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs));
  1026. if (SUCCEEDED(hr))
  1027. {
  1028. hr = pbs->GetPalette(phpal);
  1029. pbs->Release();
  1030. }
  1031. }
  1032. return hr;
  1033. }