Leaked source code of windows server 2003
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.

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