Probably. Writing a shell extension(!) to get drag images work would be the very last option I'd choose. It sounds like much work, not only because shell extensions often get nasty, too.
Here's the C++ code that I'm using in ExplorerListView. It doesn't send NM_CUSTOMDRAW notifications to the parent window, so it doesn't support custom draw. But since it draws the whole drag image itself, it's easy to customize the item appearance within the drag image.
Code: Select all
BOOL ExplorerListView::CreateLegacyOLEDragImage(IListViewItemContainer* pItems, LPSHDRAGIMAGE pDragImage)
{
ATLASSUME(pItems);
ATLASSERT_POINTER(pDragImage, SHDRAGIMAGE);
BOOL succeeded = FALSE;
// use a normal legacy drag image as base
OLE_HANDLE h = NULL;
OLE_XPOS_PIXELS xUpperLeft = 0;
OLE_YPOS_PIXELS yUpperLeft = 0;
pItems->CreateDragImage(&xUpperLeft, &yUpperLeft, &h);
if(h) {
HIMAGELIST hImageList = (HIMAGELIST) LongToHandle(h);
// retrieve the drag image's size
int bitmapHeight;
int bitmapWidth;
::ImageList_GetIconSize(hImageList, &bitmapWidth, &bitmapHeight);
pDragImage->sizeDragImage.cx = bitmapWidth;
pDragImage->sizeDragImage.cy = bitmapHeight;
WTL::CDC memoryDC;
memoryDC.CreateCompatibleDC();
pDragImage->hbmpDragImage = NULL;
if(IsComctl32Version600OrNewer()) {
// handle alpha channel
IImageList* pImgLst = NULL;
HMODULE hMod = ::LoadLibrary(TEXT("comctl32.dll"));
if(hMod) {
typedef HRESULT WINAPI HIMAGELIST_QueryInterfaceFn(HIMAGELIST, REFIID, void**);
HIMAGELIST_QueryInterfaceFn* pfnHIMAGELIST_QueryInterface = (HIMAGELIST_QueryInterfaceFn*) GetProcAddress(hMod, "HIMAGELIST_QueryInterface");
if(pfnHIMAGELIST_QueryInterface) {
pfnHIMAGELIST_QueryInterface(hImageList, IID_IImageList, reinterpret_cast<void**>(&pImgLst));
}
::FreeLibrary(hMod);
}
if(!pImgLst) {
pImgLst = reinterpret_cast<IImageList*>(hImageList);
pImgLst->AddRef();
}
ATLASSUME(pImgLst);
DWORD flags = 0;
pImgLst->GetItemFlags(0, &flags);
if(flags & ILIF_ALPHA) {
// the drag image makes use of the alpha channel
IMAGEINFO imageInfo = {0};
::ImageList_GetImageInfo(hImageList, 0, &imageInfo);
// fetch raw data
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = pDragImage->sizeDragImage.cx;
bitmapInfo.bmiHeader.biHeight = -pDragImage->sizeDragImage.cy;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
LPRGBQUAD pSourceBits = (LPRGBQUAD) malloc(pDragImage->sizeDragImage.cx * pDragImage->sizeDragImage.cy * sizeof(RGBQUAD));
::GetDIBits(memoryDC, imageInfo.hbmImage, 0, pDragImage->sizeDragImage.cy, pSourceBits, &bitmapInfo, DIB_RGB_COLORS);
// create target bitmap
LPRGBQUAD pDragImageBits = NULL;
pDragImage->hbmpDragImage = ::CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
pDragImage->crColorKey = 0xFFFFFFFF;
// transfer raw data
memcpy(pDragImageBits, pSourceBits, pDragImage->sizeDragImage.cx * pDragImage->sizeDragImage.cy * 4);
// clean up
SECUREFREE(pSourceBits);
::DeleteObject(imageInfo.hbmImage);
::DeleteObject(imageInfo.hbmMask);
}
pImgLst->Release();
}
if(pDragImage->hbmpDragImage == NULL) {
// fallback mode
memoryDC.SetBkMode(TRANSPARENT);
// create target bitmap
HDC hCompatibleDC = ::GetDC(NULL);
pDragImage->hbmpDragImage = ::CreateCompatibleBitmap(hCompatibleDC, bitmapWidth, bitmapHeight);
::ReleaseDC(NULL, hCompatibleDC);
HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(pDragImage->hbmpDragImage);
// draw target bitmap
pDragImage->crColorKey = RGB(0xF4, 0x00, 0x00);
WTL::CBrush backroundBrush;
backroundBrush.CreateSolidBrush(pDragImage->crColorKey);
memoryDC.FillRect(WTL::CRect(0, 0, bitmapWidth, bitmapHeight), backroundBrush);
::ImageList_Draw(hImageList, 0, memoryDC, 0, 0, ILD_NORMAL);
// clean up
memoryDC.SelectBitmap(hPreviousBitmap);
}
::ImageList_Destroy(hImageList);
if(pDragImage->hbmpDragImage != NULL) {
// retrieve the offset
DWORD position = ::GetMessagePos();
POINT mousePosition = {GET_X_LPARAM(position), GET_Y_LPARAM(position)};
ScreenToClient(&mousePosition);
if(GetExStyle() & WS_EX_LAYOUTRTL) {
pDragImage->ptOffset.x = xUpperLeft + pDragImage->sizeDragImage.cx - mousePosition.x;
} else {
pDragImage->ptOffset.x = mousePosition.x - xUpperLeft;
}
pDragImage->ptOffset.y = mousePosition.y - yUpperLeft;
succeeded = TRUE;
}
}
return succeeded;
}
// handles DI_GETDRAGIMAGE
LRESULT ExplorerListView::OnGetDragImage(UINT /*message*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& wasHandled)
{
BOOL succeeded = FALSE;
if(dragDropStatus.pDraggedItems) {
succeeded = CreateLegacyOLEDragImage(dragDropStatus.pDraggedItems, reinterpret_cast<LPSHDRAGIMAGE>(lParam));
}
wasHandled = succeeded;
return succeeded;
}
This method creates drag images for each dragged item and combines them to one large bitmap.
Code: Select all
STDMETHODIMP ListViewItemContainer::CreateDragImage(OLE_XPOS_PIXELS* pXUpperLeft/* = NULL*/, OLE_YPOS_PIXELS* pYUpperLeft/* = NULL*/, OLE_HANDLE* phImageList/* = NULL*/)
{
ATLASSERT_POINTER(phImageList, OLE_HANDLE);
if(phImageList == NULL) {
return E_POINTER;
}
*phImageList = NULL;
#ifdef USE_STL
switch(properties.items.size()) {
#else
//switch(properties.items.GetCount()) {
switch(items.GetCount()) {
#endif
case 0:
return S_OK;
break;
case 1: {
ATLASSUME(properties.pOwnerExLvw);
//int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[0]);
#ifdef USE_STL
int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[0]);
#else
int itemIndex = properties.pOwnerExLvw->IDToItemIndex(items[0]);
#endif
if(itemIndex != -1) {
POINT upperLeftPoint = {0};
*phImageList = HandleToLong(properties.pOwnerExLvw->CreateLegacyDragImage(itemIndex, &upperLeftPoint, NULL));
if(*phImageList) {
if(pXUpperLeft) {
*pXUpperLeft = upperLeftPoint.x;
}
if(pYUpperLeft) {
*pYUpperLeft = upperLeftPoint.y;
}
return S_OK;
}
}
break;
}
default: {
// create a large drag image out of small drag images
ATLASSUME(properties.pOwnerExLvw);
BOOL use32BPPImage = properties.pOwnerExLvw->IsComctl32Version600OrNewer();
// calculate the bitmap's required size and collect each item's imagelist
#ifdef USE_STL
std::vector< HIMAGELIST > imageLists;
std::vector< RECT > itemBoundingRects;
#else
CAtlArray< HIMAGELIST > imageLists;
CAtlArray< RECT > itemBoundingRects;
#endif
POINT upperLeftPoint = {0};
WTL::CRect boundingRect;
#ifdef USE_STL
for(std::vector< LONG >::iterator iter = properties.items.begin(); iter != properties.items.end(); ++iter) {
int itemIndex = properties.pOwnerExLvw->IDToItemIndex(*iter);
#else
//for(size_t i = 0; i < properties.items.GetCount(); ++i) {
for(size_t i = 0; i < items.GetCount(); ++i) {
//int itemIndex = properties.pOwnerExLvw->IDToItemIndex(properties.items[i]);
int itemIndex = properties.pOwnerExLvw->IDToItemIndex(items[i]);
#endif
if(itemIndex != -1) {
// NOTE: Windows skips items outside the client area to improve performance. We don't.
POINT pt = {0};
RECT itemBoundingRect = {0};
HIMAGELIST hImageList = properties.pOwnerExLvw->CreateLegacyDragImage(itemIndex, &pt, &itemBoundingRect);
boundingRect.UnionRect(&boundingRect, &itemBoundingRect);
#ifdef USE_STL
if(imageLists.size() == 0) {
#else
if(imageLists.GetCount() == 0) {
#endif
upperLeftPoint = pt;
} else {
upperLeftPoint.x = min(upperLeftPoint.x, pt.x);
upperLeftPoint.y = min(upperLeftPoint.y, pt.y);
}
#ifdef USE_STL
imageLists.push_back(hImageList);
itemBoundingRects.push_back(itemBoundingRect);
#else
imageLists.Add(hImageList);
itemBoundingRects.Add(itemBoundingRect);
#endif
}
}
WTL::CRect dragImageRect(0, 0, boundingRect.Width(), boundingRect.Height());
// setup the DCs we'll draw into
HDC hCompatibleDC = ::GetDC(NULL);
WTL::CDC memoryDC;
memoryDC.CreateCompatibleDC(hCompatibleDC);
WTL::CDC maskMemoryDC;
maskMemoryDC.CreateCompatibleDC(hCompatibleDC);
// create the bitmap and its mask
WTL::CBitmap dragImage;
LPRGBQUAD pDragImageBits = NULL;
if(use32BPPImage) {
BITMAPINFO bitmapInfo = {0};
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dragImageRect.Width();
bitmapInfo.bmiHeader.biHeight = -dragImageRect.Height();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
dragImage.CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
} else {
dragImage.CreateCompatibleBitmap(hCompatibleDC, dragImageRect.Width(), dragImageRect.Height());
}
HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(dragImage);
memoryDC.FillRect(&dragImageRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
WTL::CBitmap dragImageMask;
dragImageMask.CreateBitmap(dragImageRect.Width(), dragImageRect.Height(), 1, 1, NULL);
HBITMAP hPreviousBitmapMask = maskMemoryDC.SelectBitmap(dragImageMask);
maskMemoryDC.FillRect(&dragImageRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
// draw each single drag image into our bitmap
BOOL rightToLeft = FALSE;
HWND hWndLvw = properties.GetExLvwHWnd();
if(::IsWindow(hWndLvw)) {
rightToLeft = ((CWindow(hWndLvw).GetExStyle() & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL);
}
#ifdef USE_STL
for(size_t i = 0; i < imageLists.size(); ++i) {
#else
for(size_t i = 0; i < imageLists.GetCount(); ++i) {
#endif
if(rightToLeft) {
::ImageList_Draw(imageLists[i], 0, memoryDC, boundingRect.right - itemBoundingRects[i].right, itemBoundingRects[i].top - boundingRect.top, ILD_NORMAL);
::ImageList_Draw(imageLists[i], 0, maskMemoryDC, boundingRect.right - itemBoundingRects[i].right, itemBoundingRects[i].top - boundingRect.top, ILD_MASK);
} else {
::ImageList_Draw(imageLists[i], 0, memoryDC, itemBoundingRects[i].left - boundingRect.left, itemBoundingRects[i].top - boundingRect.top, ILD_NORMAL);
::ImageList_Draw(imageLists[i], 0, maskMemoryDC, itemBoundingRects[i].left - boundingRect.left, itemBoundingRects[i].top - boundingRect.top, ILD_MASK);
}
::ImageList_Destroy(imageLists[i]);
}
// clean up
#ifdef USE_STL
imageLists.clear();
itemBoundingRects.clear();
#else
imageLists.RemoveAll();
itemBoundingRects.RemoveAll();
#endif
memoryDC.SelectBitmap(hPreviousBitmap);
maskMemoryDC.SelectBitmap(hPreviousBitmapMask);
::ReleaseDC(NULL, hCompatibleDC);
// create the imagelist
HIMAGELIST hImageList = ::ImageList_Create(dragImageRect.Width(), dragImageRect.Height(), (use32BPPImage ? ILC_COLOR32 : ILC_COLOR24) | ILC_MASK, 1, 0);
::ImageList_SetBkColor(hImageList, CLR_NONE);
::ImageList_Add(hImageList, dragImage, dragImageMask);
*phImageList = HandleToLong((HIMAGELIST) hImageList);
if(*phImageList) {
if(pXUpperLeft) {
*pXUpperLeft = upperLeftPoint.x;
}
if(pYUpperLeft) {
*pYUpperLeft = upperLeftPoint.y;
}
return S_OK;
}
break;
}
}
return E_FAIL;
}
The last method missing is CreateLegacyDragImage, which creates a drag image for a single item. This is the one that should be edited to customize the appearance of the drag image.
Code: Select all
HIMAGELIST ExplorerListView::CreateLegacyDragImage(int itemIndex, POINT* pUpperLeftPoint, RECT* pBoundingRectangle)
{
/********************************************************************************************************
* Known problems: *
* - We assume that the selection background goes over icon and label for all themes. *
* - We use hardcoded margins. *
* - Extended Tile View is not fully supported (displaying the details in two columns doesn't work). *
********************************************************************************************************/
// retrieve item details
LVITEM item = {0};
item.iItem = itemIndex;
item.cchTextMax = MAX_ITEMTEXTLENGTH;
LPTSTR pItemTextBuffer = (LPTSTR) malloc((item.cchTextMax + 1) * sizeof(TCHAR));
item.pszText = pItemTextBuffer;
item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_TEXT;
item.stateMask = LVIS_FOCUSED | LVIS_SELECTED | LVIS_OVERLAYMASK;
SendMessage(LVM_GETITEM, 0, (LPARAM) &item);
if(item.pszText == NULL) {
item.pszText = TEXT("");
}
BOOL itemIsSelected = ((item.state & LVIS_SELECTED) == LVIS_SELECTED);
BOOL itemIsFocused = ((item.state & LVIS_FOCUSED) == LVIS_FOCUSED);
int iconToDraw = item.iImage;
CComPtr< IListViewItem > pItem = NULL;
VARIANT vTileViewColumns;
::VariantInit(&vTileViewColumns);
int lines = 0;
int maxDetailLinesBySettings = 0;
int numberOfMainTextLines = 0;
int lineHeight = 0;
LVTILEVIEWINFO tileViewInfo = {0};
int columnCount = 0;
RECT* pSubItemImageRectangles = NULL;
BOOL* pSubItemImageHasAlpha = NULL;
// retrieve window details
BOOL hasFocus = (::GetFocus() == m_hWnd);
DWORD style = GetExStyle();
DWORD textDrawStyle = DT_EDITCONTROL | DT_NOPREFIX;
if(style & WS_EX_RTLREADING) {
textDrawStyle |= DT_RTLREADING;
}
BOOL layoutRTL = ((style & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL);
DWORD extendedStyle = (DWORD) SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
BOOL fullRowSelect = ((extendedStyle & LVS_EX_FULLROWSELECT) == LVS_EX_FULLROWSELECT);
ViewConstants currentView = vIcons;
get_View(¤tView);
HIMAGELIST hSourceImageList = NULL;
switch(currentView) {
case vDetails:
hSourceImageList = cachedSettings.hSmallImageList;
textDrawStyle |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
break;
case vIcons:
hSourceImageList = cachedSettings.hLargeImageList;
textDrawStyle |= DT_CENTER | DT_WORDBREAK | DT_END_ELLIPSIS;
break;
case vList:
hSourceImageList = cachedSettings.hSmallImageList;
textDrawStyle |= DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS;
break;
case vSmallIcons:
hSourceImageList = cachedSettings.hSmallImageList;
textDrawStyle |= DT_SINGLELINE;
break;
case vTiles:
hSourceImageList = cachedSettings.hExtraLargeImageList;
textDrawStyle |= DT_END_ELLIPSIS;
break;
case vExtendedTiles:
hSourceImageList = cachedSettings.hExtraLargeImageList;
textDrawStyle |= DT_END_ELLIPSIS;
break;
}
SIZE imageSize = {0};
BOOL subItemImages = FALSE;
if(hSourceImageList) {
if(currentView == vDetails) {
subItemImages = ((extendedStyle & LVS_EX_SUBITEMIMAGES) == LVS_EX_SUBITEMIMAGES);
}
::ImageList_GetIconSize(hSourceImageList, (int*) &imageSize.cx, (int*) &imageSize.cy);
}
// create the DCs we'll draw into
HDC hCompatibleDC = GetDC();
WTL::CDC memoryDC;
memoryDC.CreateCompatibleDC(hCompatibleDC);
WTL::CDC maskMemoryDC;
maskMemoryDC.CreateCompatibleDC(hCompatibleDC);
WTL::CFontHandle font = (HFONT) SendMessage(WM_GETFONT, 0, 0);
HFONT hPreviousFont = NULL;
if(!font.IsNull()) {
hPreviousFont = memoryDC.SelectFont(font);
}
// prepare themed item drawing
CTheme themingEngine;
BOOL themedListItems = FALSE;
int themeState = LISS_NORMAL;
if(itemIsSelected) {
if(hasFocus) {
themeState = LISS_SELECTED;
} else {
themeState = LISS_SELECTEDNOTFOCUS;
}
}
if(flags.usingThemes) {
themingEngine.OpenThemeData(*this, VSCLASS_LISTVIEW);
/* We use LISS_SELECTED here, because it's more likely this one is defined and we don't want a mixture
of themed and non-themed items in drag images. What we're doing with LISS_NORMAL should work
regardless whether LISS_NORMAL is defined. */
themedListItems = themingEngine.IsThemePartDefined(LVP_LISTITEM, LISS_SELECTED/*themeState*/);
if(themedListItems) {
if(currentView == vSmallIcons) {
textDrawStyle |= DT_VCENTER;
}
}
}
// calculate the bounding rectangles of the various item parts
WTL::CRect itemBoundingRect;
WTL::CRect selectionBoundingRect;
WTL::CRect focusRect;
WTL::CRect labelBoundingRect;
WTL::CRect iconAreaBoundingRect;
WTL::CRect iconBoundingRect;
labelBoundingRect.left = LVIR_LABEL;
SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &labelBoundingRect);
selectionBoundingRect.left = LVIR_SELECTBOUNDS;
SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &selectionBoundingRect);
itemBoundingRect = selectionBoundingRect;
if(!themedListItems) {
if(currentView == vList) {
// the width of labelBoundingRect may be wrong
if(lstrlen(item.pszText) > 0) {
WTL::CRect rc = labelBoundingRect;
memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_CALCRECT);
// TODO: Don't use hard-coded margins
labelBoundingRect.right = labelBoundingRect.left + rc.Width() + 4;
}
}
}
if((currentView == vTiles) || (currentView == vExtendedTiles)) {
// the height of labelBoundingRect may be wrong
// calculate the line height
WTL::CRect rc;
rc.SetRect(0, 0, 30, 200000);
LPTSTR pDummy = TEXT("A\r\nB");
// TODO: DrawThemeText() doesn't set the rectangle's height
/*if(themedListItems) {
CT2W converter(pDummy);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_CALCRECT, 0, &rc);
} else {*/
memoryDC.DrawText(pDummy, lstrlen(pDummy), &rc, textDrawStyle | DT_WORDBREAK | DT_CALCRECT);
//}
lineHeight = rc.Height() / 2;
// retrieve the maximum number of lines
tileViewInfo.cbSize = sizeof(LVTILEVIEWINFO);
tileViewInfo.dwMask = LVTVIM_COLUMNS | LVTVIM_TILESIZE;
SendMessage(LVM_GETTILEVIEWINFO, 0, (LPARAM) &tileViewInfo);
// retrieve the number of sub-items to display
pItem = ClassFactory::InitListItem(itemIndex, this, false);
ATLASSUME(pItem);
ATLVERIFY(SUCCEEDED(pItem->get_TileViewColumns(&vTileViewColumns)));
ATLASSERT(vTileViewColumns.vt & VT_ARRAY);
maxDetailLinesBySettings = 0;
if(vTileViewColumns.parray) {
LONG l = 0;
::SafeArrayGetLBound(vTileViewColumns.parray, 1, &l);
LONG u = 0;
::SafeArrayGetUBound(vTileViewColumns.parray, 1, &u);
maxDetailLinesBySettings = min(u - l + 1, tileViewInfo.cLines);
}
// count the lines
numberOfMainTextLines = 1;
lines = 1;
CAtlString itemText = item.pszText;
// the item's main text may consist of up to two lines, so check this
if(itemText.Find(TEXT("\r\n")) != -1) {
++lines;
++numberOfMainTextLines;
}
int maxLines = min(maxDetailLinesBySettings + numberOfMainTextLines, tileViewInfo.cLines + 1);
if(currentView == vExtendedTiles) {
int maxLinesByHeight = labelBoundingRect.Height() / lineHeight;
maxLines = min(maxLines, maxLinesByHeight);
}
LVITEM subItem = {0};
subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
for(LONG line = 0; line < maxDetailLinesBySettings; ++line) {
subItem.pszText = pSubItemTextBuffer;
ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
if(subItem.pszText) {
if(lstrlen(subItem.pszText) > 0) {
++lines;
}
}
}
lines = min(lines, maxLines);
// calculate the real height of labelBoundingRect
if((currentView == vExtendedTiles) && !themedListItems) {
labelBoundingRect.bottom = selectionBoundingRect.bottom;
} else {
labelBoundingRect.bottom = labelBoundingRect.top + lines * lineHeight;
}
if(!themedListItems && (currentView != vExtendedTiles)) {
if(((tileViewInfo.dwFlags & LVTVIF_FIXEDWIDTH) == 0) && !fullRowSelect) {
// the width may be wrong, too
int remainingLines = lines;
WTL::CRect textAreaRect;
rc = labelBoundingRect;
rc.bottom = rc.top + lineHeight;
if((numberOfMainTextLines == 1) || (remainingLines == 1)) {
memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE | DT_CALCRECT);
textAreaRect.UnionRect(&textAreaRect, &rc);
--remainingLines;
} else {
int i = itemText.Find(TEXT("\r\n"));
ATLASSERT(i > 0);
CAtlString firstLine = itemText.Left(i);
CAtlString secondLine = itemText.Mid(i + 2);
memoryDC.DrawText(firstLine, lstrlen(firstLine), &rc, textDrawStyle | DT_WORDBREAK | DT_CALCRECT);
textAreaRect.UnionRect(&textAreaRect, &rc);
--remainingLines;
rc = labelBoundingRect;
rc.top = labelBoundingRect.top + lineHeight;
rc.bottom = rc.top + lineHeight;
memoryDC.DrawText(secondLine, lstrlen(secondLine), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE | DT_CALCRECT);
textAreaRect.UnionRect(&textAreaRect, &rc);
--remainingLines;
}
int numberOfDetailLines = min(remainingLines, maxDetailLinesBySettings);
int nonEmptyLine = 0;
for(LONG line = 0; nonEmptyLine < numberOfDetailLines; ++line) {
subItem.pszText = pSubItemTextBuffer;
ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
if(subItem.pszText) {
if(lstrlen(subItem.pszText) > 0) {
rc = labelBoundingRect;
rc.top = labelBoundingRect.top + (lines - remainingLines) * lineHeight;
rc.bottom = rc.top + lineHeight;
memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &rc, textDrawStyle | DT_SINGLELINE | DT_CALCRECT);
textAreaRect.UnionRect(&textAreaRect, &rc);
--remainingLines;
++nonEmptyLine;
}
}
}
labelBoundingRect.right = textAreaRect.right;
}
}
SECUREFREE(pSubItemTextBuffer);
}
if((currentView != vIcons) && (currentView != vExtendedTiles)) {
// center the label rectangle vertically
int cy = labelBoundingRect.Height();
labelBoundingRect.top = itemBoundingRect.top + (itemBoundingRect.Height() - cy) / 2;
labelBoundingRect.bottom = labelBoundingRect.top + cy;
}
if(themedListItems) {
if(currentView == vDetails) {
if(!fullRowSelect) {
// TODO: Don't use hard-coded margins
selectionBoundingRect.right -= 2;
labelBoundingRect.right = min(labelBoundingRect.right, selectionBoundingRect.right);
}
}
} else {
// selectionBoundingRect includes the icon, so correct it
if(currentView == vDetails) {
// labelBoundingRect covers the entire first column and nothing more
selectionBoundingRect.left = labelBoundingRect.left;
if(!fullRowSelect) {
labelBoundingRect.right = selectionBoundingRect.right;
}
} else {
selectionBoundingRect = labelBoundingRect;
}
}
if(hSourceImageList) {
iconAreaBoundingRect.left = LVIR_ICON;
SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &iconAreaBoundingRect);
if(themedListItems) {
if(currentView == vSmallIcons) {
// TODO: Don't use hard-coded margins
iconAreaBoundingRect.left += 4;
}
}
if((currentView == vTiles) || (currentView == vExtendedTiles)) {
if(iconAreaBoundingRect.Height() < labelBoundingRect.Height()) {
iconAreaBoundingRect.top = labelBoundingRect.top;
iconAreaBoundingRect.bottom = labelBoundingRect.bottom;
}
// TODO: Don't use hard-coded margins
iconAreaBoundingRect.right = labelBoundingRect.left - 4;
iconAreaBoundingRect.left = iconAreaBoundingRect.right - imageSize.cx;
}
if(currentView != vIcons) {
// center the icon area rectangle vertically
int cy = iconAreaBoundingRect.Height();
iconAreaBoundingRect.top = itemBoundingRect.top + (itemBoundingRect.Height() - cy) / 2;
iconAreaBoundingRect.bottom = iconAreaBoundingRect.top + cy;
}
iconBoundingRect = iconAreaBoundingRect;
if(currentView == vIcons) {
// center the icon horizontally
// TODO: Don't use hard-coded margins
iconBoundingRect.top += 2;
iconBoundingRect.left += (iconAreaBoundingRect.Width() - imageSize.cx) / 2;
iconBoundingRect.bottom = iconBoundingRect.top + imageSize.cy;
iconBoundingRect.right = iconBoundingRect.left + imageSize.cx;
} else {
// center the icon vertically
iconBoundingRect.top += (iconAreaBoundingRect.Height() - imageSize.cy) / 2;
iconBoundingRect.bottom = iconBoundingRect.top + imageSize.cy;
iconBoundingRect.right = iconBoundingRect.left + imageSize.cx;
}
}
itemBoundingRect.left = LVIR_BOUNDS;
SendMessage(LVM_GETITEMRECT, (WPARAM) itemIndex, (LPARAM) &itemBoundingRect);
if(hSourceImageList) {
itemBoundingRect.UnionRect(&itemBoundingRect, &iconAreaBoundingRect);
}
itemBoundingRect.UnionRect(&itemBoundingRect, &labelBoundingRect);
focusRect = selectionBoundingRect;
if(pBoundingRectangle) {
*pBoundingRectangle = itemBoundingRect;
}
// calculate drag image size and upper-left corner
SIZE dragImageSize = {0};
if(pUpperLeftPoint) {
pUpperLeftPoint->x = itemBoundingRect.left;
pUpperLeftPoint->y = itemBoundingRect.top;
}
dragImageSize.cx = itemBoundingRect.Width();
dragImageSize.cy = itemBoundingRect.Height();
// offset RECTs
SIZE offset = {0};
offset.cx = itemBoundingRect.left;
offset.cy = itemBoundingRect.top;
labelBoundingRect.OffsetRect(-offset.cx, -offset.cy);
iconAreaBoundingRect.OffsetRect(-offset.cx, -offset.cy);
iconBoundingRect.OffsetRect(-offset.cx, -offset.cy);
selectionBoundingRect.OffsetRect(-offset.cx, -offset.cy);
itemBoundingRect.OffsetRect(-offset.cx, -offset.cy);
// setup the DCs we'll draw into
if(itemIsSelected) {
memoryDC.SetBkColor(::GetSysColor((hasFocus ? COLOR_HIGHLIGHT : COLOR_BTNFACE)));
memoryDC.SetTextColor(::GetSysColor((hasFocus ? COLOR_HIGHLIGHTTEXT : COLOR_BTNTEXT)));
} else {
memoryDC.SetBkColor(::GetSysColor(COLOR_WINDOW));
memoryDC.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
memoryDC.SetBkMode(TRANSPARENT);
}
// create drag image bitmap
/* NOTE: We prefer creating 32bpp drag images, because this improves performance of
ListViewItemContainer::CreateDragImage(). */
BOOL doAlphaChannelProcessing = IsComctl32Version600OrNewer();
BITMAPINFO bitmapInfo = {0};
if(doAlphaChannelProcessing) {
bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitmapInfo.bmiHeader.biWidth = dragImageSize.cx;
bitmapInfo.bmiHeader.biHeight = -dragImageSize.cy;
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = BI_RGB;
}
WTL::CBitmap dragImage;
LPRGBQUAD pDragImageBits = NULL;
if(doAlphaChannelProcessing) {
dragImage.CreateDIBSection(NULL, &bitmapInfo, DIB_RGB_COLORS, reinterpret_cast<void**>(&pDragImageBits), NULL, 0);
} else {
dragImage.CreateCompatibleBitmap(hCompatibleDC, dragImageSize.cx, dragImageSize.cy);
}
HBITMAP hPreviousBitmap = memoryDC.SelectBitmap(dragImage);
WTL::CBitmap dragImageMask;
dragImageMask.CreateBitmap(dragImageSize.cx, dragImageSize.cy, 1, 1, NULL);
HBITMAP hPreviousBitmapMask = maskMemoryDC.SelectBitmap(dragImageMask);
// initialize the bitmap
if(themedListItems) {
// we need a transparent background
LPRGBQUAD pPixel = pDragImageBits;
for(int y = 0; y < dragImageSize.cy; ++y) {
for(int x = 0; x < dragImageSize.cx; ++x, ++pPixel) {
pPixel->rgbRed = 0xFF;
pPixel->rgbGreen = 0xFF;
pPixel->rgbBlue = 0xFF;
pPixel->rgbReserved = 0x00;
}
}
} else {
memoryDC.FillRect(&itemBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
}
maskMemoryDC.FillRect(&itemBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
// draw the selection area's background
if(itemIsSelected) {
if(themedListItems) {
themingEngine.DrawThemeBackground(memoryDC, LVP_LISTITEM, themeState, &selectionBoundingRect, NULL);
} else {
memoryDC.FillRect(&selectionBoundingRect, (hasFocus ? COLOR_HIGHLIGHT : COLOR_BTNFACE));
}
maskMemoryDC.FillRect(&selectionBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
}
// draw the icon
if(hSourceImageList) {
::ImageList_DrawEx(hSourceImageList, iconToDraw, memoryDC, iconBoundingRect.left, iconBoundingRect.top, imageSize.cx, imageSize.cy, CLR_NONE, CLR_NONE, ILD_NORMAL | (item.state & LVIS_OVERLAYMASK));
if(itemIsSelected && themedListItems) {
maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
} else {
::ImageList_Draw(hSourceImageList, iconToDraw, maskMemoryDC, iconBoundingRect.left, iconBoundingRect.top, ILD_MASK | (item.state & LVIS_OVERLAYMASK));
}
}
// draw the text
WTL::CRect rc = labelBoundingRect;
if((currentView == vTiles) || (currentView == vExtendedTiles)) {
int remainingLines = lines;
rc.bottom = rc.top + lineHeight;
if((numberOfMainTextLines == 1) || (remainingLines == 1)) {
if(themedListItems) {
CT2W converter(item.pszText);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_SINGLELINE, 0, &rc);
} else {
memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE);
}
--remainingLines;
} else {
CAtlString itemText = item.pszText;
int i = itemText.Find(TEXT("\r\n"));
ATLASSERT(i > 0);
CAtlString firstLine = itemText.Left(i);
CAtlString secondLine = itemText.Mid(i + 2);
if(themedListItems) {
CT2W converter(firstLine);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK, 0, &rc);
} else {
memoryDC.DrawText(firstLine, lstrlen(firstLine), &rc, textDrawStyle | DT_WORDBREAK);
}
--remainingLines;
if(!itemIsSelected) {
COLORREF bkColor = memoryDC.GetBkColor();
for(int y = rc.top; y <= rc.bottom; ++y) {
for(int x = rc.left; x <= rc.right; ++x) {
if(memoryDC.GetPixel(x, y) != bkColor) {
maskMemoryDC.SetPixelV(x, y, 0x00000000);
}
}
}
}
rc = labelBoundingRect;
rc.top = labelBoundingRect.top + lineHeight;
rc.bottom = rc.top + lineHeight;
if(themedListItems) {
CT2W converter(secondLine);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_WORDBREAK | DT_SINGLELINE, 0, &rc);
} else {
memoryDC.DrawText(secondLine, lstrlen(secondLine), &rc, textDrawStyle | DT_WORDBREAK | DT_SINGLELINE);
}
--remainingLines;
}
if(!itemIsSelected) {
COLORREF bkColor = memoryDC.GetBkColor();
for(int y = rc.top; y <= rc.bottom; ++y) {
for(int x = rc.left; x <= rc.right; ++x) {
if(memoryDC.GetPixel(x, y) != bkColor) {
maskMemoryDC.SetPixelV(x, y, 0x00000000);
}
}
}
}
LVITEM subItem = {0};
subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
int numberOfDetailLines = min(remainingLines, maxDetailLinesBySettings);
int nonEmptyLine = 0;
for(LONG line = 0; nonEmptyLine < numberOfDetailLines; ++line) {
subItem.pszText = pSubItemTextBuffer;
ATLVERIFY(SUCCEEDED(::SafeArrayGetElement(vTileViewColumns.parray, &line, &subItem.iSubItem)));
SendMessage(LVM_GETITEMTEXT, (WPARAM) itemIndex, (LPARAM) &subItem);
if(subItem.pszText) {
if(lstrlen(subItem.pszText) > 0) {
rc = labelBoundingRect;
rc.top = labelBoundingRect.top + (lines - remainingLines) * lineHeight;
rc.bottom = rc.top + lineHeight;
if(themedListItems) {
CT2W converter(subItem.pszText);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle | DT_SINGLELINE, 0, &rc);
} else {
memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &rc, textDrawStyle | DT_SINGLELINE);
}
--remainingLines;
++nonEmptyLine;
if(!itemIsSelected) {
COLORREF bkColor = memoryDC.GetBkColor();
for(int y = rc.top; y <= rc.bottom; ++y) {
for(int x = rc.left; x <= rc.right; ++x) {
if(memoryDC.GetPixel(x, y) != bkColor) {
maskMemoryDC.SetPixelV(x, y, 0x00000000);
}
}
}
}
}
}
}
SECUREFREE(pSubItemTextBuffer);
} else {
if(currentView == vDetails) {
if(layoutRTL) {
// TODO: Don't use hard-coded margins
rc.OffsetRect(1, 0);
}
}
// TODO: Don't use hard-coded margins
rc.InflateRect(-2, 0);
if(themedListItems) {
CT2W converter(item.pszText);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle, 0, &rc);
} else {
memoryDC.DrawText(item.pszText, lstrlen(item.pszText), &rc, textDrawStyle);
}
if(!itemIsSelected) {
COLORREF bkColor = memoryDC.GetBkColor();
for(int y = rc.top; y <= rc.bottom; ++y) {
for(int x = rc.left; x <= rc.right; ++x) {
if(memoryDC.GetPixel(x, y) != bkColor) {
maskMemoryDC.SetPixelV(x, y, 0x00000000);
}
}
}
}
if((currentView == vDetails) && fullRowSelect) {
columnCount = (int) containedSysHeader32.SendMessage(HDM_GETITEMCOUNT, 0, 0);
pSubItemImageRectangles = new RECT[columnCount - 1];
pSubItemImageHasAlpha = new BOOL[columnCount - 1];
// draw the sub-items
LVITEM subItem = {0};
subItem.iItem = itemIndex;
subItem.cchTextMax = MAX_ITEMTEXTLENGTH;
LPTSTR pSubItemTextBuffer = (LPTSTR) malloc((subItem.cchTextMax + 1) * sizeof(TCHAR));
subItem.mask = LVIF_STATE | LVIF_TEXT;
if(subItemImages) {
subItem.mask |= LVIF_IMAGE;
}
subItem.stateMask = LVIS_OVERLAYMASK;
LVCOLUMN column = {0};
column.mask = LVCF_FMT;
HMODULE hMod = ::LoadLibrary(TEXT("comctl32.dll"));
typedef HRESULT WINAPI HIMAGELIST_QueryInterfaceFn(HIMAGELIST, REFIID, void**);
HIMAGELIST_QueryInterfaceFn* pfnHIMAGELIST_QueryInterface = NULL;
if(hMod) {
pfnHIMAGELIST_QueryInterface = (HIMAGELIST_QueryInterfaceFn*) GetProcAddress(hMod, "HIMAGELIST_QueryInterface");
}
for(int subItemIndex = 1; subItemIndex < columnCount; ++subItemIndex) {
// collect all the data we'll need
WTL::CRect subItemRect;
subItemRect.top = subItemIndex;
subItemRect.left = LVIR_BOUNDS;
SendMessage(LVM_GETSUBITEMRECT, (WPARAM) itemIndex, (LPARAM) &subItemRect);
subItemRect.OffsetRect(-offset.cx, -offset.cy);
subItem.pszText = pSubItemTextBuffer;
subItem.iSubItem = subItemIndex;
SendMessage(LVM_GETITEM, 0, (LPARAM) &subItem);
if(subItem.pszText == NULL) {
continue;
}
SendMessage(LVM_GETCOLUMN, (WPARAM) subItemIndex, (LPARAM) &column);
switch(column.fmt & LVCFMT_JUSTIFYMASK) {
case LVCFMT_LEFT:
textDrawStyle &= ~(DT_CENTER | DT_RIGHT);
textDrawStyle |= DT_LEFT;
break;
case LVCFMT_CENTER:
textDrawStyle &= ~(DT_LEFT | DT_RIGHT);
textDrawStyle |= DT_CENTER;
break;
case LVCFMT_RIGHT:
textDrawStyle &= ~(DT_LEFT | DT_CENTER);
textDrawStyle |= DT_RIGHT;
break;
}
// draw the icon
if(subItemImages) {
iconBoundingRect = subItemRect;
// TODO: Don't use hard-coded margins
iconBoundingRect.right = iconBoundingRect.left + imageSize.cx + 2;
if(IsComctl32Version600OrNewer()) {
pSubItemImageRectangles[subItemIndex - 1] = iconBoundingRect;
IImageList* pImgLst = NULL;
if(pfnHIMAGELIST_QueryInterface) {
pfnHIMAGELIST_QueryInterface(hSourceImageList, IID_IImageList, reinterpret_cast<void**>(&pImgLst));
} else {
pImgLst = reinterpret_cast<IImageList*>(hSourceImageList);
pImgLst->AddRef();
}
ATLASSUME(pImgLst);
DWORD flags = 0;
pImgLst->GetItemFlags(subItem.iImage, &flags);
pImgLst->Release();
pSubItemImageHasAlpha[subItemIndex - 1] = ((flags & ILIF_ALPHA) == ILIF_ALPHA);
}
// background
if(!themedListItems) {
memoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
}
maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(WHITE_BRUSH));
// icon
::ImageList_DrawEx(hSourceImageList, subItem.iImage, memoryDC, iconBoundingRect.left, iconBoundingRect.top, imageSize.cx, imageSize.cy, CLR_NONE, CLR_NONE, ILD_NORMAL | (subItem.state & LVIS_OVERLAYMASK));
if(itemIsSelected && themedListItems) {
maskMemoryDC.FillRect(&iconBoundingRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
} else {
::ImageList_Draw(hSourceImageList, subItem.iImage, maskMemoryDC, iconBoundingRect.left, iconBoundingRect.top, ILD_MASK | (subItem.state & LVIS_OVERLAYMASK));
}
// TODO: Don't use hard-coded margins
subItemRect.left = iconBoundingRect.right;
}
// draw the text
if(layoutRTL) {
// TODO: Don't use hard-coded margins
subItemRect.OffsetRect(1, 0);
}
// TODO: Don't use hard-coded margins
subItemRect.InflateRect(-2, 0);
switch(column.fmt & LVCFMT_JUSTIFYMASK) {
case LVCFMT_LEFT:
// TODO: Don't use hard-coded margins
subItemRect.left += 4;
break;
case LVCFMT_RIGHT:
// TODO: Don't use hard-coded margins
subItemRect.right -= 4;
break;
}
if(themedListItems) {
CT2W converter(subItem.pszText);
LPWSTR pLabelText = converter;
themingEngine.DrawThemeText(memoryDC, LVP_LISTITEM, themeState, pLabelText, lstrlenW(pLabelText), textDrawStyle, 0, &subItemRect);
} else {
memoryDC.DrawText(subItem.pszText, lstrlen(subItem.pszText), &subItemRect, textDrawStyle);
}
if(!itemIsSelected) {
COLORREF bkColor = memoryDC.GetBkColor();
for(int y = subItemRect.top; y <= subItemRect.bottom; ++y) {
for(int x = subItemRect.left; x <= subItemRect.right; ++x) {
if(memoryDC.GetPixel(x, y) != bkColor) {
maskMemoryDC.SetPixelV(x, y, 0x00000000);
}
}
}
}
}
if(hMod) {
::FreeLibrary(hMod);
}
SECUREFREE(pSubItemTextBuffer);
}
}
if(!themedListItems) {
// draw the focus rectangle
if(hasFocus && itemIsFocused) {
if((SendMessage(WM_QUERYUISTATE, 0, 0) & UISF_HIDEFOCUS) == 0) {
memoryDC.DrawFocusRect(&focusRect);
maskMemoryDC.FrameRect(&focusRect, (HBRUSH) ::GetStockObject(BLACK_BRUSH));
}
}
}
if(doAlphaChannelProcessing) {
// correct the alpha channel
LPRGBQUAD pPixel = pDragImageBits;
POINT pt;
for(pt.y = 0; pt.y < dragImageSize.cy; ++pt.y) {
for(pt.x = 0; pt.x < dragImageSize.cx; ++pt.x, ++pPixel) {
if(layoutRTL) {
// we're working on raw data, so we've to handle WS_EX_LAYOUTRTL ourselves
POINT pt2 = pt;
pt2.x = dragImageSize.cx - pt.x - 1;
if(maskMemoryDC.GetPixel(pt2.x, pt2.y) == 0x00000000) {
if(themedListItems) {
if(itemIsSelected) {
if((pPixel->rgbReserved != 0x00) || selectionBoundingRect.PtInRect(pt2)) {
pPixel->rgbReserved = 0xFF;
}
} else if(labelBoundingRect.PtInRect(pt2)) {
if(pPixel->rgbReserved == 0x00) {
pPixel->rgbReserved = 0xFF;
}
}
} else {
// items are not themed
if(itemIsSelected) {
if((pPixel->rgbReserved == 0x00) || selectionBoundingRect.PtInRect(pt2)) {
if(subItemImages) {
BOOL changeAlpha = TRUE;
for(int i = 0; i < columnCount - 1; ++i) {
if(::PtInRect(&pSubItemImageRectangles[i], pt2)) {
changeAlpha = !pSubItemImageHasAlpha[i];
break;
}
}
if(changeAlpha) {
pPixel->rgbReserved = 0xFF;
}
} else {
pPixel->rgbReserved = 0xFF;
}
}
} else {
if(pPixel->rgbReserved == 0x00) {
pPixel->rgbReserved = 0xFF;
}
}
}
}
} else {
// layout is left to right
if(maskMemoryDC.GetPixel(pt.x, pt.y) == 0x00000000) {
if(themedListItems) {
if(itemIsSelected) {
if((pPixel->rgbReserved != 0x00) || selectionBoundingRect.PtInRect(pt)) {
pPixel->rgbReserved = 0xFF;
}
} else if(labelBoundingRect.PtInRect(pt)) {
if(pPixel->rgbReserved == 0x00) {
pPixel->rgbReserved = 0xFF;
}
}
} else {
// items are not themed
if(itemIsSelected) {
if((pPixel->rgbReserved == 0x00) || selectionBoundingRect.PtInRect(pt)) {
if(subItemImages) {
BOOL changeAlpha = TRUE;
for(int i = 0; i < columnCount - 1; ++i) {
if(::PtInRect(&pSubItemImageRectangles[i], pt)) {
changeAlpha = !pSubItemImageHasAlpha[i];
break;
}
}
if(changeAlpha) {
pPixel->rgbReserved = 0xFF;
}
} else {
pPixel->rgbReserved = 0xFF;
}
}
} else {
if(pPixel->rgbReserved == 0x00) {
pPixel->rgbReserved = 0xFF;
}
}
}
}
}
}
}
}
memoryDC.SelectBitmap(hPreviousBitmap);
maskMemoryDC.SelectBitmap(hPreviousBitmapMask);
if(hPreviousFont) {
memoryDC.SelectFont(hPreviousFont);
}
// create the imagelist
HIMAGELIST hDragImageList = ::ImageList_Create(dragImageSize.cx, dragImageSize.cy, (IsComctl32Version600OrNewer() ? ILC_COLOR32 : ILC_COLOR24) | ILC_MASK, 1, 0);
::ImageList_SetBkColor(hDragImageList, CLR_NONE);
::ImageList_Add(hDragImageList, dragImage, dragImageMask);
SECUREFREE(pItemTextBuffer);
::VariantClear(&vTileViewColumns);
ReleaseDC(hCompatibleDC);
if(pSubItemImageRectangles) {
delete[] pSubItemImageRectangles;
}
if(pSubItemImageHasAlpha) {
delete[] pSubItemImageHasAlpha;
}
return hDragImageList;
}
That's it - at least for classic drag images. Aero drag images are a different beast.