|
|
#include "shellprv.h"
#include "ids.h"
#include "defview.h"
#include "defviewp.h"
#include "dvtasks.h"
#include "guids.h"
#include "prop.h"
#include "CommonControls.h"
#include "thumbutil.h"
// Thumbnail support
HRESULT CDefView::_SafeAddImage(BOOL fQuick, IMAGECACHEINFO* prgInfo, UINT* piImageIndex, int iListID) { HRESULT hr = S_FALSE; UINT uCacheSize = 0; _pImageCache->GetCacheSize(&uCacheSize); ASSERT(_iMaxCacheSize>0);
BOOL bSpaceOpen = (uCacheSize < (UINT)_iMaxCacheSize); if (!bSpaceOpen) { BOOL bMakeSpace = TRUE; int iListIndex = -1;
// Check to see if we are visible and need to make space
if (-1 != iListID) { iListIndex = _MapIDToIndex(iListID); if (-1 == iListIndex) // Someone removed our item
{ hr = E_INVALIDARG; bMakeSpace = FALSE; } else if (!ListView_IsItemVisible(_hwndListview, iListIndex)) { hr = S_FALSE; bMakeSpace = FALSE; } }
if (bMakeSpace) { // item is visible... try and make a space
UINT uCacheIndex = 0; do { UINT uImageIndex; int iUsage; if (FAILED(_pImageCache->GetImageIndexFromCacheIndex(uCacheIndex, &uImageIndex)) || FAILED(_pImageCache->GetUsage(uImageIndex, (UINT*) &iUsage))) { break; }
if (iUsage != ICD_USAGE_SYSTEM) // Magic number for System Image
{ TraceMsg(TF_DEFVIEW, "CDefView::_SafeAddImage -- FreeImage (CI::%d II::%d)", uCacheIndex, uImageIndex); _pImageCache->FreeImage(uImageIndex); _UpdateImage(uImageIndex); bSpaceOpen = TRUE;
ASSERT((LONG)(uCacheSize - uCacheIndex) > (LONG)_ApproxItemsPerView()); }
uCacheIndex++; } while (!bSpaceOpen);
// If we repeatedly fail to add images to the list and are still decoding more images this means
// we will have to re-walk the list view every time we finish decoding another image, only to then
// throw away the result because we have no where to save it. This could lead to sluggish response
// from the UI. In short, if the following Trace is common then we have a problem that needs to be
// fixed (which might required considerable rearchitecting).
if (!bSpaceOpen) { TraceMsg(TF_WARNING, "CDefView::_SafeAddImage failed to make room in cache!!"); hr = E_FAIL; } } } *piImageIndex = I_IMAGECALLBACK; if (bSpaceOpen) // There is space in the cache for this image
{ hr = _pImageCache->AddImage(prgInfo, piImageIndex); TraceMsg(TF_DEFVIEW, "CDefView::_SafeAddImage -- AddImage (HR:0x%08x name:%s,index:%u)", hr, prgInfo->pszName, *piImageIndex); } return hr; }
COLORREF CDefView::_GetBackColor() { // SendMessage traffic is greatly reduced if we don't ask for the bkcolor
// every time we need it...
if (_rgbBackColor == CLR_INVALID) { _rgbBackColor = ListView_GetBkColor(_hwndListview); if (_rgbBackColor == CLR_NONE) _rgbBackColor = GetSysColor(COLOR_WINDOW); }
return _rgbBackColor; }
HRESULT CDefView::TaskUpdateItem(LPCITEMIDLIST pidl, int iItem, DWORD dwMask, LPCWSTR pszPath, FILETIME ftDateStamp, int iThumbnail, HBITMAP hBmp, DWORD dwItemID) { // check the size of the bitmap to make sure it is big enough, if it is not, then
// we must center it on a background...
BITMAP rgBitmap; HBITMAP hBmpCleanup = NULL; HRESULT hr = E_FAIL;
if (::GetObject((HGDIOBJ)hBmp, sizeof(rgBitmap), &rgBitmap)) { // if the image is the wrong size, or the wrong colour depth, then do the funky stuff on it..
SIZE sizeThumbnail; _GetThumbnailSize(&sizeThumbnail);
if (rgBitmap.bmWidth != sizeThumbnail.cx || rgBitmap.bmHeight != sizeThumbnail.cy || rgBitmap.bmBitsPixel > _dwRecClrDepth) { // alloc the colour table just incase....
BITMAPINFO *pInfo = (BITMAPINFO *)LocalAlloc(LPTR, sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256); if (pInfo) { // get a DC for this operation...
HDC hdcMem = CreateCompatibleDC(NULL); if (hdcMem) { pInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); if (GetDIBits(hdcMem, hBmp, 0, 0, NULL, pInfo, DIB_RGB_COLORS)) { // we have the header, now get the data....
void *pBits = LocalAlloc(LPTR, pInfo->bmiHeader.biSizeImage); if (pBits) { if (GetDIBits(hdcMem, hBmp, 0, pInfo->bmiHeader.biHeight, pBits, pInfo, DIB_RGB_COLORS)) { RECT rgRect = {0, 0, rgBitmap.bmWidth, rgBitmap.bmHeight}; CalculateAspectRatio(&sizeThumbnail, &rgRect);
HPALETTE hpal = NULL; HRESULT hrPalette = _dwRecClrDepth <= 8 ? _GetBrowserPalette(&hpal) : S_OK; if (SUCCEEDED(hrPalette)) { if (FactorAspectRatio(pInfo, pBits, &sizeThumbnail, rgRect, _dwRecClrDepth, hpal, FALSE, _GetBackColor(), &hBmpCleanup)) { // finally success :-) we have the new image we can abandon the old one...
hBmp = hBmpCleanup; hr = S_OK; } } } LocalFree(pBits); } } DeleteDC(hdcMem); } LocalFree(pInfo); } } else { // the original bitmap is fine
hr = S_OK; } }
UINT iImage; if (SUCCEEDED(hr)) { // check if we are going away, if so, then don't use Sendmessage because it will block the
// destructor of the scheduler...
if (_fDestroying) { hr = E_FAIL; } else { // copy thumbnail into the cache.
IMAGECACHEINFO rgInfo = {0}; rgInfo.cbSize = sizeof(rgInfo); rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX | ICIFLAG_LARGE | ICIFLAG_BITMAP; rgInfo.pszName = pszPath; rgInfo.dwFlags = dwMask; rgInfo.iIndex = (int) iThumbnail; rgInfo.hBitmapLarge = hBmp; rgInfo.ftDateStamp = ftDateStamp;
if (!IsNullTime(&ftDateStamp)) rgInfo.dwMask |= ICIFLAG_DATESTAMP;
if (IS_WINDOW_RTL_MIRRORED(_hwndListview)) rgInfo.dwMask |= ICIFLAG_MIRROR;
hr = _SafeAddImage(FALSE, &rgInfo, &iImage, (int) dwItemID); } }
if (hBmpCleanup) { DeleteObject(hBmpCleanup); }
#ifdef USEMASK
DeleteObject(hbmMask); #endif
if (SUCCEEDED(hr)) { LPITEMIDLIST pidlToSend = ILClone(pidl); if (pidlToSend) { DSV_UPDATETHUMBNAIL* putn = (DSV_UPDATETHUMBNAIL*)LocalAlloc(LPTR, sizeof(DSV_UPDATETHUMBNAIL)); if (putn) { putn->iImage = (hr == S_OK) ? iImage : I_IMAGECALLBACK; putn->iItem = iItem; putn->pidl = pidlToSend;
// post to the main thread so we don't deadlock
if (!::PostMessage(_hwndView, WM_DSV_UPDATETHUMBNAIL, 0, (LPARAM)putn)) _CleanupUpdateThumbnail(putn); } else { ILFree(pidlToSend); } } }
return hr; }
HRESULT CDefView::UpdateImageForItem(DWORD dwTaskID, HBITMAP hImage, int iItem, LPCITEMIDLIST pidl, LPCWSTR pszPath, FILETIME ftDateStamp, BOOL fCache, DWORD dwPriority) { HRESULT hr = S_OK; TaskUpdateItem(pidl, iItem, _GetOverlayMask(pidl), pszPath, ftDateStamp, 0, hImage, dwTaskID);
if (_pDiskCache && fCache && (_iWriteTaskCount < MAX_WRITECACHE_TASKS)) { // REVIEW: if pidl is an encrypted file but isn't in an encrytped folder, should avoid writing it's thumbnail?
// If we don't, other users could otherwise view the thumbnail and thus know the contents of the encrypted file.
// Add a cache write test
IRunnableTask *pTask; if (SUCCEEDED(CWriteCacheTask_Create(dwTaskID, this, pszPath, ftDateStamp, hImage, &pTask))) { _AddTask(pTask, TOID_WriteCacheHandler, dwTaskID, dwPriority - PRIORITY_DELTA_WRITE, ADDTASK_ONLYONCE | ADDTASK_ATEND); pTask->Release(); hr = S_FALSE; } }
return hr; }
DWORD CDefView::_GetOverlayMask(LPCITEMIDLIST pidl) { DWORD dwLink = SFGAO_GHOSTED; // SFGAO_LINK | SFGAO_SHARE
_pshf->GetAttributesOf(1, &pidl, &dwLink); return dwLink; }
void CDefView::_UpdateThumbnail(int iItem, int iImage, LPCITEMIDLIST pidl) { if (!_IsOwnerData()) { if (_hwndListview) { int iFoundItem = _FindItemHint(pidl, iItem); if (-1 != iFoundItem) { LV_ITEM rgItem = {0}; rgItem.mask = LVIF_IMAGE; rgItem.iItem = iFoundItem; rgItem.iImage = iImage;
// We are about to change the given item for purely internal reasons, we should not treat
// this change as a "real change". As such we set a flag so that we ignore the LVN_ITEMCHANGED
// notification that is generated by this LVM_SETITEM message. If we don't ingore this
// next message then we would fire another DISPID_SELECTIONCHANGED every time we finish
// extracting an image (if the image is selected).
_fIgnoreItemChanged = TRUE; ListView_SetItem(_hwndListview, &rgItem); _fIgnoreItemChanged = FALSE; } } } else { RECT rc; ListView_GetItemRect(_hwndListview, iItem, &rc, LVIR_BOUNDS); InvalidateRect(_hwndListview, &rc, FALSE); } }
void CDefView::_CleanupUpdateThumbnail(DSV_UPDATETHUMBNAIL* putn) { ILFree(putn->pidl); LocalFree((HLOCAL)putn); }
int CDefView::ViewGetIconIndex(LPCITEMIDLIST pidl) { int iIndex = -1;
if (_psi) { // check to see if we succeeded and we weren't told to extract the icon
// ourselves ...
if ((S_OK == _psi->GetIconOf(pidl, 0, &iIndex)) && _psio) { int iOverlay; if (SUCCEEDED(_psio->GetOverlayIndex(pidl, &iOverlay))) { iIndex |= iOverlay << 24; } } }
if (-1 == iIndex) { iIndex = SHMapPIDLToSystemImageListIndex(_pshf, pidl, NULL); }
return (iIndex >= 0) ? iIndex : II_DOCNOASSOC; }
HRESULT CDefView::CreateDefaultThumbnail(int iIndex, HBITMAP *phBmpThumbnail, BOOL fCorner) { HRESULT hr = E_FAIL; // get the background for the default thumbnail.
HDC hdc = GetDC(NULL); HDC hMemDC = CreateCompatibleDC(hdc); if (hMemDC) { SIZE sizeThumbnail; _GetThumbnailSize(&sizeThumbnail);
*phBmpThumbnail = CreateCompatibleBitmap(hdc, sizeThumbnail.cx, sizeThumbnail.cy); if (*phBmpThumbnail) { HGDIOBJ hTmp = SelectObject(hMemDC, *phBmpThumbnail); RECT rc = {0, 0, sizeThumbnail.cx, sizeThumbnail.cy};
SHFillRectClr(hMemDC, &rc, _GetBackColor()); IImageList* piml; if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml)))) { int cxIcon, cyIcon, x, y, dx, dy; // calculate position and width of icon.
piml->GetIconSize(&cxIcon, &cyIcon); if (cxIcon < sizeThumbnail.cx) { if (fCorner) { x = 0; } else { x = (sizeThumbnail.cx - cxIcon) / 2; } dx = cxIcon; } else { // in case icon size is larger than thumbnail size.
x = 0; dx = sizeThumbnail.cx; } if (cyIcon < sizeThumbnail.cy) { if (fCorner) { y = sizeThumbnail.cy - cyIcon; } else { y = (sizeThumbnail.cy - cyIcon) / 2; } dy = cyIcon; } else { // in case icon size is larger than thumbnail size.
y = 0; dy = sizeThumbnail.cy; }
IMAGELISTDRAWPARAMS idp = {sizeof(idp)}; idp.i = (iIndex & 0x00ffffff); idp.hdcDst = hMemDC; idp.x = x; idp.y = y; idp.cx = dx; idp.cy = dy; idp.rgbBk = CLR_DEFAULT; idp.rgbFg = CLR_DEFAULT; idp.fStyle = ILD_TRANSPARENT; piml->Draw(&idp); piml->Release(); } // get the bitmap produced so that it will be returned.
*phBmpThumbnail = (HBITMAP) SelectObject(hMemDC, hTmp); hr = S_OK; } } if (hMemDC) DeleteDC(hMemDC); ReleaseDC(NULL, hdc); return hr; }
void CDefView::_CacheDefaultThumbnail(LPCITEMIDLIST pidl, int* piIcon) { // create the default one for that file type,
// the index into the sys image list is used to detect items of the
// same type, thus we only generate one default thumbnail for each
// particular icon needed
UINT iIndex = (UINT) ViewGetIconIndex(pidl);
if (iIndex == (UINT) I_IMAGECALLBACK) { iIndex = II_DOCNOASSOC; }
if (_pImageCache) { // check if the image is already in the image cache.
IMAGECACHEINFO rgInfo; rgInfo.cbSize = sizeof(rgInfo); rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX; rgInfo.pszName = L"Default"; rgInfo.dwFlags = _GetOverlayMask(pidl); rgInfo.iIndex = (int) iIndex;
HRESULT hr = _pImageCache->FindImage(&rgInfo, (UINT*)piIcon); if (hr != S_OK) { HBITMAP hBmpThumb = NULL;
hr = CreateDefaultThumbnail(iIndex, &hBmpThumb, FALSE); if (SUCCEEDED(hr)) { // we are creating a new one, so we shouldn't have an index yet ..
Assert(*piIcon == I_IMAGECALLBACK);
// copy thumbnail into the imagelist.
rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS | ICIFLAG_INDEX | ICIFLAG_LARGE | ICIFLAG_BITMAP; rgInfo.hBitmapLarge = hBmpThumb; rgInfo.hMaskLarge = NULL;
if (IS_WINDOW_RTL_MIRRORED(_hwndListview)) rgInfo.dwMask |= ICIFLAG_MIRROR;
hr = _SafeAddImage(TRUE, &rgInfo, (UINT*)piIcon, -1);
DeleteObject(hBmpThumb); } else { *piIcon = (UINT) I_IMAGECALLBACK; } } } else { *piIcon = II_DOCNOASSOC; } }
//
// Creates an thumbnail overlay based on the system index
//
HRESULT CDefView::_CreateOverlayThumbnail(int iIndex, HBITMAP* phbmOverlay, HBITMAP* phbmMask) { HRESULT hr = CreateDefaultThumbnail(iIndex, phbmOverlay, TRUE); if (SUCCEEDED(hr)) { HDC hdc = GetDC(NULL); BITMAP bm; hr = E_FAIL; if (::GetObject(*phbmOverlay, sizeof(bm), &bm) == sizeof(bm)) { HDC hdcImg = ::CreateCompatibleDC(hdc); HDC hdcMask = ::CreateCompatibleDC(hdc);
if (hdcImg && hdcMask) { *phbmMask = ::CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL); if (*phbmMask) { HBITMAP hbmpOldImg = (HBITMAP) ::SelectObject(hdcImg, *phbmOverlay); HBITMAP hbmpOldMsk = (HBITMAP) ::SelectObject(hdcMask, *phbmMask); COLORREF clrTransparent = ::GetPixel(hdcImg, 0, 0); ::SetBkColor(hdcImg, clrTransparent); ::BitBlt(hdcMask, 0, 0, bm.bmWidth, bm.bmHeight, hdcImg, 0, 0, SRCCOPY);
::SelectObject(hdcImg, hbmpOldImg); ::SelectObject(hdcMask, hbmpOldMsk);
hr = S_OK; } } if (hdcImg) { DeleteDC(hdcImg); } if (hdcMask) { DeleteDC(hdcMask); } }
ReleaseDC(NULL, hdc); }
return hr; }
void CDefView::_DoThumbnailReadAhead() { // Start up the ReadAheadHandler if:
// 1) view requires thumbnails
// 2) we have items in view (handle delayed enum)
// 3) we haven't kicked it off already
// 4) If we're not ownerdata
if (_IsImageMode()) { UINT cItems = ListView_GetItemCount(_hwndListview); if (cItems && !_fReadAhead && !_IsOwnerData()) { // Start the read-ahead task
_fReadAhead = TRUE; IRunnableTask *pTask; if (SUCCEEDED(CReadAheadTask_Create(this, &pTask))) { // add with a low prority, but higher than HTML extraction...
_AddTask(pTask, TOID_ReadAheadHandler, 0, PRIORITY_READAHEAD, ADDTASK_ATEND); pTask->Release(); } } } }
HRESULT CDefView::ExtractItem(UINT *puIndex, int iItem, LPCITEMIDLIST pidl, BOOL fBackground, BOOL fForce, DWORD dwMaxPriority) { if (!_pImageCache || _fDestroying) return S_FALSE;
if (iItem == -1 && !pidl) { return S_FALSE; // failure....
}
if (iItem == -1) { // LISTVIEW
iItem = _FindItem(pidl, NULL, FALSE); if (iItem == -1) { return S_FALSE; } }
IExtractImage *pExtract; HRESULT hr = _pshf->GetUIObjectOf(_hwndMain, 1, &pidl, IID_X_PPV_ARG(IExtractImage, 0, &pExtract)); if (FAILED(hr)) { hr = _GetDefaultTypeExtractor(pidl, &pExtract); }
if (SUCCEEDED(hr)) { FILETIME ftImageTimeStamp = {0,0};
// do they support date stamps....
IExtractImage2 *pei2; if (SUCCEEDED(pExtract->QueryInterface(IID_PPV_ARG(IExtractImage2, &pei2)))) { pei2->GetDateStamp(&ftImageTimeStamp); pei2->Release(); }
if (IsNullTime(&ftImageTimeStamp) && _pshf2) { // fall back to this (most common case)
GetDateProperty(_pshf2, pidl, &SCID_WRITETIME, &ftImageTimeStamp); }
// always extract at 24 bit incase we have to cache it ...
WCHAR szPath[MAX_PATH]; DWORD dwFlags = IEIFLAG_ASYNC | IEIFLAG_ORIGSIZE; if (fForce) { dwFlags |= IEIFLAG_QUALITY; // Force means give me the high-quality thumbnail, if possible
}
// Let this run at a slightly higher priority so that we can get the eventual
// cache read or extract task scheduled sooner
DWORD dwPriority = PRIORITY_EXTRACT_NORMAL; SIZE sizeThumbnail; _GetThumbnailSize(&sizeThumbnail); hr = pExtract->GetLocation(szPath, ARRAYSIZE(szPath), &dwPriority, &sizeThumbnail, 24, &dwFlags); if (dwPriority == PRIORITY_EXTRACT_NORMAL) { dwPriority = dwMaxPriority; } else if (dwPriority > PRIORITY_EXTRACT_NORMAL) { dwPriority = dwMaxPriority + PRIORITY_DELTA_FAST; } else { dwPriority = dwMaxPriority - PRIORITY_DELTA_SLOW; }
if (SUCCEEDED(hr) || (hr == E_PENDING)) { BOOL fAsync = (hr == E_PENDING); hr = E_FAIL;
// use the name of the item in defview as the key for the caches
DisplayNameOf(_pshf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath));
if (!fForce) { // check if the image is already in the in memory cache
IMAGECACHEINFO rgInfo = {0}; rgInfo.cbSize = sizeof(rgInfo); rgInfo.dwMask = ICIFLAG_NAME | ICIFLAG_FLAGS; rgInfo.pszName = szPath; rgInfo.dwFlags = _GetOverlayMask(pidl); rgInfo.ftDateStamp = ftImageTimeStamp;
if (!IsNullTime(&ftImageTimeStamp)) rgInfo.dwMask |= ICIFLAG_DATESTAMP; hr = _pImageCache->FindImage(&rgInfo, puIndex); }
if (hr != S_OK) { DWORD dwTaskID = _MapIndexPIDLToID(iItem, pidl); if (dwTaskID != (DWORD) -1) { // create a task for a disk cache
CTestCacheTask *pTask; hr = CTestCacheTask_Create(dwTaskID, this, pExtract, szPath, ftImageTimeStamp, pidl, iItem, dwFlags, dwPriority, fAsync, fBackground, fForce, &pTask); if (SUCCEEDED(hr)) { // does it not support Async, or were we told to run it forground ?
if (!fAsync || !fBackground) { TraceMsg(TF_WARNING, "CDefView::ExtractItem is going to extract on the foreground thread (%s)", szPath); if (!fBackground) { // make sure there is no extract task already underway as we
// are not adding this to the queue...
_pScheduler->RemoveTasks(TOID_ExtractImageTask, dwTaskID, TRUE); }
// NOTE: We must call RunInitRT, not Run, for CTestCacheTask. The reason is that RunInitRT
// will return S_FALSE if it needs the default icon to be displayed but we would loose that
// extra data if we call Run directly.
hr = pTask->RunInitRT();
// If RunInitRT returns S_OK then the correct image index was generated, however we don't know what
// that index is at this time. We will return S_OK and I_IMAGECALLBACK in this case because we
// know that a WM_UPDATEITEMIMAGE message should have been posted
} else { // add the task to the scheduler...
TraceMsg(TF_DEFVIEW, "ExtractItem *ADDING* CCheckCacheTask (szPath=%s priority=%x index=%d ID=%d)", szPath, dwPriority, iItem, dwTaskID); hr = _AddTask((IRunnableTask *)pTask, TOID_CheckCacheTask, dwTaskID, dwPriority, ADDTASK_ONLYONCE);
// signify we want a default icon for now....
hr = S_FALSE; } pTask->Release(); } } } } pExtract->Release(); }
return hr; }
DWORD GetCurrentColorFlags(UINT * puBytesPerPixel) { DWORD dwFlags = 0; UINT uBytesPerPix = 1; int res = (int)GetCurColorRes(); switch (res) { case 16 : dwFlags = ILC_COLOR16; uBytesPerPix = 2; break; case 24 : case 32 : dwFlags = ILC_COLOR24; uBytesPerPix = 3; break; default : dwFlags = ILC_COLOR8; uBytesPerPix = 1; } if (puBytesPerPixel) { *puBytesPerPixel = uBytesPerPix; }
return dwFlags; }
UINT CalcCacheMaxSize(const SIZE * psizeThumbnail, UINT uBytesPerPix) { // the minimum in the cache is the number of thumbnails visible on the screen at once.
HDC hdc = GetDC(NULL); int iWidth = GetDeviceCaps(hdc, HORZRES); int iHeight = GetDeviceCaps(hdc, VERTRES); ReleaseDC(NULL, hdc);
// the minimum number of thumbnails in the cache, is set to the maximum amount
// of thumbnails that can be diplayed by a single view at once.
int iRow = iWidth / (psizeThumbnail->cx + DEFSIZE_BORDER); int iCol = iHeight / (psizeThumbnail->cy + DEFSIZE_VERTBDR); UINT iMinThumbs = iRow * iCol + NUM_OVERLAY_IMAGES;
// calculate the maximum number of thumbnails in the cache based on available memory
MEMORYSTATUS ms; ms.dwLength = sizeof(ms); GlobalMemoryStatus(&ms);
// set the thumbnail maximum by calculating the memory required for a single thumbnail.
// then use no more than 1/3 the available memory.
// Say you had 80x80x32bpp thumbnails, this would be 13 images per MB of available memory.
int iMemReqThumb = psizeThumbnail->cx * psizeThumbnail->cy * uBytesPerPix; UINT iMaxThumbs = UINT((ms.dwAvailPhys / 3) / iMemReqThumb);
#ifdef DEBUG
return iMinThumbs; #else
return __max(iMaxThumbs, iMinThumbs); #endif
}
void ListView_InvalidateImageIndexes(HWND hwndList) { int iItem = -1; while ((iItem = ListView_GetNextItem(hwndList, iItem, 0)) != -1) { LV_ITEM lvi = {0}; lvi.mask = LVIF_IMAGE; lvi.iItem = iItem; lvi.iImage = I_IMAGECALLBACK;
ListView_SetItem(hwndList, &lvi); } }
ULONG CDefView::_ApproxItemsPerView() { RECT rcClient; ULONG ulItemsPerView = 0; if (_hwndView && GetClientRect(_hwndView, &rcClient)) { SIZE sizeThumbnail; _GetThumbnailSize(&sizeThumbnail);
ULONG ulItemWidth = sizeThumbnail.cx + DEFSIZE_BORDER; ULONG ulItemHeight = sizeThumbnail.cy + DEFSIZE_VERTBDR; ulItemsPerView = (rcClient.right - rcClient.left + ulItemWidth / 2) / ulItemWidth; ulItemsPerView *= (rcClient.bottom - rcClient.top + ulItemHeight / 2) / ulItemHeight; }
return ulItemsPerView; }
void CDefView::_SetThumbview() { // Since we are switching into thumbnail view, remove any icon tasks
if (_pScheduler) _pScheduler->RemoveTasks(TOID_DVIconExtract, ITSAT_DEFAULT_LPARAM, TRUE);
if (_pImageCache == NULL) { // create the image cache (before we do the CreateWindow)....
CoCreateInstance(CLSID_ImageListCache, NULL, CLSCTX_INPROC, IID_PPV_ARG(IImageCache3, &_pImageCache)); }
if (_pDiskCache == NULL && !SHRestricted(REST_NOTHUMBNAILCACHE) && !SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("DisableThumbnailCache"), 0, FALSE)) { LPITEMIDLIST pidlFolder = _GetViewPidl(); if (pidlFolder) { LoadFromIDList(CLSID_ShellThumbnailDiskCache, pidlFolder, IID_PPV_ARG(IShellImageStore, &_pDiskCache)); ILFree(pidlFolder); } }
if (_IsOwnerData()) _ThumbnailMapInit();
if (_pImageCache) { HRESULT hrInit = E_FAIL; UINT uBytesPerPix; IMAGECACHEINITINFO rgInitInfo; rgInitInfo.cbSize = sizeof(rgInitInfo); rgInitInfo.dwMask = ICIIFLAG_LARGE | ICIIFLAG_SORTBYUSED; _GetThumbnailSize(&rgInitInfo.rgSizeLarge); rgInitInfo.iStart = 0; rgInitInfo.iGrow = 5; _dwRecClrDepth = rgInitInfo.dwFlags = GetCurrentColorFlags(&uBytesPerPix); rgInitInfo.dwFlags |= ILC_MASK; _iMaxCacheSize = CalcCacheMaxSize(&rgInitInfo.rgSizeLarge, uBytesPerPix);
hrInit = _pImageCache->GetImageList(&rgInitInfo); if (SUCCEEDED(hrInit)) { // GetImageList() will return S_FALSE if it was already created...
if (_dwRecClrDepth <= 8) { HPALETTE hpal = NULL; HRESULT hrPalette = _GetBrowserPalette(&hpal); if (SUCCEEDED(_GetBrowserPalette(&hpal))) { PALETTEENTRY rgColours[256]; RGBQUAD rgDIBColours[256];
int nColours = GetPaletteEntries(hpal, 0, ARRAYSIZE(rgColours), rgColours);
// translate from the LOGPALETTE structure to the RGBQUAD structure ...
for (int iColour = 0; iColour < nColours; iColour ++) { rgDIBColours[iColour].rgbRed = rgColours[iColour].peRed; rgDIBColours[iColour].rgbBlue = rgColours[iColour].peBlue; rgDIBColours[iColour].rgbGreen = rgColours[iColour].peGreen; rgDIBColours[iColour].rgbReserved = 0; }
ImageList_SetColorTable(rgInitInfo.himlLarge, 0, nColours, rgDIBColours); }
// Make sure we are not using the double buffer stuff...
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_DOUBLEBUFFER, 0); }
ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_BORDERSELECT, LVS_EX_BORDERSELECT);
if (_fs.fFlags & FWF_OWNERDATA) { InvalidateRect(_hwndListview, NULL, TRUE); } else { ListView_InvalidateImageIndexes(_hwndListview); }
ListView_SetImageList(_hwndListview, rgInitInfo.himlLarge, LVSIL_NORMAL);
HIMAGELIST himlLarge; Shell_GetImageLists(&himlLarge, NULL);
int cxIcon, cyIcon; ImageList_GetIconSize(himlLarge, &cxIcon, &cyIcon); int cySpacing = (_fs.fFlags & FWF_HIDEFILENAMES) ? cyIcon / 4 + rgInitInfo.rgSizeLarge.cy + 3 : 0; int cxSpacing = cxIcon / 4 + rgInitInfo.rgSizeLarge.cx + 1;
// Usability issue: people have trouble unselecting, marquee selecting, and dropping
// since they can't find the background. Add an extra 20 pixels between the thumbnails
// to avoid this problem.
//
ListView_SetIconSpacing(_hwndListview, cxSpacing + 20, cySpacing);
// NOTE: if you need to adjust cySpacing above, you can't do it directly since we
// can't calculate the proper size of the icons. Do it this way:
// DWORD dwOld = ListView_SetIconSpacing(_hwndListview, cxSpacing, cySpacing);
// ListView_SetIconSpacing(_hwndListview, LOWORD(dwOld)+20, HIWORD(dwOld)+20);
if (_fs.fFlags & FWF_HIDEFILENAMES) ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_HIDELABELS, LVS_EX_HIDELABELS);
// We need to pre-populate the ImageList controled by _pImageCache
// to contain the default system overlays so that our overlays will
// work. We are going to get them from the already created shell image
// lists as they are in hard-coded locations
UINT uCacheSize = 0; _pImageCache->GetCacheSize(&uCacheSize);
if (!uCacheSize) // If there are images in the cache the overlays are already there
{ IImageList* piml; if (SUCCEEDED(SHGetImageList(SHIL_EXTRALARGE, IID_PPV_ARG(IImageList, &piml)))) { struct _OverlayMap { int iSystemImage; int iThumbnailImage; } rgOverlay[NUM_OVERLAY_IMAGES]; // For whatever reason Overlays are one-based
for (int i = 1; i <= NUM_OVERLAY_IMAGES; i++) { int iSysImageIndex; if (SUCCEEDED(piml->GetOverlayImage(i, &iSysImageIndex)) && (iSysImageIndex != -1)) { int iMap; for (iMap = 0; iMap < i - 1; iMap++) { if (rgOverlay[iMap].iSystemImage == iSysImageIndex) break; }
if (iMap == (i - 1)) // We haven't used this System Image yet
{ HBITMAP hbmOverlay = NULL; HBITMAP hbmMask = NULL; if (SUCCEEDED(_CreateOverlayThumbnail(iSysImageIndex, &hbmOverlay, &hbmMask)) && hbmOverlay && hbmMask) { IMAGECACHEINFO rgInfo = {0}; int iThumbImageIndex; rgInfo.cbSize = sizeof(rgInfo); rgInfo.dwMask = ICIFLAG_SYSTEM | ICIFLAG_LARGE | ICIFLAG_BITMAP; rgInfo.hBitmapLarge = hbmOverlay; rgInfo.hMaskLarge = hbmMask;
if (IS_WINDOW_RTL_MIRRORED(_hwndListview)) rgInfo.dwMask |= ICIFLAG_MIRROR;
if (SUCCEEDED(_SafeAddImage(TRUE, &rgInfo, (UINT*)&iThumbImageIndex, -1))) { ImageList_SetOverlayImage(rgInitInfo.himlLarge, iThumbImageIndex, i); rgOverlay[iMap].iSystemImage = iSysImageIndex; rgOverlay[iMap].iThumbnailImage = iThumbImageIndex; } else { rgOverlay[i - 1].iSystemImage = -1; // failed to add the image
ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i); } } else { rgOverlay[i - 1].iSystemImage = -1; // failed to import htis image
ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i); } if (hbmOverlay) { DeleteObject(hbmOverlay); } if (hbmMask) { DeleteObject(hbmMask); } } else { ImageList_SetOverlayImage(rgInitInfo.himlLarge, rgOverlay[iMap].iThumbnailImage, i); rgOverlay[i - 1].iSystemImage = -1; // image already shows up in list
} } else { rgOverlay[i - 1].iSystemImage = -1; // Didn't find a system image
ImageList_SetOverlayImage(rgInitInfo.himlLarge, -1, i); } } } } } } } void CDefView::_ResetThumbview() { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_BORDERSELECT, 0);
if (_fs.fFlags & FWF_HIDEFILENAMES) ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_HIDELABELS, 0);
if (_dwRecClrDepth <= 8) { ListView_SetExtendedListViewStyleEx(_hwndListview, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); }
ListView_SetIconSpacing(_hwndListview, -1, -1); _SetSysImageList();
if (_IsOwnerData()) _ThumbnailMapClear(); }
HRESULT CDefView::_GetDefaultTypeExtractor(LPCITEMIDLIST pidl, IExtractImage **ppExt) { IAssociationArray * paa; HRESULT hr = _pshf->GetUIObjectOf(NULL, 1, &pidl, IID_X_PPV_ARG(IAssociationArray, NULL, &paa)); if (SUCCEEDED(hr)) { LPWSTR psz; hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, AQN_NAMED_VALUE, L"Thumbnail", &psz); if (SUCCEEDED(hr)) { LPITEMIDLIST pidlThumb; hr = SHILCreateFromPath(psz, &pidlThumb, NULL); if (SUCCEEDED(hr)) { SHGetUIObjectFromFullPIDL(pidlThumb, NULL, IID_PPV_ARG(IExtractImage, ppExt)); ILFree(pidlThumb); } CoTaskMemFree(psz); } paa->Release(); } return hr; }
struct ThumbMapNode { int iIndex; LPITEMIDLIST pidl;
~ThumbMapNode() { ILFree(pidl); } };
int CDefView::_MapIndexPIDLToID(int iIndex, LPCITEMIDLIST pidl) { int ret = -1; if (_IsOwnerData()) { int cNodes = DPA_GetPtrCount(_dpaThumbnailMap); int iNode = 0; for (; iNode < cNodes; iNode++) { ThumbMapNode* pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iNode); ASSERT(pNode); if (pNode->iIndex == iIndex) { if (!(_pshf->CompareIDs(0, pidl, pNode->pidl))) // 99 percent of the time we are good
{ ret = iNode; } else // Someone moved our pidl!
{ int iNodeStop = iNode; for (iNode = (iNode + 1) % cNodes; iNode != iNodeStop; iNode = (iNode + 1) % cNodes) { pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iNode); if (!(_pshf->CompareIDs(0, pidl, pNode->pidl))) { ret = iNode; pNode->iIndex = iIndex; // Newer index for pidl
break; } } } break; } } if (ret == -1) { ThumbMapNode* pNode = new ThumbMapNode; if (pNode) { pNode->iIndex = iIndex; pNode->pidl = ILClone(pidl); ret = DPA_AppendPtr(_dpaThumbnailMap, pNode); if (ret == -1) { delete pNode; } } } } else { ret = ListView_MapIndexToID(_hwndListview, iIndex); } return ret; }
int CDefView::_MapIDToIndex(int iID) { int ret = -1; if (_IsOwnerData()) { ThumbMapNode* pNode = (ThumbMapNode*) DPA_GetPtr(_dpaThumbnailMap, iID); if (pNode) { ret = pNode->iIndex; } } else { ret = ListView_MapIDToIndex(_hwndListview, iID); } return ret; }
void CDefView::_ThumbnailMapInit() { if (_dpaThumbnailMap) { _ThumbnailMapClear(); } else { _dpaThumbnailMap = DPA_Create(1); } }
void CDefView::_ThumbnailMapClear() { if (_dpaThumbnailMap) { int i = DPA_GetPtrCount(_dpaThumbnailMap); while (--i >= 0) { ThumbMapNode* pNode = (ThumbMapNode*) DPA_FastGetPtr(_dpaThumbnailMap, i); delete pNode; } DPA_DeleteAllPtrs(_dpaThumbnailMap); } }
HRESULT CDefView::_GetBrowserPalette(HPALETTE* phpal) { HRESULT hr = E_UNEXPECTED; if (_psb) { IBrowserService *pbs; hr = _psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)); if (SUCCEEDED(hr)) { hr = pbs->GetPalette(phpal); pbs->Release(); } }
return hr; }
|