|
|
#include "shellprv.h"
#include "defview.h"
#include "lvutil.h"
#include "ids.h"
#include "idlcomm.h"
#pragma hdrstop
#include "datautil.h"
#include "apithk.h"
BOOL DAD_IsDraggingImage(void); void DAD_SetDragCursor(int idCursor); BOOL DAD_IsDragging();
#define MONITORS_MAX 16 // Is this really the max?
#define DCID_NULL 0
#define DCID_NO 1
#define DCID_MOVE 2
#define DCID_COPY 3
#define DCID_LINK 4
#define DCID_MAX 5
#define TF_DRAGIMAGES 0x02000000
#define DRAGDROP_ALPHA 120
#define MAX_WIDTH_ALPHA 200
#define MAX_HEIGHT_ALPHA 200
#define CIRCULAR_ALPHA // Circular Alpha Blending Centered on Center of image
class CDragImages : public IDragSourceHelper, IDropTargetHelper { public: // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef() { return 2; }; // One global Com object per process
STDMETHODIMP_(ULONG) Release() { return 1; }; // One global Com object per process
// IDragSourceHelper methods
STDMETHODIMP InitializeFromBitmap(LPSHDRAGIMAGE pshdi, IDataObject* pdtobj); STDMETHODIMP InitializeFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj);
// IDropTargetHelper methods
STDMETHODIMP DragEnter(HWND hwndTarget, IDataObject* pdtobj, POINT* ppt, DWORD dwEffect); STDMETHODIMP DragLeave(); STDMETHODIMP DragOver(POINT* ppt, DWORD dwEffect); STDMETHODIMP Drop(IDataObject* pdtobj, POINT* ppt, DWORD dwEffect); STDMETHODIMP Show(BOOL fShow);
// These are public so the DAD_* routines can access.
BOOL IsDragging() { return (Initialized() && _Single.bDragging); }; BOOL IsDraggingImage() { return (Initialized() && _fImage && _Single.bDragging); }; BOOL IsDraggingLayeredWindow() { return _shdi.hbmpDragImage != NULL; }; BOOL SetDragImage(HIMAGELIST himl, int index, POINT * pptOffset); void SetDragCursor(int idCursor); HWND GetTarget() { return _hwndTarget; } BOOL Initialized(); DWORD GetThread() { return _idThread; }; void FreeDragData();
void ThreadDetach(); void ProcessDetach();
// for drag source feedback communication
void SetDropEffectCursor(int idCur);
CDragImages() {};
private: ~CDragImages();
void _InitDragData(); BOOL _IsLayeredSupported();
HRESULT _SaveToDataObject(IDataObject* pdtobj); HRESULT _LoadFromDataObject(IDataObject* pdtobj);
HRESULT _LoadLayeredBitmapBits(HGLOBAL hGlobal); HRESULT _SaveLayeredBitmapBits(HGLOBAL* phGlobal);
BOOL _ShowDragImageInterThread(HWND hwndLock, BOOL * pfShow);
// MultiRectDragging
void _MultipleDragShow(BOOL bShow); void _MultipleDragStart(HWND hwndLock, LPRECT aRect, int nRects, POINT ptStart, POINT ptOffset); void _MultipleDragMove(POINT ptNew); HRESULT _SetLayeredDragging(LPSHDRAGIMAGE pshdi); HRESULT _SetMultiItemDragging(HWND hwndLV, int cItems, POINT *pptOffset); HRESULT _SetMultiRectDragging(int cItems, LPRECT prect, POINT *pptOffset);
// Merged Cursors
HBITMAP CreateColorBitmap(int cx, int cy); void _DestroyCachedCursors(); HRESULT _GetCursorLowerRight(HCURSOR hcursor, int * px, int * py, POINT *pptHotSpot); int _MapCursorIDToImageListIndex(int idCur); int _AddCursorToImageList(HCURSOR hcur, LPCTSTR idMerge, POINT *pptHotSpot); BOOL _MergeIcons(HCURSOR hcursor, LPCTSTR idMerge, HBITMAP *phbmImage, HBITMAP *phbmMask, POINT* pptHotSpot); HCURSOR _SetCursorHotspot(HCURSOR hcur, POINT *ptHot);
// Helper Routines
BOOL _CreateDragWindow(); BOOL _PreProcessDragBitmap(void** ppvBits);
// Member Variables
SHDRAGIMAGE _shdi; HWND _hwndTarget; HWND _hwnd; // The HWND of the Layered Window
HDC _hdcDragImage; HBITMAP _hbmpOld;
BOOL _fLayeredSupported; BOOL _fCursorDataInited;
POINT _ptDebounce;
// Legacy drag support
BOOL _fImage; POINT _ptOffset; DWORD _idThread; HIMAGELIST _himlCursors; UINT _cRev; int _aindex[DCID_MAX]; // will be initialized.
HCURSOR _ahcur[DCID_MAX]; POINT _aptHotSpot[DCID_MAX]; int _idCursor;
// _Single struct is used between DAD_Enter and DAD_Leave
struct { // Common part
BOOL bDragging; BOOL bLocked; HWND hwndLock; BOOL bSingle; // Single imagelist dragging.
DWORD idThreadEntered;
// Multi-rect dragging specific part
struct { BOOL bShown; LPRECT pRect; int nRects; POINT ptOffset; POINT ptNow; } _Multi; } _Single;
// following fields are used only when fImage==FALSE
RECT* _parc; // cItems
UINT _cItems; // This is a sentinal. Needs to be the last item.
};
CDragImages::~CDragImages() { FreeDragData(); } //
// Read 'Notes' in CDropSource_GiveFeedback for detail about this
// g_fDraggingOverSource flag, which is TRUE only if we are dragging
// over the source window itself with left mouse button
// (background and large/small icon mode only).
//
UINT g_cRev = 0; CDragImages* g_pdiDragImages = NULL; BOOL g_fDraggingOverSource = FALSE;
STDAPI CDragImages_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppvOut) { ASSERT(pUnkOuter == NULL); //Who's trying to aggregate us?
if (!g_pdiDragImages) g_pdiDragImages = new CDragImages();
if (g_pdiDragImages && ppvOut) // ppvOut test for internal create usage
return g_pdiDragImages->QueryInterface(riid, ppvOut);
return E_OUTOFMEMORY; }
STDMETHODIMP CDragImages::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDragImages, IDragSourceHelper), QITABENT(CDragImages, IDropTargetHelper), { 0 }, }; return QISearch(this, qit, riid, ppv); }
#define UM_KILLYOURSELF WM_USER
LRESULT CALLBACK DragWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == UM_KILLYOURSELF) { DestroyWindow(hwnd);
return 0; }
return DefWindowProc(hwnd, uMsg, wParam, lParam); }
BOOL CDragImages::_CreateDragWindow() { if (_hwnd == NULL) { WNDCLASS wc = {0};
wc.hInstance = g_hinst; wc.lpfnWndProc = DragWndProc; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = TEXT("SysDragImage"); wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); // NULL;
SHRegisterClass(&wc);
_hwnd = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TOOLWINDOW, TEXT("SysDragImage"), TEXT("Drag"), WS_POPUPWINDOW, 0, 0, 50, 50, NULL, NULL, g_hinst, NULL);
if (!_hwnd) return FALSE;
//
// This window should not be mirrored so that the image contents won't be flipped. [samera]
//
SetWindowBits(_hwnd, GWL_EXSTYLE, RTL_MIRRORED_WINDOW, 0); }
return TRUE; }
BOOL CDragImages::Initialized() { return _fCursorDataInited; }
void CDragImages::FreeDragData() {
if (_hwnd) { SendMessage(_hwnd, UM_KILLYOURSELF, 0, 0); _hwnd = NULL; }
_fCursorDataInited = FALSE;
// Make sure we destroy the cursors on an invalidate.
if (_himlCursors) _DestroyCachedCursors();
// Do we have an array?
if (_parc) { delete [] _parc; _parc = NULL; }
if (_fImage) ImageList_EndDrag();
if (_hbmpOld) { SelectObject(_hdcDragImage, _hbmpOld); _hbmpOld = NULL; }
if (_hdcDragImage) { DeleteDC(_hdcDragImage); _hdcDragImage = NULL; }
if (_shdi.hbmpDragImage) DeleteObject(_shdi.hbmpDragImage);
ZeroMemory(&_Single, sizeof(_Single)); ZeroMemory(&_shdi, sizeof(_shdi));
_ptOffset.x = 0; _ptOffset.y = 0;
_ptDebounce.x = 0; _ptDebounce.y = 0;
_hwndTarget = _hwnd = NULL; _fCursorDataInited = _fLayeredSupported = FALSE; _fImage = FALSE; _idThread = 0; _himlCursors = NULL; _cRev = 0; _idCursor = 0; }
void CDragImages::_InitDragData() { _idThread = GetCurrentThreadId();
if (_himlCursors && _cRev != g_cRev) _DestroyCachedCursors();
if (_himlCursors == NULL) { UINT uFlags = ILC_MASK | ILC_SHARED; if (IS_BIDI_LOCALIZED_SYSTEM()) uFlags |= ILC_MIRROR;
//
// if this is not a palette device, use a DDB for the imagelist
// this is important when displaying high-color cursors
//
HDC hdc = GetDC(NULL); if (!(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)) { uFlags |= ILC_COLORDDB; } ReleaseDC(NULL, hdc);
_himlCursors = ImageList_Create(GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR), uFlags, 1, 0);
_cRev = g_cRev;
// We need to initialize s_cursors._aindex[*]
_MapCursorIDToImageListIndex(-1); } _fCursorDataInited = TRUE; }
BOOL AreAllMonitorsAtLeast(int iBpp) { DISPLAY_DEVICE DisplayDevice; BOOL fAreAllMonitorsAtLeast = TRUE;
for (int iEnum = 0; fAreAllMonitorsAtLeast && iEnum < MONITORS_MAX; iEnum++) { ZeroMemory(&DisplayDevice, sizeof(DisplayDevice)); DisplayDevice.cb = sizeof(DisplayDevice);
if (EnumDisplayDevices(NULL, iEnum, &DisplayDevice, 0) && (DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) {
HDC hdc = CreateDC(NULL, (LPTSTR)DisplayDevice.DeviceName, NULL, NULL); if (hdc) { int iBits = GetDeviceCaps(hdc, BITSPIXEL);
if (iBits < iBpp) fAreAllMonitorsAtLeast = FALSE;
DeleteDC(hdc); } } }
return fAreAllMonitorsAtLeast; }
BOOL CDragImages::_IsLayeredSupported() { // For the first rev, we will only support Layered drag images
// when the Color depth is greater than 65k colors.
// We should ask everytime....
_fLayeredSupported = AreAllMonitorsAtLeast(16); if (_fLayeredSupported) { BOOL bDrag; if (SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bDrag, 0)) { _fLayeredSupported = BOOLIFY(bDrag); }
if (_fLayeredSupported) _fLayeredSupported = SHRegGetBoolUSValue(REGSTR_EXPLORER_ADVANCED, TEXT("NewDragImages"), FALSE, TRUE); } return _fLayeredSupported; }
//
// initialize the static drag image manager from a structure
// this is implemented for WindowLess controls that can act as a
// drag source.
//
HRESULT CDragImages::_SetLayeredDragging(LPSHDRAGIMAGE pshdi) { // We don't support being initialized from a bitmap when Layered Windows are not supported
HRESULT hr; if (_IsLayeredSupported()) { RIP(IsValidHANDLE(pshdi->hbmpDragImage));
_shdi = *pshdi; // Keep a copy of this.
_idCursor = -1; // Initialize this... This is an arbitraty place and can be put
// anywhere before the first Setcursor call
_InitDragData(); hr = S_OK; } else hr = E_FAIL; return hr; }
STDMETHODIMP CDragImages::InitializeFromBitmap(LPSHDRAGIMAGE pshdi, IDataObject* pdtobj) { FreeDragData();
HRESULT hr = _SetLayeredDragging(pshdi); if (SUCCEEDED(hr)) { hr = _SaveToDataObject(pdtobj); if (FAILED(hr)) FreeDragData(); } return hr; }
BOOL ListView_HasMask(HWND hwnd) { HIMAGELIST himl = ListView_GetImageList(hwnd, LVSIL_NORMAL); return himl && (ImageList_GetFlags(himl) & ILC_MASK); }
//
// initialize the static drag image manager from an HWND that
// can process the RegisteredWindowMessage(DI_GETDRAGIMAGE)
//
STDMETHODIMP CDragImages::InitializeFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj) { HRESULT hr = E_FAIL;
FreeDragData();
if (_IsLayeredSupported()) { // Register the message that gets us the Bitmap from the control.
static int g_msgGetDragImage = 0; if (g_msgGetDragImage == 0) g_msgGetDragImage = RegisterWindowMessage(DI_GETDRAGIMAGE);
// Can this HWND generate a drag image for me?
if (g_msgGetDragImage && SendMessage(hwnd, g_msgGetDragImage, 0, (LPARAM)&_shdi)) { // Yes; Now we select that into the window
hr = _SetLayeredDragging(&_shdi); } }
if (FAILED(hr)) { TCHAR szClassName[50];
if (GetClassName(hwnd, szClassName, ARRAYSIZE(szClassName))) { if (lstrcmpi(szClassName, WC_LISTVIEW) == 0) { POINT ptOffset = {0,0};
if (ppt) ptOffset = *ppt;
int cItems = ListView_GetSelectedCount(hwnd); if (cItems >= 1) { if ((cItems == 1) && ListView_HasMask(hwnd)) { POINT ptTemp; HIMAGELIST himl = ListView_CreateDragImage(hwnd, ListView_GetNextItem(hwnd, -1, LVNI_SELECTED), &ptTemp); if (himl) { ClientToScreen(hwnd, &ptTemp); ptOffset.x -= ptTemp.x;
// Since the listview is mirrored, then mirror the selected
// icon coord. This would result in negative offset so let's
// compensate. [samera]
if (IS_WINDOW_RTL_MIRRORED(hwnd)) ptOffset.x *= -1;
ptOffset.y -= ptTemp.y; SetDragImage(himl, 0, &ptOffset); ImageList_Destroy(himl); hr = S_OK; } } else { hr = _SetMultiItemDragging(hwnd, cItems, &ptOffset); } } } else if (lstrcmpi(szClassName, WC_TREEVIEW) == 0) { HIMAGELIST himlDrag = TreeView_CreateDragImage(hwnd, NULL); if (himlDrag) { SetDragImage(himlDrag, 0, NULL); ImageList_Destroy(himlDrag); hr = S_OK; } } } }
if (SUCCEEDED(hr)) { // ignore failure here as this will still work in process due to the globals
// fonts folder depends on this
_SaveToDataObject(pdtobj); }
return hr; }
//
// create the drag window in the layered window case, or to begin drawing the
// Multi Rect or icon drag images.
//
STDMETHODIMP CDragImages::DragEnter(HWND hwndTarget, IDataObject* pdtobj, POINT* ppt, DWORD dwEffect) { HRESULT hr = _LoadFromDataObject(pdtobj); if (SUCCEEDED(hr)) { _hwndTarget = hwndTarget ? hwndTarget : GetDesktopWindow(); SetDragCursor(-1); _Single.bDragging = TRUE; _Single.bSingle = _fImage; _Single.hwndLock = _hwndTarget; _Single.bLocked = FALSE; _Single.idThreadEntered = GetCurrentThreadId();
_ptDebounce.x = 0; _ptDebounce.y = 0;
if (_shdi.hbmpDragImage) { TraceMsg(TF_DRAGIMAGES, "CDragImages::DragEnter : Creating Drag Window"); // At this point the information has been read from the data object.
// Reconstruct the HWND if necessary
if (_CreateDragWindow() && _hdcDragImage) { POINT ptSrc = {0, 0}; POINT pt;
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
GetMsgPos(&pt);
pt.x -= _shdi.ptOffset.x; pt.y -= _shdi.ptOffset.y;
BLENDFUNCTION blend; blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = 0; blend.AlphaFormat = AC_SRC_ALPHA; blend.SourceConstantAlpha = 0xFF /*DRAGDROP_ALPHA*/;
HDC hdc = GetDC(_hwnd); if (hdc) { DWORD fULWType = ULW_ALPHA;
// Should have been preprocess already
UpdateLayeredWindow(_hwnd, hdc, &pt, &(_shdi.sizeDragImage), _hdcDragImage, &ptSrc, _shdi.crColorKey, &blend, fULWType);
ReleaseDC(_hwnd, hdc); } hr = S_OK; } } else { // These are in Client Cordinates, not screen coords. Translate:
POINT pt = *ppt; RECT rc; GetWindowRect(_hwndTarget, &rc); pt.x -= rc.left; pt.y -= rc.top; if (_fImage) { // Avoid the flicker by always pass even coords
ImageList_DragEnter(hwndTarget, pt.x & ~1, pt.y & ~1); hr = S_OK; } else { _MultipleDragStart(hwndTarget, _parc, _cItems, pt, _ptOffset); hr = S_OK; } }
//
// We should always show the image whenever this function is called.
//
Show(TRUE); } return hr; }
//
// kill the Layered Window, or to stop painting the icon or rect drag images
//
STDMETHODIMP CDragImages::DragLeave() { TraceMsg(TF_DRAGIMAGES, "CDragImages::DragLeave"); if (Initialized()) { if (_hwnd) { FreeDragData(); } else if (_Single.bDragging && _Single.idThreadEntered == GetCurrentThreadId()) { Show(FALSE);
if (_fImage) { ImageList_DragLeave(_Single.hwndLock); }
_Single.bDragging = FALSE;
DAD_SetDragImage((HIMAGELIST)-1, NULL); }
_ptDebounce.x = 0; _ptDebounce.y = 0; }
return S_OK; }
// move the Layered window or to rerender the icon or rect images within
// the Window they are over.
//
STDMETHODIMP CDragImages::DragOver(POINT* ppt, DWORD dwEffect) { if (Initialized()) { TraceMsg(TF_DRAGIMAGES, "CDragImages::DragOver pt {%d, %d}", ppt->x, ppt->y); // Avoid the flicker by always pass even coords
ppt->x &= ~1; ppt->y &= ~1;
if (_ptDebounce.x != ppt->x || _ptDebounce.y != ppt->y) { _ptDebounce.x = ppt->x; _ptDebounce.y = ppt->y; if (IsDraggingLayeredWindow()) { POINT pt; GetCursorPos(&pt); pt.x -= _shdi.ptOffset.x; pt.y -= _shdi.ptOffset.y;
SetWindowPos(_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
UpdateLayeredWindow(_hwnd, NULL, &pt, NULL, NULL, NULL, 0, NULL, 0); } else { // These are in Client Cordinates, not screen coords. Translate:
POINT pt = *ppt; RECT rc; GetWindowRect(_hwndTarget, &rc); pt.x -= rc.left; pt.y -= rc.top; if (_fImage) { ImageList_DragMove(pt.x, pt.y); } else { _MultipleDragMove(pt); } } } }
return S_OK; }
// do any cleanup after a drop (Currently calls DragLeave)
//
STDMETHODIMP CDragImages::Drop(IDataObject* pdtobj, POINT* ppt, DWORD dwEffect) { return DragLeave(); }
// initialize the static drag image manager from a structure
// this is implemented for WindowLess controls that can act as a
// drag source.
//
void CDragImages::SetDragCursor(int idCursor) { //
// Ignore if we are dragging over ourselves.
//
if (IsDraggingImage()) { POINT ptHotSpot;
if (_himlCursors && (idCursor != -1)) { int iIndex = _MapCursorIDToImageListIndex(idCursor); if (iIndex != -1) { ImageList_GetDragImage(NULL, &ptHotSpot); ptHotSpot.x -= _aptHotSpot[idCursor].x; ptHotSpot.y -= _aptHotSpot[idCursor].y; if (ptHotSpot.x < 0) { ptHotSpot.x = 0; }
if (ptHotSpot.y < 0) { ptHotSpot.y = 0; }
ImageList_SetDragCursorImage(_himlCursors, iIndex, ptHotSpot.x, ptHotSpot.y); } else { // You passed a bad Cursor ID.
ASSERT(0); } }
_idCursor = idCursor; } }
// init our state from the hGlobal so we can draw
HRESULT CDragImages::_LoadLayeredBitmapBits(HGLOBAL hGlobal) { HRESULT hr = E_FAIL;
if (!Initialized()) { ASSERT(_shdi.hbmpDragImage == NULL); ASSERT(_hdcDragImage == NULL);
HDC hdcScreen = GetDC(NULL); if (hdcScreen) { void *pvDragStuff = (void*)GlobalLock(hGlobal); if (pvDragStuff) { CopyMemory(&_shdi, pvDragStuff, sizeof(_shdi));
BITMAPINFO bmi = {0};
// Create a buffer to read the bits into
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = _shdi.sizeDragImage.cx; bmi.bmiHeader.biHeight = _shdi.sizeDragImage.cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB;
// Next create a DC and an HBITMAP.
_hdcDragImage = CreateCompatibleDC(hdcScreen); if (_hdcDragImage) { void *pvBits; _shdi.hbmpDragImage = CreateDIBSection(_hdcDragImage, &bmi, DIB_RGB_COLORS, &pvBits, NULL, NULL); if (_shdi.hbmpDragImage) { _hbmpOld = (HBITMAP)SelectObject(_hdcDragImage, _shdi.hbmpDragImage);
// then Set the bits into the Bitmap
RGBQUAD* pvStart = (RGBQUAD*)((BYTE*)pvDragStuff + sizeof(SHDRAGIMAGE)); DWORD dwCount = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD); CopyMemory((RGBQUAD*)pvBits, (RGBQUAD*)pvStart, dwCount);
hr = S_OK; // success!
} } GlobalUnlock(hGlobal); } ReleaseDC(NULL, hdcScreen); } } return hr; }
// Writes the written information into phGlobal to recreate the drag image
HRESULT CDragImages::_SaveLayeredBitmapBits(HGLOBAL* phGlobal) { HRESULT hr = E_FAIL; if (Initialized()) { ASSERT(_shdi.hbmpDragImage);
DWORD cbImageSize = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD); *phGlobal = GlobalAlloc(GPTR, cbImageSize + sizeof(SHDRAGIMAGE)); if (*phGlobal) { void *pvDragStuff = GlobalLock(*phGlobal); CopyMemory(pvDragStuff, &_shdi, sizeof(SHDRAGIMAGE));
void *pvBits; hr = _PreProcessDragBitmap(&pvBits) ? S_OK : E_FAIL; if (SUCCEEDED(hr)) { RGBQUAD* pvStart = (RGBQUAD*)((BYTE*)pvDragStuff + sizeof(SHDRAGIMAGE)); DWORD dwCount = _shdi.sizeDragImage.cx * _shdi.sizeDragImage.cy * sizeof(RGBQUAD); CopyMemory((RGBQUAD*)pvStart, (RGBQUAD*)pvBits, dwCount); } GlobalUnlock(*phGlobal); } } return hr; }
BOOL IsColorKey(RGBQUAD rgbPixel, COLORREF crKey) { // COLORREF is backwards to RGBQUAD
return InRange( rgbPixel.rgbBlue, ((crKey & 0xFF0000) >> 16) - 5, ((crKey & 0xFF0000) >> 16) + 5) && InRange( rgbPixel.rgbGreen, ((crKey & 0x00FF00) >> 8) - 5, ((crKey & 0x00FF00) >> 8) + 5) && InRange( rgbPixel.rgbRed, ((crKey & 0x0000FF) >> 0) - 5, ((crKey & 0x0000FF) >> 0) + 5); }
#ifdef RADIAL
int QuickRoot(int n, int iNum) {
int iRoot = iNum; for (int i=10; i > 0; i--) { int iOld = iRoot; iRoot = (iRoot + iNum/iRoot)/2; if (iRoot == iOld) break; }
return iRoot; }
#endif
BOOL CDragImages::_PreProcessDragBitmap(void** ppvBits) { BOOL fRet = FALSE;
ASSERT(_hdcDragImage == NULL); _hdcDragImage = CreateCompatibleDC(NULL); if (_hdcDragImage) { ULONG* pul; HBITMAP hbmpResult = NULL; HBITMAP hbmpOld; HDC hdcSource = NULL; BITMAPINFO bmi = {0}; HBITMAP hbmp = _shdi.hbmpDragImage;
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmi.bmiHeader.biWidth = _shdi.sizeDragImage.cx; bmi.bmiHeader.biHeight = _shdi.sizeDragImage.cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB;
hdcSource = CreateCompatibleDC(_hdcDragImage); if (hdcSource) { hbmpResult = CreateDIBSection(_hdcDragImage, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
if (hbmpResult) { _hbmpOld = (HBITMAP)SelectObject(_hdcDragImage, hbmpResult); hbmpOld = (HBITMAP)SelectObject(hdcSource, hbmp);
BitBlt(_hdcDragImage, 0, 0, _shdi.sizeDragImage.cx, _shdi.sizeDragImage.cy, hdcSource, 0, 0, SRCCOPY);
pul = (ULONG*)*ppvBits;
int iOffsetX = _shdi.ptOffset.x; int iOffsetY = _shdi.ptOffset.y; int iDenomX = max(_shdi.sizeDragImage.cx - iOffsetX, iOffsetX); int iDenomY = max(_shdi.sizeDragImage.cy - iOffsetY, iOffsetY); BOOL fRadialFade = TRUE; // If both are less than the max, then no radial fade.
if (_shdi.sizeDragImage.cy <= MAX_HEIGHT_ALPHA && _shdi.sizeDragImage.cx <= MAX_WIDTH_ALPHA) fRadialFade = FALSE;
for (int Y = 0; Y < _shdi.sizeDragImage.cy; Y++) { int y = _shdi.sizeDragImage.cy - Y; // Bottom up DIB.
for (int x = 0; x < _shdi.sizeDragImage.cx; x++) { RGBQUAD* prgb = (RGBQUAD*)&pul[Y * _shdi.sizeDragImage.cx + x];
if (_shdi.crColorKey != CLR_NONE && IsColorKey(*prgb, _shdi.crColorKey)) { // Write a pre-multiplied value of 0:
*((DWORD*)prgb) = 0; } else { int Alpha = prgb->rgbReserved; if (_shdi.crColorKey != CLR_NONE) { Alpha = DRAGDROP_ALPHA; } else { Alpha -= (Alpha / 3); }
if (fRadialFade && Alpha > 0) { // This does not generate a smooth curve, but this is just
// an effect, not trying to be accurate here.
// 3 devides per pixel
int ddx = (x < iOffsetX)? iOffsetX - x : x - iOffsetX; int ddy = (y < iOffsetY)? iOffsetY - y : y - iOffsetY;
__int64 iAlphaX = (100000l - (((__int64)ddx * 100000l) / (iDenomX ))); __int64 iAlphaY = (100000l - (((__int64)ddy * 100000l) / (iDenomY )));
ASSERT (iAlphaX >= 0); ASSERT (iAlphaY >= 0);
__int64 iDenom = 100000; iDenom *= 100000;
Alpha = (int) ((Alpha * iAlphaX * iAlphaY * 100000) / (iDenom* 141428)); }
ASSERT(Alpha <= 0xFF); prgb->rgbReserved = (BYTE)Alpha; prgb->rgbRed = ((prgb->rgbRed * Alpha) + 128) / 255; prgb->rgbGreen = ((prgb->rgbGreen * Alpha) + 128) / 255; prgb->rgbBlue = ((prgb->rgbBlue * Alpha) + 128) / 255; } } }
DeleteObject(hbmp); _shdi.hbmpDragImage = hbmpResult;
fRet = TRUE;
if (hbmpOld) SelectObject(hdcSource, hbmpOld); }
DeleteObject(hdcSource); } }
return fRet; }
CLIPFORMAT _GetDragContentsCF() { static UINT s_cfDragContents = 0; if (0 == s_cfDragContents) s_cfDragContents = RegisterClipboardFormat(CFSTR_DRAGCONTEXT); return (CLIPFORMAT) s_cfDragContents; }
CLIPFORMAT _GetDragImageBitssCF() { static UINT s_cfDragImageBitss = 0; if (0 == s_cfDragImageBitss) s_cfDragImageBitss = RegisterClipboardFormat(TEXT("DragImageBits")); return (CLIPFORMAT) s_cfDragImageBitss; }
// persist our state into the data object. so on the target side they can grab this
// data out and render the thing being dragged
HRESULT CDragImages::_SaveToDataObject(IDataObject *pdtobj) { HRESULT hr = E_FAIL; // one form of the saves below must succeed
if (Initialized()) { STGMEDIUM medium = {0}; medium.tymed = TYMED_ISTREAM;
if (SUCCEEDED(CreateStreamOnHGlobal(NULL, TRUE, &medium.pstm))) { // Set the header .
DragContextHeader hdr = {0}; hdr.fImage = _fImage; hdr.fLayered = IsDraggingLayeredWindow(); hdr.ptOffset = _ptOffset; //First Write the drag context header
ULONG ulWritten; if (SUCCEEDED(medium.pstm->Write(&hdr, sizeof(hdr), &ulWritten)) && (ulWritten == sizeof(hdr))) { if (hdr.fLayered) { STGMEDIUM mediumBits = {0}; // Set the medium.
mediumBits.tymed = TYMED_HGLOBAL;
// Write out layered window information
hr = _SaveLayeredBitmapBits(&mediumBits.hGlobal); if (SUCCEEDED(hr)) { FORMATETC fmte = {_GetDragImageBitssCF(), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
// Set the medium in the data.
hr = pdtobj->SetData(&fmte, &mediumBits, TRUE); if (FAILED(hr)) ReleaseStgMedium(&mediumBits); // cleanup
} } else if (hdr.fImage) { // write an image
HIMAGELIST himl = ImageList_GetDragImage(NULL, NULL); if (ImageList_Write(himl, medium.pstm)) { hr = S_OK; // success
} } else { // multi rect
if (SUCCEEDED(medium.pstm->Write(&_cItems, sizeof(_cItems), &ulWritten)) && (ulWritten == sizeof(_cItems))) { // Write the rects into the stream
if (SUCCEEDED(medium.pstm->Write(_parc, sizeof(_parc[0]) * _cItems, &ulWritten)) && (ulWritten == (sizeof(_parc[0]) * _cItems))) { hr = S_OK; // success
} } }
if (SUCCEEDED(hr)) { // Set the seek pointer at the beginning.
medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
// Set the Formatetc
FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
// Set the medium in the data.
hr = pdtobj->SetData(&fmte, &medium, TRUE); } }
if (FAILED(hr)) ReleaseStgMedium(&medium); } } return hr; }
// Gets the information to rebuild the drag images from the data object
HRESULT CDragImages::_LoadFromDataObject(IDataObject *pdtobj) { // Check if we have a drag context
HRESULT hr;
// NULL pdtobj is for the old DAD_DragEnterXXX() APIs...
// we hope this in the same process
if (Initialized() || !pdtobj) { hr = S_OK; // already loaded
} else { // Set the format we are interested in
FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM};
//if the data object has the format we are interested in
// then Get the data
STGMEDIUM medium = {0}; hr = pdtobj->GetData(&fmte, &medium); if (SUCCEEDED(hr)) // if no pstm, bag out.
{ // Set the seek pointer at the beginning. PARANOIA: This is for people
// Who don't set the seek for me.
medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
//First Read the drag context header
DragContextHeader hdr; if (SUCCEEDED(IStream_Read(medium.pstm, &hdr, sizeof(hdr)))) { if (hdr.fLayered) { STGMEDIUM mediumBits; FORMATETC fmte = {_GetDragImageBitssCF(), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
hr = pdtobj->GetData(&fmte, &mediumBits); if (SUCCEEDED(hr)) { hr = _LoadLayeredBitmapBits(mediumBits.hGlobal); ReleaseStgMedium(&mediumBits); } } else if (hdr.fImage) { // single image
HIMAGELIST himl = ImageList_Read(medium.pstm); if (himl) { DAD_SetDragImage(himl, &(hdr.ptOffset)); ImageList_Destroy(himl); hr = S_OK; } } else { // multi rect
int cItems; if (SUCCEEDED(IStream_Read(medium.pstm, &cItems, sizeof(cItems)))) { RECT *prect = (RECT *)LocalAlloc(LPTR, sizeof(*prect) * cItems); if (prect) { if (SUCCEEDED(IStream_Read(medium.pstm, prect, sizeof(*prect) * cItems))) { hr = _SetMultiRectDragging(cItems, prect, &hdr.ptOffset); } LocalFree(prect); } } } }
if (SUCCEEDED(hr)) _InitDragData();
// Set the seek pointer at the beginning. Just cleaning up...
medium.pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
// Release the stg medium.
ReleaseStgMedium(&medium); } } return hr; }
// Shows or hides the drag images. NOTE: Doesn't do anything in the layered window case.
// We don't need to because this function is specifically for drawing to a locked window.
STDMETHODIMP CDragImages::Show(BOOL bShow) { BOOL fOld = bShow; TraceMsg(TF_DRAGIMAGES, "CDragImages::Show(%s)", bShow? TEXT("true") : TEXT("false"));
if (!Initialized() || !_Single.bDragging) { return S_FALSE; }
// No point in showing and hiding a Window. This causes unnecessary flicker.
if (_hwnd) { return S_OK; }
// If we're going across thread boundaries we have to try a context switch
if (GetCurrentThreadId() != GetWindowThreadProcessId(_Single.hwndLock, NULL) && _ShowDragImageInterThread(_Single.hwndLock, &fOld)) return fOld;
fOld = _Single.bLocked;
//
// If we are going to show the drag image, lock the target window.
//
if (bShow && !_Single.bLocked) { TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : Shown and not locked"); UpdateWindow(_Single.hwndLock); LockWindowUpdate(_Single.hwndLock); _Single.bLocked = TRUE; }
if (_Single.bSingle) { TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : Calling ImageList_DragShowNoLock"); ImageList_DragShowNolock(bShow); } else { TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : MultiDragShow"); _MultipleDragShow(bShow); }
//
// If we have just hide the drag image, unlock the target window.
//
if (!bShow && _Single.bLocked) { TraceMsg(TF_DRAGIMAGES, "CDragImages::Show : hiding image, unlocking"); LockWindowUpdate(NULL); _Single.bLocked = FALSE; }
return fOld ? S_OK : S_FALSE; }
// tell the drag source to hide or unhide the drag image to allow
// the destination to do drawing (unlock the screen)
//
// in:
// bShow FALSE - hide the drag image, allow drawing
// TRUE - show the drag image, no drawing allowed after this
// Helper function for DAD_ShowDragImage - handles the inter-thread case.
// We need to handle this case differently because LockWindowUpdate calls fail
// if they are on the wrong thread.
BOOL CDragImages::_ShowDragImageInterThread(HWND hwndLock, BOOL * pfShow) { TCHAR szClassName[50];
if (GetClassName(hwndLock, szClassName, ARRAYSIZE(szClassName))) { UINT uMsg = 0; ULONG_PTR dw = 0;
if (lstrcmpi(szClassName, TEXT("SHELLDLL_DefView")) == 0) uMsg = WM_DSV_SHOWDRAGIMAGE; if (lstrcmpi(szClassName, TEXT("CabinetWClass")) == 0) uMsg = CWM_SHOWDRAGIMAGE;
if (uMsg) { SendMessageTimeout(hwndLock, uMsg, 0, *pfShow, SMTO_ABORTIFHUNG, 1000, &dw); *pfShow = (dw != 0); return TRUE; } }
return FALSE; }
void CDragImages::ThreadDetach() { if (_idThread == GetCurrentThreadId()) FreeDragData(); }
void CDragImages::ProcessDetach() { FreeDragData(); }
BOOL CDragImages::SetDragImage(HIMAGELIST himl, int index, POINT * pptOffset) { if (himl) { // We are setting
if (Initialized()) return FALSE;
_fImage = TRUE; if (pptOffset) { // Avoid the flicker by always pass even coords
_ptOffset.x = (pptOffset->x & ~1); _ptOffset.y = (pptOffset->y & ~1); }
ImageList_BeginDrag(himl, index, _ptOffset.x, _ptOffset.y); _InitDragData(); } else { FreeDragData(); } return TRUE; }
//=====================================================================
// Multiple Drag show
//=====================================================================
void CDragImages::_MultipleDragShow(BOOL bShow) { HDC hDC; int nRect; RECT rc, rcClip;
if ((bShow && _Single._Multi.bShown) || (!bShow && !_Single._Multi.bShown)) return;
_Single._Multi.bShown = bShow;
// clip to window, NOT SM_CXSCREEN/SM_CYSCREEN (multiple monitors)
GetWindowRect(_Single.hwndLock, &rcClip); rcClip.right -= rcClip.left; rcClip.bottom -= rcClip.top;
hDC = GetDCEx(_Single.hwndLock, NULL, DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
for (nRect = _Single._Multi.nRects - 1; nRect >= 0; --nRect) { rc = _Single._Multi.pRect[nRect]; OffsetRect(&rc, _Single._Multi.ptNow.x - _Single._Multi.ptOffset.x, _Single._Multi.ptNow.y - _Single._Multi.ptOffset.y);
if ((rc.top < rcClip.bottom) && (rc.bottom > 0) && (rc.left < rcClip.right) && (rc.right > 0)) { DrawFocusRect(hDC, &rc); } } ReleaseDC(_Single.hwndLock, hDC); }
void CDragImages::_MultipleDragStart(HWND hwndLock, LPRECT aRect, int nRects, POINT ptStart, POINT ptOffset) { _Single._Multi.bShown = FALSE; _Single._Multi.pRect = aRect; _Single._Multi.nRects = nRects; _Single._Multi.ptOffset = ptOffset; _Single._Multi.ptNow = ptStart; }
void CDragImages::_MultipleDragMove(POINT ptNew) { if ((_Single._Multi.ptNow.x == ptNew.x) && (_Single._Multi.ptNow.y == ptNew.y)) { // nothing has changed. bail
return; }
if (_Single._Multi.bShown) { HDC hDC; int nRect; RECT rc, rcClip; int dx1 = _Single._Multi.ptNow.x - _Single._Multi.ptOffset.x; int dy1 = _Single._Multi.ptNow.y - _Single._Multi.ptOffset.y; int dx2 = ptNew.x - _Single._Multi.ptNow.x; int dy2 = ptNew.y - _Single._Multi.ptNow.y;
// clip to window, NOT SM_CXSCREEN/SM_CYSCREEN (multiple monitors)
GetWindowRect(_Single.hwndLock, &rcClip); rcClip.right -= rcClip.left; rcClip.bottom -= rcClip.top;
hDC = GetDCEx(_Single.hwndLock, NULL, DCX_WINDOW | DCX_CACHE | DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
for (nRect = _Single._Multi.nRects - 1; nRect >= 0; --nRect) { rc = _Single._Multi.pRect[nRect]; // hide pass
OffsetRect(&rc, dx1, dy1); if ((rc.top < rcClip.bottom) && (rc.bottom > 0) && (rc.left < rcClip.right) && (rc.right > 0)) { DrawFocusRect(hDC, &rc); } // show pass
OffsetRect(&rc, dx2, dy2); if ((rc.top < rcClip.bottom) && (rc.bottom > 0) && (rc.left < rcClip.right) && (rc.right > 0)) { DrawFocusRect(hDC, &rc); } } ReleaseDC(_Single.hwndLock, hDC); }
_Single._Multi.ptNow = ptNew; }
HRESULT CDragImages::_SetMultiRectDragging(int cItems, LPRECT prect, POINT *pptOffset) { if (!Initialized()) { // Multiple item drag
_cItems = cItems; _parc = new RECT[2 * _cItems]; if (_parc) { for (int i = 0; i < cItems; i++) _parc[i] = prect[i];
// Avoid the flicker by always pass even coords
_ptOffset.x = (pptOffset->x & ~1); _ptOffset.y = (pptOffset->y & ~1); _InitDragData(); } } return S_OK; }
#define ListView_IsIconView(hwndLV) ((GetWindowLong(hwndLV, GWL_STYLE) & (UINT)LVS_TYPEMASK) == (UINT)LVS_ICON)
HRESULT CDragImages::_SetMultiItemDragging(HWND hwndLV, int cItems, POINT *pptOffset) { HRESULT hr = E_FAIL;
if (!Initialized()) { // Multiple item drag
ASSERT(NULL == _parc);
_parc = new RECT[2 * cItems]; if (_parc) { POINT ptTemp; int iLast, iNext; int cxScreens, cyScreens; LPRECT prcNext; RECT rc;
_cItems = 0; ASSERT(_fImage == FALSE);
//
// If this is a mirrored Window, then lead edge is going
// to be the far end in screen coord. So let's compute
// as the original code, and later in _MultipleDragMove
// we will compensate.
//
GetWindowRect( hwndLV , &rc ); ptTemp.x = rc.left; ptTemp.y = rc.top;
//
// Reflect the shift the if the window is RTL mirrored.
//
if (IS_WINDOW_RTL_MIRRORED(hwndLV)) { ptTemp.x = -ptTemp.x; pptOffset->x = ((rc.right-rc.left)-pptOffset->x); }
cxScreens = GetSystemMetrics(SM_CXVIRTUALSCREEN); cyScreens = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// for pre-Nashville platforms
if (!cxScreens || !cyScreens) { cxScreens = GetSystemMetrics(SM_CXSCREEN); cyScreens = GetSystemMetrics(SM_CYSCREEN); }
for (iNext = cItems - 1, iLast = -1, prcNext = _parc; iNext >= 0; --iNext) { iLast = ListView_GetNextItem(hwndLV, iLast, LVNI_SELECTED); if (iLast != -1) { ListView_GetItemRect(hwndLV, iLast, &prcNext[0], LVIR_ICON); OffsetRect(&prcNext[0], ptTemp.x, ptTemp.y);
if (((prcNext[0].left - pptOffset->x) < cxScreens) && ((pptOffset->x - prcNext[0].right) < cxScreens) && ((prcNext[0].top - pptOffset->y) < cyScreens)) {
ListView_GetItemRect(hwndLV, iLast, &prcNext[1], LVIR_LABEL); OffsetRect(&prcNext[1], ptTemp.x, ptTemp.y); if ((pptOffset->y - prcNext[1].bottom) < cxScreens) { //
// Fix 24857: Ask JoeB why we are drawing a bar instead of
// a text rectangle.
//
prcNext[1].top = (prcNext[1].top + prcNext[1].bottom)/2; prcNext[1].bottom = prcNext[1].top + 2; prcNext += 2; _cItems += 2; } } } }
// Avoid the flicker by always pass even coords
_ptOffset.x = (pptOffset->x & ~1); _ptOffset.y = (pptOffset->y & ~1); _InitDragData(); hr = S_OK; } } return hr; }
//=====================================================================
// Cursor Merging
//=====================================================================
void CDragImages::_DestroyCachedCursors() { if (_himlCursors) { ImageList_Destroy(_himlCursors); _himlCursors = NULL; }
HCURSOR hcursor = GetCursor(); for (int i = 0; i < ARRAYSIZE(_ahcur); i++) { if (_ahcur[i]) { if (_ahcur[i] == hcursor) { //
// Stuff in some random cursor so that we don't try to
// destroy the current cursor (and leak it too).
//
SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); } DestroyCursor(_ahcur[i]); _ahcur[i] = NULL; } } }
HBITMAP CDragImages::CreateColorBitmap(int cx, int cy) { HDC hdc = GetDC(NULL); HBITMAP hbm = CreateCompatibleBitmap(hdc, cx, cy); ReleaseDC(NULL, hdc); return hbm; }
#define CreateMonoBitmap( cx, cy) CreateBitmap(cx, cy, 1, 1, NULL)
typedef WORD CURMASK; #define _BitSizeOf(x) (sizeof(x)*8)
HRESULT CDragImages::_GetCursorLowerRight(HCURSOR hcursor, int * px, int * py, POINT *pptHotSpot) { ICONINFO iconinfo; HRESULT hr = E_FAIL; if (GetIconInfo(hcursor, &iconinfo)) { CURMASK CurMask[16*8]; BITMAP bm; int i; int xFine = 16;
GetObject(iconinfo.hbmMask, sizeof(bm), (LPTSTR)&bm); GetBitmapBits(iconinfo.hbmMask, sizeof(CurMask), CurMask); pptHotSpot->x = iconinfo.xHotspot; pptHotSpot->y = iconinfo.yHotspot; if (iconinfo.hbmColor) { i = (int)(bm.bmWidth * bm.bmHeight / _BitSizeOf(CURMASK) - 1); } else { i = (int)(bm.bmWidth * (bm.bmHeight/2) / _BitSizeOf(CURMASK) - 1); }
if ( i >= sizeof(CurMask)) { i = sizeof(CurMask) -1; }
// this assumes that the first pixel encountered on this bottom
// up/right to left search will be reasonably close to the rightmost pixel
// which for all of our cursors is correct, but it not necessarly correct.
// also, it assumes the cursor has a good mask... not like the IBeam XOR only
// cursor
for (; i >= 0; i--) { if (CurMask[i] != 0xFFFF) { // this is only accurate to 16 pixels... which is a big gap..
// so let's try to be a bit more accurate.
int j; DWORD dwMask;
for (j = 0; j < 16; j++, xFine--) { if (j < 8) { dwMask = (1 << (8 + j)); } else { dwMask = (1 << (j - 8)); }
if (!(CurMask[i] & dwMask)) break; } ASSERT(j < 16); break; } }
if (iconinfo.hbmColor) { DeleteObject(iconinfo.hbmColor); }
if (iconinfo.hbmMask) { DeleteObject(iconinfo.hbmMask); }
// Compute the pointer height
// use width in both directions because the cursor is square, but the
// height might be doubleheight if it's mono
*py = ((i + 1) * _BitSizeOf(CURMASK)) / (int)bm.bmWidth; *px = ((i * _BitSizeOf(CURMASK)) % (int)bm.bmWidth) + xFine + 2; // hang it off a little
hr = S_OK; } return hr; }
// this will draw iiMerge's image over iiMain on main's lower right.
BOOL CDragImages::_MergeIcons(HCURSOR hcursor, LPCTSTR idMerge, HBITMAP *phbmImage, HBITMAP *phbmMask, POINT* pptHotSpot) { *phbmImage = NULL; *phbmMask = NULL;
BOOL fRet = FALSE;
int xDraw; int yDraw; // find the lower corner of the cursor and put it there.
// do this whether or not we have an idMerge because it will set the hotspot
if (SUCCEEDED(_GetCursorLowerRight(hcursor, &xDraw, &yDraw, pptHotSpot))) { int xBitmap; int yBitmap; int xCursor = GetSystemMetrics(SM_CXCURSOR); int yCursor = GetSystemMetrics(SM_CYCURSOR); HBITMAP hbmp; if (idMerge != (LPCTSTR)-1) { hbmp = (HBITMAP)LoadImage(HINST_THISDLL, idMerge, IMAGE_BITMAP, 0, 0, 0); if (hbmp) { BITMAP bm; GetObject(hbmp, sizeof(bm), &bm); xBitmap = bm.bmWidth; yBitmap = bm.bmHeight/2;
if (xDraw + xBitmap > xCursor) xDraw = xCursor - xBitmap; if (yDraw + yBitmap > yCursor) yDraw = yCursor - yBitmap; } } else hbmp = NULL;
HDC hdcCursor = CreateCompatibleDC(NULL);
HBITMAP hbmMask = CreateMonoBitmap(xCursor, yCursor); HBITMAP hbmImage = CreateColorBitmap(xCursor, yCursor);
if (hdcCursor && hbmMask && hbmImage) { HBITMAP hbmTemp = (HBITMAP)SelectObject(hdcCursor, hbmImage); DrawIconEx(hdcCursor, 0, 0, hcursor, 0, 0, 0, NULL, DI_NORMAL);
HDC hdcBitmap; if (hbmp) { hdcBitmap = CreateCompatibleDC(NULL); SelectObject(hdcBitmap, hbmp);
//blt the two bitmaps onto the color and mask bitmaps for the cursor
BitBlt(hdcCursor, xDraw, yDraw, xBitmap, yBitmap, hdcBitmap, 0, 0, SRCCOPY); }
SelectObject(hdcCursor, hbmMask);
DrawIconEx(hdcCursor, 0, 0, hcursor, 0, 0, 0, NULL, DI_MASK);
if (hbmp) { BitBlt(hdcCursor, xDraw, yDraw, xBitmap, yBitmap, hdcBitmap, 0, yBitmap, SRCCOPY);
// select back in the old bitmaps
SelectObject(hdcBitmap, hbmTemp); DeleteDC(hdcBitmap); DeleteObject(hbmp); }
// select back in the old bitmaps
SelectObject(hdcCursor, hbmTemp); }
if (hdcCursor) DeleteDC(hdcCursor);
*phbmImage = hbmImage; *phbmMask = hbmMask; fRet = (hbmImage && hbmMask); } return fRet; }
// this will take a cursor index and load
int CDragImages::_AddCursorToImageList(HCURSOR hcur, LPCTSTR idMerge, POINT *pptHotSpot) { int iIndex; HBITMAP hbmImage, hbmMask;
// merge in the plus or link arrow if it's specified
if (_MergeIcons(hcur, idMerge, &hbmImage, &hbmMask, pptHotSpot)) { iIndex = ImageList_Add(_himlCursors, hbmImage, hbmMask); } else { iIndex = -1; }
if (hbmImage) DeleteObject(hbmImage);
if (hbmMask) DeleteObject(hbmMask);
return iIndex; }
int _MapEffectToId(DWORD dwEffect) { int idCursor;
// DebugMsg(DM_TRACE, "sh TR - DAD_GiveFeedBack dwEffect=%x", dwEffect);
switch (dwEffect & (DROPEFFECT_COPY|DROPEFFECT_LINK|DROPEFFECT_MOVE)) { case 0: idCursor = DCID_NO; break;
case DROPEFFECT_COPY: idCursor = DCID_COPY; break;
case DROPEFFECT_LINK: idCursor = DCID_LINK; break;
case DROPEFFECT_MOVE: idCursor = DCID_MOVE; break;
default: // if it's a right drag, we can have any effect... we'll
// default to the arrow without merging in anything
idCursor = DCID_MOVE; break; }
return idCursor; }
int CDragImages::_MapCursorIDToImageListIndex(int idCur) { const static struct { BOOL fSystem; LPCTSTR idRes; LPCTSTR idMerge; } c_acurmap[DCID_MAX] = { { FALSE, MAKEINTRESOURCE(IDC_NULL), (LPCTSTR)-1}, { TRUE, IDC_NO, (LPCTSTR)-1 }, { TRUE, IDC_ARROW, (LPCTSTR)-1 }, { TRUE, IDC_ARROW, MAKEINTRESOURCE(IDB_PLUS_MERGE) }, { TRUE, IDC_ARROW, MAKEINTRESOURCE(IDB_LINK_MERGE) }, };
ASSERT(idCur >= -1 && idCur < (int)ARRAYSIZE(c_acurmap));
// -1 means "Initialize the image list index array".
if (idCur == -1) { for (int i = 0; i < ARRAYSIZE(c_acurmap); i++) { _aindex[i] = -1; } idCur = 0; // fall through to return -1
} else { if (_aindex[idCur] == -1) { HINSTANCE hinst = c_acurmap[idCur].fSystem ? NULL : HINST_THISDLL; HCURSOR hcur = LoadCursor(hinst, c_acurmap[idCur].idRes); if (hcur) { _aindex[idCur] = _AddCursorToImageList(hcur, c_acurmap[idCur].idMerge, &_aptHotSpot[idCur]); } } } return _aindex[idCur]; }
HCURSOR CDragImages::_SetCursorHotspot(HCURSOR hcur, POINT *ptHot) { ICONINFO iconinfo = { 0 }; HCURSOR hcurHotspot;
GetIconInfo(hcur, &iconinfo); iconinfo.xHotspot = ptHot->x; iconinfo.yHotspot = ptHot->y; iconinfo.fIcon = FALSE; hcurHotspot = (HCURSOR)CreateIconIndirect(&iconinfo); if (iconinfo.hbmColor) { DeleteObject(iconinfo.hbmColor); }
if (iconinfo.hbmMask) { DeleteObject(iconinfo.hbmMask); } return hcurHotspot; }
void CDragImages::SetDropEffectCursor(int idCur) { if (_himlCursors && (idCur != -1)) { if (!_ahcur[idCur]) { int iIndex = _MapCursorIDToImageListIndex(idCur); if (iIndex != -1) { HCURSOR hcurColor = ImageList_GetIcon(_himlCursors, iIndex, 0); //
// On non C1_COLORCURSOR displays, CopyImage() will enforce
// monochrome. So on color cursor displays, we'll get colored
// dragdrop pix.
//
HCURSOR hcurScreen = (HCURSOR)CopyImage(hcurColor, IMAGE_CURSOR, 0, 0, LR_COPYRETURNORG | LR_DEFAULTSIZE);
HCURSOR hcurFinal = _SetCursorHotspot(hcurScreen, &_aptHotSpot[idCur]);
if ((hcurScreen != hcurColor) && hcurColor) { DestroyCursor(hcurColor); }
if (hcurFinal) { if (hcurScreen) { DestroyCursor(hcurScreen); } } else { hcurFinal = hcurScreen; }
_ahcur[idCur] = hcurFinal; } }
if (_ahcur[idCur]) { //
// This code assumes that SetCursor is pretty quick if it is
// already set.
//
SetCursor(_ahcur[idCur]); } } }
//=====================================================================
// CDropSource
//=====================================================================
class CDropSource : public IDropSource { private: LONG _cRef; DWORD _grfInitialKeyState; IDataObject* _pdtobj;
public: explicit CDropSource(IDataObject *pdtobj); virtual ~CDropSource();
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IDropSource methods
STDMETHODIMP GiveFeedback(DWORD dwEffect); STDMETHODIMP QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState); };
void DAD_ShowCursor(BOOL fShow) { static BOOL s_fCursorHidden = FALSE;
if (fShow) { if (s_fCursorHidden) { ShowCursor(TRUE); s_fCursorHidden = FALSE; } } else { if (!s_fCursorHidden) { ShowCursor(FALSE); s_fCursorHidden = TRUE; } } }
CDropSource::CDropSource(IDataObject *pdtobj) : _cRef(1), _pdtobj(pdtobj), _grfInitialKeyState(0) { _pdtobj->AddRef(); // Tell the data object that we're entering the drag loop.
DataObj_SetDWORD(_pdtobj, g_cfInDragLoop, 1); }
CDropSource::~CDropSource() { DAD_ShowCursor(TRUE); // just in case
_pdtobj->Release(); }
//
// Create an instance of CDropSource
//
STDMETHODIMP CDropSource_CreateInstance(IDropSource **ppdsrc, IDataObject *pdtobj) { *ppdsrc = new CDropSource(pdtobj); return *ppdsrc ? S_OK : E_OUTOFMEMORY; }
STDMETHODIMP CDropSource::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDropSource, IDropSource), { 0 }, }; return QISearch(this, qit, riid, ppvObj); }
STDMETHODIMP_(ULONG) CDropSource::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CDropSource::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
STDMETHODIMP CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState) { HRESULT hr = S_OK;
if (fEscapePressed) { hr = DRAGDROP_S_CANCEL; } else { // initialize ourself with the drag begin button
if (_grfInitialKeyState == 0) _grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
// If the window is hung for a while, the drag operation can happen before
// the first call to this function, so grfInitialKeyState will be 0. If this
// happened, then we did a drop. No need to assert...
//ASSERT(this->grfInitialKeyState);
if (!(grfKeyState & _grfInitialKeyState)) { //
// A button is released.
//
hr = DRAGDROP_S_DROP; } else if (_grfInitialKeyState != (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON))) { //
// If the button state is changed (except the drop case, which we handle
// above, cancel the drag&drop.
//
hr = DRAGDROP_S_CANCEL; } }
if (hr != S_OK) { SetCursor(LoadCursor(NULL, IDC_ARROW)); DAD_ShowCursor(TRUE); DAD_SetDragCursor(DCID_NULL);
// Tell the data object that we're leaving the drag loop.
if (_pdtobj) DataObj_SetDWORD(_pdtobj, g_cfInDragLoop, 0); }
return hr; }
STDMETHODIMP CDropSource::GiveFeedback(DWORD dwEffect) { int idCursor = _MapEffectToId(dwEffect);
//
// OLE does not give us DROPEFFECT_MOVE even though our IDT::DragOver
// returns it, if we haven't set that bit when we have called DoDragDrop.
// Instead of arguing whether or not this is a bug or by-design of OLE,
// we work around it. It is important to note that this hack around
// g_fDraggingOverSource is purely visual hack. It won't affect the
// actual drag&drop operations at all (DV_AlterEffect does it all).
//
// - SatoNa
//
if (idCursor == DCID_NO && g_fDraggingOverSource) { idCursor = DCID_MOVE; } //
// No need to merge the cursor, if we are not dragging over to
// one of shell windows.
//
if (DAD_IsDraggingImage()) { // Feedback for single (image) dragging
DAD_ShowCursor(FALSE); DAD_SetDragCursor(idCursor); } else if (DAD_IsDragging() && g_pdiDragImages) { // Feedback for multiple (rectangles) dragging
g_pdiDragImages->SetDropEffectCursor(idCursor); DAD_ShowCursor(TRUE); return NOERROR; } else { DAD_ShowCursor(TRUE); }
return DRAGDROP_S_USEDEFAULTCURSORS; }
//=====================================================================
// DAD
//=====================================================================
void FixupDragPoint(HWND hwnd, POINT* ppt) { if (hwnd) { RECT rc = {0}; GetWindowRect(hwnd, &rc); ppt->x += rc.left; ppt->y += rc.top; } }
BOOL DAD_InitDragImages() { if (!g_pdiDragImages) CDragImages_CreateInstance(NULL, IID_IDragSourceHelper, NULL);
return g_pdiDragImages != NULL; }
STDAPI_(BOOL) DAD_ShowDragImage(BOOL bShow) { if (DAD_InitDragImages()) return g_pdiDragImages->Show(bShow) == S_OK ? TRUE : FALSE; return FALSE; }
BOOL DAD_IsDragging() { if (DAD_InitDragImages()) return g_pdiDragImages->IsDragging(); return FALSE; }
void DAD_SetDragCursor(int idCursor) { if (DAD_InitDragImages()) g_pdiDragImages->SetDragCursor(idCursor); }
STDAPI_(BOOL) DAD_DragEnterEx3(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtobj) { RECT rc; GetWindowRect(hwndTarget, &rc);
// If hwndTarget is RTL mirrored, then measure the
// the client point from the visual right edge
// (near edge in RTL mirrored windows). [samera]
POINT pt; if (IS_WINDOW_RTL_MIRRORED(hwndTarget)) pt.x = rc.right - ptStart.x; else pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top; return DAD_DragEnterEx2(hwndTarget, pt, pdtobj); }
STDAPI_(BOOL) DAD_DragEnterEx2(HWND hwndTarget, const POINT ptStart, IDataObject *pdtobj) { BOOL bRet = FALSE; if (DAD_InitDragImages()) { POINT pt = ptStart; FixupDragPoint(hwndTarget, &pt); bRet = SUCCEEDED(g_pdiDragImages->DragEnter(hwndTarget, pdtobj, &pt, NULL)); } return bRet; }
STDAPI_(BOOL) DAD_DragEnterEx(HWND hwndTarget, const POINT ptStart) { return DAD_DragEnterEx2(hwndTarget, ptStart, NULL); }
STDAPI_(BOOL) DAD_DragEnter(HWND hwndTarget) { POINT ptStart;
GetCursorPos(&ptStart); if (hwndTarget) ScreenToClient(hwndTarget, &ptStart);
return DAD_DragEnterEx(hwndTarget, ptStart); }
STDAPI_(BOOL) DAD_DragMoveEx(HWND hwndTarget, const POINTL ptStart) { RECT rc; GetWindowRect(hwndTarget, &rc);
// If hwndTarget is RTL mirrored, then measure the
// the client point from the visual right edge
// (near edge in RTL mirrored windows). [samera]
POINT pt; if (IS_WINDOW_RTL_MIRRORED(hwndTarget)) pt.x = rc.right - ptStart.x; else pt.x = ptStart.x - rc.left;
pt.y = ptStart.y - rc.top; return DAD_DragMove(pt); }
STDAPI_(BOOL) DAD_DragMove(POINT pt) { if (DAD_InitDragImages()) { FixupDragPoint(g_pdiDragImages->GetTarget(), &pt); return g_pdiDragImages->DragOver(&pt, 0); } return FALSE; }
STDAPI_(BOOL) DAD_SetDragImage(HIMAGELIST him, POINT *pptOffset) { if (DAD_InitDragImages() && !g_pdiDragImages->IsDraggingLayeredWindow()) { //
// DAD_SetDragImage(-1, NULL) means "clear the drag image only
// if the image is set by this thread"
//
if (him == (HIMAGELIST)-1) { BOOL fThisThreadHasImage = FALSE; ENTERCRITICAL; if (g_pdiDragImages->Initialized() && g_pdiDragImages->GetThread() == GetCurrentThreadId()) { fThisThreadHasImage = TRUE; } LEAVECRITICAL;
if (fThisThreadHasImage) { g_pdiDragImages->FreeDragData(); return TRUE; } return FALSE; }
return g_pdiDragImages->SetDragImage(him, 0, pptOffset); }
return TRUE; }
//
// This function returns TRUE, if we are dragging an image. It means
// you have called either DAD_SetDragImage (with him != NULL) or
// DAD_SetDragImageFromListview.
//
BOOL DAD_IsDraggingImage(void) { if (DAD_InitDragImages()) return g_pdiDragImages->IsDraggingImage(); return FALSE; }
STDAPI_(BOOL) DAD_DragLeave() { if (DAD_InitDragImages()) return g_pdiDragImages->DragLeave(); return FALSE; }
STDAPI_(void) DAD_ProcessDetach(void) { if (g_pdiDragImages) { g_pdiDragImages->ProcessDetach(); g_pdiDragImages->Release(); } }
STDAPI_(void) DAD_ThreadDetach(void) { if (g_pdiDragImages) g_pdiDragImages->ThreadDetach(); }
// called from defview on SPI_SETCURSORS (user changed the system cursors)
STDAPI_(void) DAD_InvalidateCursors(void) { g_cRev++; }
STDAPI_(BOOL) DAD_SetDragImageFromWindow(HWND hwnd, POINT* ppt, IDataObject* pdtobj) { if (DAD_InitDragImages()) return S_OK == g_pdiDragImages->InitializeFromWindow(hwnd, ppt, pdtobj); return FALSE; }
// shell32.dll export, but only used by print queue window code
//
STDAPI_(BOOL) DAD_SetDragImageFromListView(HWND hwndLV, POINT ptOffset) { // really a nop, as this does not have access to the data object
return DAD_InitDragImages(); }
// wrapper around OLE DoDragDrop(), will create drag source on demand and supports
// drag images for you
STDAPI SHDoDragDrop(HWND hwnd, IDataObject *pdtobj, IDropSource *pdsrc, DWORD dwEffect, DWORD *pdwEffect) { IDropSource *pdsrcRelease = NULL;
if (pdsrc == NULL) { CDropSource_CreateInstance(&pdsrcRelease, pdtobj); pdsrc = pdsrcRelease; }
// if there is no drag contents clipboard format present, try to add it
FORMATETC fmte = {_GetDragContentsCF(), NULL, DVASPECT_CONTENT, -1, TYMED_ISTREAM}; if (S_OK != pdtobj->QueryGetData(&fmte)) { if (DAD_InitDragImages()) g_pdiDragImages->InitializeFromWindow(hwnd, NULL, pdtobj); }
HRESULT hr = DoDragDrop(pdtobj, pdsrc, dwEffect, pdwEffect);
if (pdsrcRelease) pdsrcRelease->Release();
return hr; }
|