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

1185 lines
31 KiB

// History:
// 02-03-93 SatoNa Removed obsolete DropFiles()
//
#include "shellprv.h"
#pragma hdrstop
// Function prototype
void _DestroyCachedCursors();
typedef struct _DAD_DRAGCONTEXT // dadc
{
HWND hwndFrom;
BOOL fImage;
POINT ptOffset;
DWORD _idProcess;
DWORD _idThread;
// _ds 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;
} mlt;
} _ds;
// following fields are used only when fImage==FALSE
UINT cItems; //
RECT arc[1]; // cItems
} DAD_DRAGCONTEXT, * LPDAD_DRAGCONTEXT;
int _MapCursorIDToImageListIndex(int idCur);
// move to commctrl\cutils.c
/*
* QueryDropObject() -
*
* Determines where in the window heirarchy the "drop" takes place, and
* sends a message to the deepest child window first. If that window does
* not respond, we go up the heirarchy (recursively, for the moment) until
* we either get a window that does respond or the parent doesn't respond.
*
* in:
*
* out:
* lpds->ptDrop set to the point of the query (window coordinates)
* lpds->hwndSink the window that answered the query
*
* returns:
* value from WM_QUERYDROPOBJECT (0, 1, or hCursor)
*/
HCURSOR QueryDropObject(HWND hwnd, LPDROPSTRUCT lpds)
{
HWND hwndT;
HCURSOR hCurT = 0;
POINT pt;
BOOL fNC;
RECT rc;
pt = lpds->ptDrop; /* pt is in screen coordinates */
GetWindowRect(hwnd, &rc);
/* reject points outside this window or if the window is disabled */
if (!PtInRect(&rc, pt) || !IsWindowEnabled(hwnd))
return NULL;
/* are we dropping in the nonclient area of the window or on an iconic
* window? */
GetClientRect(hwnd, &rc);
MapWindowPoints(hwnd, NULL, (LPPOINT)&rc, 2);
if (IsMinimized(hwnd) || !PtInRect(&rc, pt)) {
fNC = TRUE;
ScreenToClient(hwnd, &lpds->ptDrop);
goto SendQueryDrop;
}
fNC = FALSE; /* dropping in client area */
for (hwndT = GetWindow(hwnd, GW_CHILD); hwndT && !hCurT; hwndT = GetWindow(hwndT, GW_HWNDNEXT)) {
if (!IsWindowVisible(hwndT)) /* Ignore invisible windows */
continue;
GetWindowRect(hwndT, &rc);
if (!PtInRect(&rc, pt)) /* not in window? skip it*/
continue;
if (!IsWindowEnabled(hwndT))
/* If point is in a disabled, visible window, get the heck out. No
* need to check further since no drops allowed here. */
break;
/* recursively search child windows for the drop place */
hCurT = QueryDropObject(hwndT, lpds);
/* don't look at windows below this one in the zorder
*/
break;
}
if (!hCurT) {
/* there are no children who are in the right place or who want
* drops... convert the point into client coordinates of the
* current window. Because of the recursion, this is already
* done if a child window grabbed the drop. */
ScreenToClient(hwnd, &lpds->ptDrop);
SendQueryDrop:
lpds->hwndSink = hwnd;
hCurT = (HCURSOR)SendMessage(hwnd, WM_QUERYDROPOBJECT, fNC, (LPARAM)lpds);
/* restore drop point to screen coordinates if this window won't take
* drops */
if (!hCurT)
lpds->ptDrop = pt;
}
return hCurT;
}
//=====================================================================
// Multile Drag show
//=====================================================================
void _MultipleDragShow(LPDAD_DRAGCONTEXT pdadc, BOOL bShow)
{
HDC hDC;
int nRect;
RECT rc;
int cxScreen = GetSystemMetrics(SM_CXSCREEN);
int cyScreen = GetSystemMetrics(SM_CYSCREEN);
if ((bShow && pdadc->_ds.mlt.bShown) || (!bShow && !pdadc->_ds.mlt.bShown))
{
return;
}
pdadc->_ds.mlt.bShown = bShow;
hDC = GetDCEx(pdadc->_ds.hwndLock, NULL, DCX_WINDOW | DCX_CACHE |
DCX_LOCKWINDOWUPDATE | DCX_CLIPSIBLINGS);
for (nRect = pdadc->_ds.mlt.nRects - 1; nRect >= 0; --nRect)
{
rc = pdadc->_ds.mlt.pRect[nRect];
OffsetRect(&rc, pdadc->_ds.mlt.ptNow.x-pdadc->_ds.mlt.ptOffset.x, pdadc->_ds.mlt.ptNow.y-pdadc->_ds.mlt.ptOffset.y);
if (rc.left < cxScreen && rc.right > 0 &&
rc.top < cyScreen && rc.bottom > 0)
{
DrawFocusRect(hDC, &rc);
}
}
ReleaseDC(pdadc->_ds.hwndLock, hDC);
}
void _MultipleDragStart(LPDAD_DRAGCONTEXT pdadc, HWND hwndLock, LPRECT aRect, int nRects, POINT ptStart, POINT ptOffset)
{
pdadc->_ds.mlt.bShown = FALSE;
pdadc->_ds.mlt.pRect = aRect;
pdadc->_ds.mlt.nRects = nRects;
pdadc->_ds.mlt.ptOffset = ptOffset;
pdadc->_ds.mlt.ptNow = ptStart;
}
void _MultipleDragMove(LPDAD_DRAGCONTEXT pdadc, POINT ptNew)
{
BOOL bShown;
// nothing has changed. bail
if (pdadc->_ds.mlt.bShown &&
pdadc->_ds.mlt.ptNow.x == ptNew.x &&
pdadc->_ds.mlt.ptNow.y == ptNew.y)
return;
bShown = pdadc->_ds.mlt.bShown;
_MultipleDragShow(pdadc, FALSE);
pdadc->_ds.mlt.ptNow = ptNew;
/* Only show the drag if it was already shown.
*/
if (bShown)
{
_MultipleDragShow(pdadc, TRUE);
}
}
//=====================================================================
// DAD
//=====================================================================
//
// WARNING: s_pdadc MUST be in shared data section.
//
DAD_DRAGCONTEXT * s_pdadc = NULL;
UINT g_cRev = 0;
//
// 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).
//
BOOL g_fDraggingOverSource = FALSE; // shared
#pragma data_seg(DATASEG_PERINSTANCE)
struct {
HIMAGELIST _himl;
UINT _cRev; // == g_cRev if it is still valid
int _aindex[DCID_MAX]; // will be initialized.
HCURSOR _ahcur[DCID_MAX];
POINT _aptHotSpot[DCID_MAX];
} s_cursors = { NULL, 0, /* { -1, -1, -1, -1, -1 } */ };
#pragma data_seg()
//
// We don't want to destroy the cached cursors now. We simply increment
// g_cRef (global) to make it different from s_cursors._cRef.
//
void DAD_InvalidateCursors(void)
{
g_cRev++;
}
void _SetDragContext(LPDAD_DRAGCONTEXT pdadc)
{
if (pdadc)
{
s_pdadc = pdadc;
pdadc->_idProcess = GetCurrentProcessId();
pdadc->_idThread = GetCurrentThreadId();
//
// If the cached cursors are invalidated (by DAD_InvalidateCursors)
// we need to destroy the old one and recreate the new one.
//
if (s_cursors._himl && (s_cursors._cRev!=g_cRev)) {
_DestroyCachedCursors();
}
if (!s_cursors._himl)
{
UINT uFlags = ILC_MASK | ILC_SHARED;
HDC hdc;
//
// if this is not a palette device, use a DDB for the imagelist
// this is important when displaying high-color cursors
//
hdc = GetDC(NULL);
if (!(GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE))
{
uFlags |= ILC_COLORDDB;
}
ReleaseDC(NULL, hdc);
s_cursors._himl = ImageList_Create(
GetSystemMetrics(SM_CXCURSOR),
GetSystemMetrics(SM_CYCURSOR),
uFlags, 1, 0);
s_cursors._cRev = g_cRev;
// We need to initialize s_cursors._aindex[*]
_MapCursorIDToImageListIndex(DCID_INVALID);
}
}
else
{
if (s_pdadc)
{
if (s_pdadc->fImage)
{
ImageList_EndDrag();
}
Free(s_pdadc);
s_pdadc=NULL;
}
}
}
// 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
BOOL WINAPI DAD_ShowDragImage(BOOL bShow)
{
BOOL fOld;
if (!s_pdadc || !s_pdadc->_ds.bDragging)
{
return FALSE;
}
fOld = s_pdadc->_ds.bLocked;
//
// If we are going to show the drag image, lock the target window.
//
if (bShow && !s_pdadc->_ds.bLocked)
{
UpdateWindow(s_pdadc->_ds.hwndLock);
LockWindowUpdate(s_pdadc->_ds.hwndLock);
s_pdadc->_ds.bLocked = TRUE;
}
if (s_pdadc->_ds.bSingle)
{
ImageList_DragShowNolock(bShow);
}
else
{
_MultipleDragShow(s_pdadc, bShow);
}
//
// If we have just hide the drag image, unlock the target window.
//
if (!bShow && s_pdadc->_ds.bLocked)
{
LockWindowUpdate(NULL);
s_pdadc->_ds.bLocked = FALSE;
}
return fOld;
}
#define SCROLLDELAY 250 // 1/4 second
BOOL DAD_IsDragging()
{
return (s_pdadc && s_pdadc->_ds.bDragging);
}
void _DestroyCachedCursors()
{
int i;
if (s_cursors._himl) {
ImageList_Destroy(s_cursors._himl);
s_cursors._himl = NULL;
}
for (i=0 ; i<DCID_MAX ; i++) {
if (s_cursors._ahcur[i])
{
DestroyCursor(s_cursors._ahcur[i]);
s_cursors._ahcur[i] = NULL;
}
}
}
void DAD_ProcessDetach(void)
{
if (s_pdadc && s_pdadc->_idProcess==GetCurrentProcessId())
{
DAD_SetDragImage(NULL, NULL);
}
_DestroyCachedCursors();
}
void DAD_ThreadDetach(void)
{
if (s_pdadc && s_pdadc->_idProcess==GetCurrentProcessId() && s_pdadc->_idThread==GetCurrentThreadId())
{
DAD_SetDragImage(NULL, NULL);
}
}
BOOL _SetDragImage(HIMAGELIST himl, int index, POINT * pptOffset)
{
if (himl)
{
DAD_DRAGCONTEXT * pdadc;
// We are setting
if (s_pdadc) {
return FALSE;
}
pdadc = Alloc(SIZEOF(DAD_DRAGCONTEXT));
if (pdadc)
{
pdadc->fImage = TRUE;
if (pptOffset) {
// Avoid the flicker by always pass even coords
pdadc->ptOffset.x = (pptOffset->x & ~1);
pdadc->ptOffset.y = (pptOffset->y & ~1);
}
ImageList_BeginDrag(himl, index, pdadc->ptOffset.x, pdadc->ptOffset.y);
}
_SetDragContext(pdadc);
}
else
{
// We are unsetting
_SetDragContext(NULL);
}
return TRUE;
}
BOOL WINAPI DAD_SetDragImage(HIMAGELIST him, POINT * pptOffset)
{
//
// 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 (s_pdadc && s_pdadc->_idThread == GetCurrentThreadId())
{
fThisThreadHasImage=TRUE;
}
LEAVECRITICAL;
if (fThisThreadHasImage)
{
return _SetDragImage(NULL, 0, NULL);
}
return FALSE;
}
return _SetDragImage(him, 0, pptOffset);
}
#define ListView_IsIconView(hwndLV) ((GetWindowLong(hwndLV, GWL_STYLE) & (UINT)LVS_TYPEMASK) == (UINT)LVS_ICON)
BOOL _SetMultiItemDragging(HWND hwndLV, int cItems, LPPOINT pptOffset)
{
LPDAD_DRAGCONTEXT pdadc = NULL; // assume error
BOOL fRet = FALSE;
if (s_pdadc) {
return FALSE;
}
// Multiple item drag
pdadc = Alloc(SIZEOF(DAD_DRAGCONTEXT)-SIZEOF(RECT) + 2*cItems*SIZEOF(RECT));
if (pdadc)
{
POINT ptTemp;
int iLast, iNext;
int cxScreen, cyScreen;
LPRECT prcNext;
pdadc->cItems = 0;
Assert(pdadc->fImage == FALSE);
ptTemp.x = ptTemp.y = 0;
ClientToScreen(hwndLV, &ptTemp);
cxScreen = GetSystemMetrics(SM_CXSCREEN);
cyScreen = GetSystemMetrics(SM_CYSCREEN);
for (iNext=cItems-1, iLast=-1, prcNext=pdadc->arc; 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) < cxScreen) &&
((pptOffset->x - prcNext[0].right) < cxScreen) &&
((prcNext[0].top - pptOffset->y) < cyScreen)) {
ListView_GetItemRect(hwndLV, iLast, &prcNext[1], LVIR_LABEL);
OffsetRect(&prcNext[1], ptTemp.x, ptTemp.y);
if ((pptOffset->y - prcNext[1].bottom) < cxScreen) {
//
// 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;
pdadc->cItems += 2;
}
}
}
}
// Avoid the flicker by always pass even coords
pdadc->ptOffset.x = (pptOffset->x & ~1);
pdadc->ptOffset.y = (pptOffset->y & ~1);
pdadc->hwndFrom = hwndLV;
_SetDragContext(pdadc);
fRet = TRUE;
}
return fRet;
}
//
// This function allocate a shared memory block which contains either
// a set of images (currently always one) or a set of rectangles.
//
// Notes: NEVER think about making this function public!
//
BOOL WINAPI DAD_SetDragImageFromListView(HWND hwndLV, POINT ptOffset)
{
POINT ptTemp;
HIMAGELIST himl;
//
// Count the number of selected items.
//
int cItems = ListView_GetSelectedCount(hwndLV);
switch (cItems)
{
case 0:
// There's nothing to drag
break;
case 1:
if (NULL != (himl = ListView_CreateDragImage(hwndLV,
ListView_GetNextItem(hwndLV, -1, LVNI_SELECTED), &ptTemp)))
{
ClientToScreen(hwndLV, &ptTemp);
ptOffset.x -= ptTemp.x;
ptOffset.y -= ptTemp.y;
_SetDragImage(himl, 0, &ptOffset);
ImageList_Destroy(himl);
return TRUE;
}
break;
default:
return _SetMultiItemDragging(hwndLV, cItems, &ptOffset);
}
return FALSE;
}
BOOL WINAPI DAD_DragEnterEx(HWND hwndTarget, const POINT ptStart)
{
if (s_pdadc)
{
DAD_SetDragCursor(DCID_INVALID);
s_pdadc->_ds.bDragging = TRUE;
s_pdadc->_ds.bSingle = s_pdadc->fImage;
s_pdadc->_ds.hwndLock = hwndTarget ? hwndTarget : GetDesktopWindow();
s_pdadc->_ds.bLocked = FALSE;
s_pdadc->_ds.idThreadEntered = GetCurrentThreadId();
if (s_pdadc->fImage)
{
// Avoid the flicker by always pass even coords
ImageList_DragEnter(hwndTarget, ptStart.x & ~1, ptStart.y & ~1);
}
else
{
_MultipleDragStart(s_pdadc, hwndTarget, s_pdadc->arc, s_pdadc->cItems, ptStart, s_pdadc->ptOffset);
}
//
// We should always show the image whenever this function is called.
//
DAD_ShowDragImage(TRUE);
}
return TRUE;
}
BOOL WINAPI DAD_DragEnter(HWND hwndTarget)
{
POINT ptStart;
GetCursorPos(&ptStart);
if (hwndTarget) {
ScreenToClient(hwndTarget, &ptStart);
}
return DAD_DragEnterEx(hwndTarget, ptStart);
}
BOOL WINAPI DAD_DragMove(POINT pt)
{
if (s_pdadc)
{
// Avoid the flicker by always pass even coords
pt.x &= ~1;
pt.y &= ~1;
if (s_pdadc->fImage)
{
ImageList_DragMove(pt.x, pt.y);
}
else
{
_MultipleDragMove(s_pdadc, pt);
}
}
return TRUE;
}
BOOL WINAPI DAD_DragLeave()
{
if ( s_pdadc && s_pdadc->_ds.bDragging &&
s_pdadc->_ds.idThreadEntered == GetCurrentThreadId() )
{
DAD_ShowDragImage(FALSE);
if (s_pdadc->fImage)
{
ImageList_DragLeave(s_pdadc->_ds.hwndLock);
}
s_pdadc->_ds.bDragging = FALSE;
}
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)
{
return (s_pdadc && s_pdadc->fImage && s_pdadc->_ds.bDragging);
}
HBITMAP CreateColorBitmap(int cx, int cy)
{
HDC hdc;
HBITMAP hbm;
hdc = GetDC(NULL);
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)
void _GetCursorLowerRight(HCURSOR hcursor, int * px, int * py, POINT *pptHotSpot)
{
ICONINFO iconinfo;
CURMASK CurMask[16*8];
BITMAP bm;
int i;
int xFine = 16;
GetIconInfo(hcursor, &iconinfo);
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;
// BUGBUG: 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
}
// this will draw iiMerge's image over iiMain on main's lower right.
BOOL _MergeIcons(HCURSOR hcursor, LPCTSTR idMerge, HBITMAP *phbmImage, HBITMAP *phbmMask, POINT* pptHotSpot)
{
BITMAP bm;
int xBitmap;
int yBitmap;
int xDraw;
int yDraw;
HDC hdcCursor, hdcBitmap;
HBITMAP hbmTemp;
HBITMAP hbmImage;
HBITMAP hbmMask;
int xCursor = GetSystemMetrics(SM_CXCURSOR);
int yCursor = GetSystemMetrics(SM_CYCURSOR);
HBITMAP hbmp;
// 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
_GetCursorLowerRight(hcursor, &xDraw, &yDraw, pptHotSpot);
if (idMerge != (LPCTSTR)-1) {
hbmp = LoadImage(HINST_THISDLL, idMerge, IMAGE_BITMAP, 0, 0, 0);
if (hbmp) {
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;
hdcCursor = CreateCompatibleDC(NULL);
hbmMask = CreateMonoBitmap(xCursor, yCursor);
hbmImage = CreateColorBitmap(xCursor, yCursor);
if (hdcCursor && hbmMask && hbmImage) {
hbmTemp = SelectObject(hdcCursor, hbmImage);
DrawIconEx(hdcCursor, 0, 0, hcursor, 0, 0, 0, NULL, DI_NORMAL);
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;
return (hbmImage && hbmMask);
}
// this will take a cursor index and load
int _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(s_cursors._himl, hbmImage, hbmMask);
} else {
iIndex = -1;
}
if (hbmImage)
DeleteObject(hbmImage);
if (hbmMask)
DeleteObject(hbmMask);
return iIndex;
}
int _MapCursorIDToImageListIndex(int idCur)
{
#pragma data_seg(".text", "CODE")
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) },
};
#pragma data_seg()
Assert(idCur>=DCID_INVALID && idCur<DCID_MAX);
//
// idCur==DCID_INVALID means "Initialize the image list index array".
//
if (idCur==DCID_INVALID)
{
int i;
for (i=0 ; i<DCID_MAX ; i++) {
s_cursors._aindex[i] = -1;
}
return -1;
}
if (s_cursors._aindex[idCur] == -1)
{
HINSTANCE hinst = c_acurmap[idCur].fSystem ? NULL : HINST_THISDLL;
HCURSOR hcur = LoadCursor(hinst, c_acurmap[idCur].idRes);
s_cursors._aindex[idCur] = _AddCursorToImageList(hcur, c_acurmap[idCur].idMerge,
&s_cursors._aptHotSpot[idCur]);
}
return s_cursors._aindex[idCur];
}
void DAD_SetDragCursor(int idCursor)
{
static int s_idCursor = -1;
//
// Ignore if we are dragging over ourselves.
//
if (DAD_IsDraggingImage() && (s_idCursor!=idCursor) )
{
POINT ptHotSpot;
if (s_cursors._himl && (idCursor!=DCID_INVALID))
{
int iIndex = _MapCursorIDToImageListIndex(idCursor);
if (iIndex != -1) {
ImageList_GetDragImage(NULL, &ptHotSpot);
ptHotSpot.x -= s_cursors._aptHotSpot[idCursor].x;
ptHotSpot.y -= s_cursors._aptHotSpot[idCursor].y;
if (ptHotSpot.x < 0)
ptHotSpot.x = 0;
if (ptHotSpot.y < 0)
ptHotSpot.y = 0;
ImageList_SetDragCursorImage(s_cursors._himl, iIndex, ptHotSpot.x, ptHotSpot.y);
} else {
Assert(0);
}
}
s_idCursor = idCursor;
}
}
HCURSOR SetCursorHotspot(HCURSOR hcur, POINT *ptHot)
{
ICONINFO iconinfo;
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 _SetDropEffectCursor(int idCur)
{
if (s_cursors._himl && (idCur!=DCID_INVALID))
{
if (!s_cursors._ahcur[idCur])
{
int iIndex = _MapCursorIDToImageListIndex(idCur);
if (iIndex != -1)
{
HCURSOR hcurColor = ImageList_GetIcon(s_cursors._himl, iIndex, 0);
//
// On non C1_COLORCURSOR displays, CopyImage() will enforce
// monochrome. So on color cursor displays, we'll get colored
// dragdrop pix.
//
HCURSOR hcurScreen = CopyImage(hcurColor, IMAGE_CURSOR,
0, 0, LR_COPYRETURNORG | LR_DEFAULTSIZE);
HCURSOR hcurFinal = SetCursorHotspot(hcurScreen, &s_cursors._aptHotSpot[idCur]);
if (hcurScreen != hcurColor) {
DestroyCursor(hcurColor);
}
if (hcurFinal)
DestroyCursor(hcurScreen);
else
hcurFinal = hcurScreen;
s_cursors._ahcur[idCur] = hcurFinal;
}
}
if (s_cursors._ahcur[idCur]) {
//
// This code assumes that SetCursor is pretty quick if it is
// already set.
//
SetCursor(s_cursors._ahcur[idCur]);
}
}
}
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
//
// REVIEW: Our Defview's DragEnter code is lazy and does not pick the default.
// We'll fix it only if it causes some problem with OLE-apps.
//
#if 0
if (GetKeyState(VK_LBUTTON) < 0)
{
// if the left button is down we should always have
// one of the above
Assert(0);
}
#endif
idCursor = DCID_MOVE;
break;
}
return idCursor;
}
//-----------------------------------------------------------------
typedef struct {
IDropSource dsrc;
UINT cRef;
DWORD grfInitialKeyState;
} CDropSource;
extern IDropSourceVtbl c_CDropSourceVtbl; // forward decl
//
// Create an instance of CDropSource
//
HRESULT CDropSource_CreateInstance(IDropSource **ppdsrc)
{
CDropSource *this = (CDropSource *)LocalAlloc(LPTR, SIZEOF(CDropSource));
if (this)
{
this->dsrc.lpVtbl = &c_CDropSourceVtbl;
this->cRef = 1;
*ppdsrc = &this->dsrc;
return NOERROR;
}
else
{
*ppdsrc = NULL;
return E_OUTOFMEMORY;
}
}
HRESULT CDropSource_QueryInterface(IDropSource *pdsrc, REFIID riid, LPVOID *ppvObj)
{
CDropSource *this = IToClass(CDropSource, dsrc, pdsrc);
if (IsEqualIID(riid, &IID_IDropSource) || IsEqualIID(riid, &IID_IUnknown))
{
*ppvObj = this;
this->cRef++;
return NOERROR;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
ULONG CDropSource_AddRef(IDropSource *pdsrc)
{
CDropSource *this = IToClass(CDropSource, dsrc, pdsrc);
this->cRef++;
return this->cRef;
}
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;
}
}
}
ULONG CDropSource_Release(IDropSource *pdsrc)
{
CDropSource *this = IToClass(CDropSource, dsrc, pdsrc);
this->cRef--;
if (this->cRef > 0)
return this->cRef;
DAD_ShowCursor(TRUE); // just in case
LocalFree((HLOCAL)this);
return 0;
}
HRESULT CDropSource_QueryContinueDrag(IDropSource *pdsrc, BOOL fEscapePressed, DWORD grfKeyState)
{
CDropSource *this = IToClass(CDropSource, dsrc, pdsrc);
HRESULT hres = S_OK;
if (fEscapePressed)
{
hres = DRAGDROP_S_CANCEL;
}
else
{
// initialize ourself with the drag begin button
if (this->grfInitialKeyState == 0)
this->grfInitialKeyState = (grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
Assert(this->grfInitialKeyState);
if (!(grfKeyState & this->grfInitialKeyState))
{
//
// A button is released.
//
hres = DRAGDROP_S_DROP;
}
else if (this->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.
//
hres = DRAGDROP_S_CANCEL;
}
}
if (hres != S_OK)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
DAD_ShowCursor(TRUE);
DAD_SetDragCursor(DCID_NULL);
}
return hres;
}
HRESULT CDropSource_GiveFeedback(IDropSource *pdsrc, DWORD dwEffect)
{
CDropSource *this = IToClass(CDropSource, dsrc, pdsrc);
int idCursor = _MapEffectToId(dwEffect);
//
// Notes:
//
// 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())
{
// Feedback for multiple (rectangles) dragging
_SetDropEffectCursor(idCursor);
DAD_ShowCursor(TRUE);
return NOERROR;
}
else
{
DAD_ShowCursor(TRUE);
}
return DRAGDROP_S_USEDEFAULTCURSORS;
}
#pragma data_seg(".text", "CODE")
IDropSourceVtbl c_CDropSourceVtbl = {
CDropSource_QueryInterface,
CDropSource_AddRef,
CDropSource_Release,
CDropSource_QueryContinueDrag,
CDropSource_GiveFeedback
};
#pragma data_seg()