|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: drag.cpp
//
// Contents: Api's for doing drag'n'drop
//
// Classes: CPoint
// CDragOperation
// CDropTarget
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop rpc
// for Drag Drop protocol
// 20-Oct-94 alexgo added Win3.1 style drag drop
// for Chicago/NT shell
// 30-Sep-94 ricksa Drag/Drop optimization.
// 18-Jul-94 ricksa made cursors work in shared WOW
// 21-Apr-94 ricksa made drag/drop handle WM_CANCELMODE
// 04-Apr-94 ricksa rewrote DoDragDrop loop
// 11-Jan-94 alexgo added VDATEHEAP to every function
// 29-Dec-93 alexgo converted to RPC alogirithm for
// getting IDropTarget, etc.
// 06-Dec-93 alexgo commented, formatted
// 93/94 Johann Posch (JohannP) created Drag/Drop for Ole 16 bit
//
// Notes:
//
// RPC Drag Drop algorithm:
//
// During a drag drop operation, the user is moving the mouse around
// the screen, passing over many windows. For each window the mouse
// is over, we need to determine if the window is a drop target.
// If it is, then we remote the IDropTarget interface to the DropSource
// so that the correct visual feedbacks can be given.
//
// To accomplish this, RegisterDragDrop adds two properties to the
// drop target window: a public property, EndPoint ID (provided to
// us by compobj), and a private property (available only to the calling
// process), the IDropTarget pointer.
//
// During the DoDragDrop loop, we ask compobj to test each window for
// the EndpointID property. If it is there, compobj (via
// GetInterfaceFromWindowProp), then we will rpc to the drop target
// process, get the IDropTarget pointer and marshal it back to the
// drop source process. We also install a custom message filter to
// ensure that messages (particularly mouse move messages) are handled
// correctly.
//
// RevokeDragDrop simply removes the above mentioned properties from
// the window handle.
//
// Because in Win32, you can always switch windows and mouse capture
// depends on having the mouse button down, drag/drop processing
// is changed slightly. Whenever, the user does an operation that
// would switch windows, the clipboard window that we use for capture
// will get a WM_CANCELMODE. It will notify the drag operation and
// the drag operation will proceed as if the user aborted the operation.
//
//
// Win 3.1 DragDrop algorithm:
//
// Win3.1 apps can register a window as a drop target via DragAcceptFiles.
// This API sets the WS_EX_ACCEPTFILES bit in the window style.
//
// In Win3.1, these apps would get a WM_DROPFILES message when
// files where dropped on them. An hglobal with the filenames is
// sent in the wparam of WM_DROPFILES.
//
// In Chicago and NT3.5, CF_HDROP is a new clipboard format that is
// identical to the data sent in WM_DROPFILES. If we see this format
// available in a data object passed to DoDragDrop, then we enter
// into our Win31 compatibility mode (which affects finding a drop
// target).
//
// When finding a drop target for a given window, we check to
// see if a window in the hierarchy is registered as a Win31 drop
// target. If so, then we create a wrapper drop target. This wrapper
// drop target will forward calls to the real drop target (if available).
//
// With Win3.1 drag drop, we can do a COPY. If the OLE target indicates
// that no OLE drop can be performed (by returning DROPEFFECT_NONE),
// then we substitute in DROPEFFECT_COPY.
//
// On Drop, if the OLE target chooses not to accept the drop, then
// we will post the window a WM_DROPFILES message with the hglobal
// obtained from IDataObject::GetData(CF_HDROP).
//
//--------------------------------------------------------------------------
#include <le2int.h>
#pragma SEG(drag)
#include <getif.hxx>
#include <dragopt.h>
#include <resource.h>
#include "enumgen.h"
#include "clipbrd.h"
#include "drag.h"
NAME_SEG(Drag) ASSERTDATA
ATOM g_aEndPointAtom;
// DROPFILES is the structure of data contained in the CF_HDROP format.
// However, this is private to the shell, so it is not declared in any
// header files.
typedef struct _DROPFILES { DWORD pFiles; // offset of file list
POINTL pt; // drop point (client coords)
DWORD fNC; // is it on NonClient area
// and pt is in screen coords
DWORD fWide; // WIDE character switch
} DROPFILES, FAR * LPDROPFILES;
#define WM_NCMOUSEFIRST 0x00A0
#define WM_NCMOUSELAST 0x00A9
// From ido.cpp to create shared memory formats
HANDLE CreateSharedDragFormats(IDataObject *pIDataObject);
#define VK_ALT VK_MENU
static const struct { int keyCode; WPARAM keyFlag; } vKeyMap [] = { { VK_LBUTTON, MK_LBUTTON }, { VK_RBUTTON, MK_RBUTTON }, { VK_MBUTTON, MK_MBUTTON }, { VK_ALT , MK_ALT }, { VK_SHIFT , MK_SHIFT }, { VK_CONTROL, MK_CONTROL } };
// This is the default cursor object for 32 bit apps. Only one such object
// is needed for 32 bit apps. 16 bit apps need one per shared WOW application
// that is running.
CDragDefaultCursors *cddcDefault32 = NULL;
extern ATOM g_aDropTarget; extern ATOM g_aDropTargetMarshalHwnd;
//+-------------------------------------------------------------------------
//
// Member: DragDropProcessUninitialize
//
// Synopsis: Does any Unitialization necessary at OleUninitialize time.
// for the last Unitialize for the Process
//
// Returns: none
//
// Algorithm:
// History: dd-mmm-yy Author Comment
// 18-Jul-94 rogerg Created
//
// Note: We need a per thread default cursor object in WOW because
// of the clean up that WOW does. For 32 bit apps, we just use
// one for the entire process.
//
//--------------------------------------------------------------------------
void DragDropProcessUninitialize(void) {
if (NULL != cddcDefault32) { delete cddcDefault32; cddcDefault32 = NULL; }
}
//+-------------------------------------------------------------------------
//
// Member: CDragDefaultCursors::GetDefaultCursorObject, static
//
// Synopsis: Get appropriate pointer to default cursor object
//
// Returns: NULL - error occurred
// ~NULL - pointer to appropriate default cursor table
//
// Algorithm: If we are in a 32 bit app, just get a pointer to the
// single cursor table. In 16 bit, get the per thread cursor
// table. If there is none, then allocate and initialize it.
//
// History: dd-mmm-yy Author Comment
// 18-Jul-94 Ricksa Created
//
// Note: We need a per thread default cursor object in WOW because
// of the clean up that WOW does. For 32 bit apps, we just use
// one for the entire process.
//
//--------------------------------------------------------------------------
CDragDefaultCursors *CDragDefaultCursors::GetDefaultCursorObject(void) { if (!IsWOWThread()) { // If we aren't in WOW, we can use the single common default cursor
// object. We make sure that it is initialized before we use it.
if (NULL == cddcDefault32) { cddcDefault32 = new CDragDefaultCursors; if (cddcDefault32) { if (!cddcDefault32->Init()) { delete cddcDefault32; cddcDefault32 = NULL; }
} }
return cddcDefault32; }
COleTls tls;
// We are in WOW. Get the cursor object if it has already been allocated
CDragDefaultCursors *pccdc16 = (CDragDefaultCursors *) tls->pDragCursors;
if (pccdc16 == NULL) { // No cursor table so allocate it -- Please note that we take advantage
// of the fact that this object has only the default constructor by
// simply allocating it rather than "newing" it. The point is that
// we need to free the memory at thread release time and this happens
// in code that doesn't know about the the object.
pccdc16 = (CDragDefaultCursors *) PrivMemAlloc(sizeof(CDragDefaultCursors));
if (pccdc16 != NULL) { // Successfully allocated so initialize it
if (!pccdc16->Init()) { PrivMemFree(pccdc16); return NULL; }
tls->pDragCursors = pccdc16; } }
return pccdc16; }
//+-------------------------------------------------------------------------
//
// Function: CDragDefaultCursors::Init
//
// Synopsis: Initialize object by loading all the default cursors.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
// Note: We continue the Win16 practice of ignoring possible failure
// cases when loading the cursors although we do put in a
// debug verification that they all loaded.
//
//--------------------------------------------------------------------------
BOOL CDragDefaultCursors::Init(void) { // Make sure table is set to NULLs.
memset(&ahcursorDefaults[0][0], 0, sizeof(ahcursorDefaults));
// Load cursors for operation
if ( !(ahcursorDefaults[NO_SCROLL] [NO_DROP] = LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURNONE))) ) return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [MOVE_DROP] = LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURMOVE))) ) return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [COPY_DROP] = LoadCursor (g_hmodOLE2, MAKEINTRESOURCE(CURCOPY))) ) return FALSE;
if (!(ahcursorDefaults[NO_SCROLL] [LINK_DROP] = LoadCursor(g_hmodOLE2, MAKEINTRESOURCE(CURLINK))) ) return FALSE;
// Load cursors for operation
ahcursorDefaults[SCROLL] [NO_DROP] = ahcursorDefaults[NO_SCROLL] [NO_DROP];
ahcursorDefaults[SCROLL] [MOVE_DROP] = ahcursorDefaults[NO_SCROLL] [MOVE_DROP];
ahcursorDefaults[SCROLL] [COPY_DROP] = ahcursorDefaults[NO_SCROLL] [COPY_DROP];
ahcursorDefaults[SCROLL] [LINK_DROP] = ahcursorDefaults[NO_SCROLL] [LINK_DROP];
#if DBG == 1
// For debug, verify that cursors were loaded correctly
for (int i = 0; i < 2; i++) { for (int j = 0; j < 4; j++) { AssertSz((ahcursorDefaults[i] [j] != NULL), "Drag/Drop cursor initialization failed!"); } } #endif // DBG == 1
return TRUE; }
//+-------------------------------------------------------------------------
//
// Function: CDragDefaultCursors::SetCursor
//
// Synopsis: Set cursor to appropriate value
//
// Algorithm: We use the input effect to calculate the appropriate offset
// into the table for the cursor to use.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
// Note: We use the table approach so we to make consistent behavior
// between scroll and non-scroll cursors.
//
//--------------------------------------------------------------------------
void CDragDefaultCursors::SetCursor(DWORD dwEffect) { // Get Scroll index
int iScroll = (dwEffect & DROPEFFECT_SCROLL) ? SCROLL : NO_SCROLL;
int iCursorType = NO_DROP;
if (dwEffect & DROPEFFECT_LINK) { iCursorType = LINK_DROP; } else if (dwEffect & DROPEFFECT_COPY) { iCursorType = COPY_DROP; } else if (dwEffect & DROPEFFECT_MOVE) { iCursorType = MOVE_DROP; }
::SetCursor(ahcursorDefaults[iScroll] [iCursorType]); }
//
// Drag/Drop Operation Statics
//
LONG CDragOperation::s_wScrollInt = -1;
//+-------------------------------------------------------------------------
//
// Function: GetControlKeysState
//
// Synopsis: queries the current status of the control keys
//
// Arguments: [fAll] -- if true, the just query the keys, not mouse
// buttons too
//
// Returns: the MK flags for each key pressed
//
// Algorithm: Get key state either for all keys and mouse buttons in
// the vKeyMap table or simply for the key portion of the table
// and translate it to the WPARAM form as returned in mouse
// messages.
//
// History: dd-mmm-yy Author Comment
// 06-Dec-93 alexgo 32bit port
//
//--------------------------------------------------------------------------
WORD GetControlKeysState(BOOL fAll) { WORD grfKeyState = 0;
int i = (fAll) ? 0 : 3; for (; i < sizeof(vKeyMap) / sizeof(vKeyMap[0]); i++) { if (GetKeyState(vKeyMap[i].keyCode) < 0) // Key down
{ grfKeyState |= vKeyMap[i].keyFlag; } }
return grfKeyState; }
//+-------------------------------------------------------------------------
//
// Function: GetControlKeysStateOfParam
//
// Synopsis: gets the key/button state of wparam (used with mouse messages)
//
// Arguments: [wParam] -- the wParam to parse apart
//
// Returns: the key's set in wParam
//
// Algorithm: First determine if keys we are interested in are set
// in the wParam message. Then go check the state of the
// ALT key and record that in the key state. We then return
// that to the caller.
//
// History: dd-mmm-yy Author Comment
// 06-Dec-93 alexgo 32bit port
//
//--------------------------------------------------------------------------
WORD GetControlKeysStateOfParam(WPARAM wParam) { // Check all the buttons we are interested in at once.
WORD grfKeyState = (WORD) wParam & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON | MK_SHIFT | MK_CONTROL);
// get the alt key
if (GetKeyState(VK_ALT) < 0) // Key down
{ grfKeyState |= MK_ALT; }
return grfKeyState; }
//+-------------------------------------------------------------------------
//
// Function: IsWin31DropTarget
//
// Synopsis: determines whether the given hwnd is a valid drop target
// for Win31 style drag drop
//
// Effects:
//
// Arguments: [hwnd] -- the window to check
//
// Requires:
//
// Returns: TRUE/
// FALSE
//
// Signals:
//
// Modifies:
//
// Algorithm: checks the WS_EX_ACCEPTFILES style bit. If this bit is
// set and the window is not disabled, then it is a valid
// Win3.1 drop target.
//
// History: dd-mmm-yy Author Comment
// 25-Jan-95 alexgo added check for WS_DISABLED
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL IsWin31DropTarget( HWND hwnd ) { LONG exstyle;
exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if( (exstyle & WS_EX_ACCEPTFILES) ) { LONG style; style = GetWindowLong(hwnd, GWL_STYLE);
if( !(style & WS_DISABLED) ) { return TRUE; } }
return FALSE; }
//+-------------------------------------------------------------------------
//
// Function: UseWin31DragDrop
//
// Synopsis: tests the given data object to see if enough data is offered
// to perform Win3.1 style drag drop
//
// Effects:
//
// Arguments: [pDataObject] -- pointer to the data object
//
// Requires: pdataobj must not be NULL
//
// Returns: TRUE/FALSE
//
// Signals:
//
// Modifies:
//
// Algorithm: does an IDataObject::QueryGetData for CF_HDROP
//
// History: dd-mmm-yy Author Comment
// 30-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL UseWin31DragDrop(IDataObject *pDataObject) { FORMATETC formatetc;
INIT_FORETC(formatetc); formatetc.cfFormat = CF_HDROP; formatetc.tymed = TYMED_HGLOBAL;
if( pDataObject->QueryGetData(&formatetc) == NOERROR ) { return TRUE; } else { return FALSE; } }
//+-------------------------------------------------------------------------
//
// Function: IsNCDrop
//
// Synopsis: are we dropping into the non-client area of the window or
// on an iconic window?
//
// Effects: *DOES A SEND MESSAGE*!!!
//
// Arguments: [hwnd] -- the window to ask
// [pt] -- the point in screen coords
//
// Requires:
//
// Returns: TRUE/FALSE (TRUE if in non-client area)
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 25-Jan-95 alexgo borrowed from Win95 shell sources
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL IsNCDrop(HWND hwnd, POINT pt) { return (!IsIconic(hwnd) && HTCLIENT!=SendMessage(hwnd, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y))); }
//+-------------------------------------------------------------------------
//
// Member: GetDropTarget
//
// Synopsis: Gets the IDropTarget * from the closest window in the
// hierachy up from the given window (if available, of
// course ;-)
//
// Arguments: [hwndCur] -- the window to the cursor is currently over
// [hwndDropTarget] -- the window that contains a valid DropTarget
//
// Returns: Result of drag enter operation at Target
//
// Algorithm: Loop calling PrivDragDrop until we get a drop target or
// we run out of windows that are parent to the window that
// the mouse is currently on.
//
// If a window in the hierarchy has registered itself for
// Win3.1 drag drop, then we create a drop target wrapper
// (CDropTarget) to handle the Win3.1 protocol. Note
// that a window hierarchy may be both OLE *and* Win3.1
// targets.
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to use PrivDragDrop
// 20-Oct-94 alexgo added Win31 drop target support
// 30-Sep-94 ricksa Drag/Drop optimization.
// 21-Jul-94 alexgo removed GetDropTargetFromWindow
// optimization and put that functionality
// in GetInterfaceFromWindowProp (to
// help make clipboard faster).
// 06-Apr-94 Ricksa Modified to call GetDropTargetFromWindow
// to optimize local calls
// 11-Jan-94 alexgo changed name from GetTopStm to
// GetDropTarget, converted to the RPC-style
// drag drop, added a VDATEHEAP macro
// 06-Dec-93 alexgo commented
//
//--------------------------------------------------------------------------
HRESULT CDragOperation::GetDropTarget(HWND hwnd31,HWND hwndDropTarget) { IDropTarget *ptarget = NULL; DDInfo hDDInfo = NULL;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN GetDropTarget ( %x,%x)\n", this, hwnd31,hwndDropTarget));
_pDropTarget = NULL;
HRESULT hr = E_FAIL;
if (hwndDropTarget) { HWND hwndClipWindow;
Assert(GetProp(hwndDropTarget, (LPCWSTR)g_aDropTarget));
// If the DropTarget hasn't been marshaled, Marshal it now.
if (hwndClipWindow = (HWND) GetProp(hwndDropTarget,(LPCWSTR) g_aDropTargetMarshalHwnd)) { SSSendMessage(hwndClipWindow,WM_OLE_CLIPBRD_MARSHALDROPTARGET,0,(LPARAM) hwndDropTarget); } hr = PrivDragDrop(hwndDropTarget, DRAGOP_ENTER, _DOBuffer, _pDataObject, _grfKeyState, _cpt.GetPOINTL(), _pdwEffect, NULL, &hDDInfo);
if (hr != NOERROR) { hwndDropTarget = NULL; }
}
Assert( (NULL == hwnd31) || IsWin31DropTarget(hwnd31));
if( hwndDropTarget || hwnd31 ) { ptarget = new CDropTarget(hwnd31, hwndDropTarget, *_pdwEffect, this, hDDInfo);
if( ptarget == NULL ) { hr = E_OUTOFMEMORY; } else { hr = NOERROR; }
// if we have a Win31 drop target AND the OLE drop target returned
// DROPEFFECT_NONE, then we should return DROPEFFECT_COPY
if( hr == NOERROR && *_pdwEffect == DROPEFFECT_NONE && hwnd31 ) { *_pdwEffect = DROPEFFECT_COPY; }
_pDropTarget = ptarget;
} DDDebugOut((DEB_ITRACE, "%p OUT GetDropTarget ( %lx ) [ %p ]\n", this, hr, _pDropTarget));
return hr; }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::CDragOperation
//
// Synopsis: Initialize the object to start the operation
//
// Arguments: [pDataObject] - pointer to data object to drop
// [pDropSource] - pointer to source for drop operation
// [dwOKEffects] - effects allowed in drag operation
// [pdwEffect] - how operation affected source data
// [hr] - whether constructor succeeded
//
// Algorithm: Initialize data in object. Make sure that static data
// is initialized. Wait for first mouse message to begin.
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo added support for Win31 drag drop
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
CDragOperation::CDragOperation( LPDATAOBJECT pDataObject, LPDROPSOURCE pDropSource, DWORD dwOKEffects, DWORD FAR *pdwEffect, HRESULT& hr) : _pDataObject(pDataObject), _DOBuffer(NULL), _pDropSource(pDropSource), _pDropTarget(NULL), _pRealDropTarget(NULL), _hFormats(NULL), _dwOKEffects(dwOKEffects), _pdwEffect(pdwEffect), _fEscapePressed(FALSE), _curOld(GetCursor()), _hwndLast((HWND) -1), _grfKeyState(0), _hrDragResult(S_OK), _fReleasedCapture(FALSE), _pcddcDefault(NULL), _fUseWin31(FALSE) { VDATEHEAP();
// Set the default scroll interval
if (s_wScrollInt < 0) { InitScrollInt(); }
hr = GetMarshalledInterfaceBuffer(IID_IDataObject, pDataObject, &_DOBuffer);
if( hr != NOERROR ) { Assert(NULL == _DOBuffer); return; }
// Get appropriate default cursor table object
if ((_pcddcDefault = CDragDefaultCursors::GetDefaultCursorObject()) == NULL) { // Some error occurred while we were trying to initialize the
// so return an error. This should be highly unusual.
DDDebugOut((DEB_ERROR, "CDragDefaultCursors::GetDefaultCursorObject Failed!\n")); hr = E_FAIL; return; }
// We will use the clipboard window to capture the mouse but we
// must have a clipboard window so we make sure it is created
// if it is not already there.
hr = ClipSetCaptureForDrag(this);
if (FAILED(hr)) { return; }
_hFormats = CreateSharedDragFormats(pDataObject);
// it's OK for _hFormats to be NULL (indicates an empty or non-existant
// formatetc enumertor
// For following peek
MSG msg;
// Busy wait until a mouse or escape message is in the queue
while (!PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) { // Note: all keyboard messages except escape are tossed. This is
// fairly reasonable since the user has to be holding the left
// mouse button down at this point. They can't really be doing
// too much data input one handed.
if ((PeekMessage(&msg, 0, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) || PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYDOWN, PM_REMOVE)) && msg.wParam == VK_ESCAPE) { _fEscapePressed = TRUE; break; } }
// get mouse pos and key state
if (!_fEscapePressed) { _cpt.Set(msg.pt.x, msg.pt.y); _grfKeyState = GetControlKeysStateOfParam(msg.wParam); } else { // We ask the cursor for its position since we didn't get a
// position from the mouse.
GetCursorPos(_cpt.GetAddressOfPOINT()); _grfKeyState = GetControlKeysState(TRUE); }
// Check to see if we need to do Win3.1 style drag drop.
// If we do, then set a flag so we can construct a fake drop target as
// needed
if( UseWin31DragDrop(pDataObject) ) { _fUseWin31 = TRUE; }
}
//+-------------------------------------------------------------------------
//
// Function: ~CDragOperation
//
// Synopsis: Clean up object
//
// Algorithm: Release mouse capture. Restore ole cursor. Remove enum
// formats.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
CDragOperation::~CDragOperation(void) { VDATEHEAP();
AssertSz((_pDropTarget == NULL), "CDragOperation::~CDragOperation");
// Stop the mouse capture
ReleaseCapture();
// Restore the cursor if it got changed
SetCursor(_curOld);
// Close the handle to the shared memory
if (_hFormats) { CloseHandle(_hFormats); _hFormats = NULL; }
if( _DOBuffer ) { ReleaseMarshalledInterfaceBuffer(_DOBuffer); } }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::InitScrollInt
//
// Synopsis: Initialize the scroll interval
//
// Algorithm: Look in profile for defined interval. If none set, then
// default to zero.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
void CDragOperation::InitScrollInt(void) { DWORD dw; OLECHAR szBuffer[20];
s_wScrollInt = DD_DEFSCROLLDELAY;
dw = sizeof(szBuffer); if (ERROR_SUCCESS == RegQueryValueEx(HKEY_CURRENT_USER, OLESTR("Control Panel\\Mouse\\DragScrollDelay"), NULL, NULL, (LPBYTE)szBuffer, &dw)) { s_wScrollInt = wcstol(szBuffer, NULL, 0); } }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::UpdateTarget
//
// Synopsis: Update the target window based on mouse location
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: First, we query the source to see if it wants to continue
// with the drop. If so, we get current window for mouse. If
// it is different than the previous window check to see whether
// the targets are different. If they are different, then notify
// the current target that we are leaving and then notify the
// new target that we have arrived.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
// 10-Jul-94 AlexT Allow same IDropTarget on different HWNDs
//
//--------------------------------------------------------------------------
BOOL CDragOperation::UpdateTarget(void) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::UpdateTarget ( )\n", this));
// Assume this operation will continue the drag drop
BOOL fResult = TRUE; HRESULT hr; LPDROPTARGET lpCurDropTarget = NULL, lpOldDropTarget = NULL; HWND hWndTemp = NULL;
HWND hwndCur = WindowFromPoint(_cpt.GetPOINT());
// Query continue can return telling us one of four things:
// (1) Keep going (S_OK), (2) Drop operation should occur
// (DRAGDROP_S_DROP), (3) Drop operation is canceled
// (DRAGDROP_S_CANCEL) or (4) An unexpected error has occurred.
HRESULT hrQuery = _pDropSource->QueryContinueDrag(_fEscapePressed, _grfKeyState);
if (FAILED(hrQuery) || (hrQuery == ResultFromScode(DRAGDROP_S_CANCEL))) { // Unexpected error or the operation has been cancelled so give up.
_hrDragResult = hrQuery; fResult = FALSE; goto UpdateTarget_exit; }
// walk up the window list to find the actual pointer values for the current
// and old IDropTarget interfaces
if (hwndCur != _hwndLast) { hWndTemp = _hwndLast;
BOOL fChangedWin31 = FALSE;
HWND hWndOldDrop = NULL; HWND hWndNewDrop = NULL; HWND hWndWin31Drop = NULL;
LPDROPTARGET lpRealDropTarget = NULL; HANDLE hTemp = NULL;
DWORD dwCurrentProcessId = 0;
if (hWndTemp != (HWND)-1) GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
DWORD dwTempProcessID = dwCurrentProcessId;
while (hWndTemp && !lpRealDropTarget && hWndTemp != (HWND)-1 && dwTempProcessID == dwCurrentProcessId) { if (lpRealDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget)) { hWndOldDrop = hWndTemp; }
hWndTemp = GetParent(hWndTemp);
if (hWndTemp) { GetWindowThreadProcessId(hWndTemp, &dwTempProcessID); } }
hWndTemp = hwndCur;
if (hWndTemp != (HWND)-1) GetWindowThreadProcessId(hWndTemp, &dwCurrentProcessId);
dwTempProcessID = dwCurrentProcessId;
while (hWndTemp && dwTempProcessID == dwCurrentProcessId) { // If we haven't found the DropTarget yet, check this window.
if (!lpCurDropTarget) { if (lpCurDropTarget = (IDropTarget *)GetProp(hWndTemp, (LPCWSTR)g_aDropTarget)) { hWndNewDrop = hWndTemp; } }
// if the current window is a win31 drop target, update the win31 window
// handle in our DropTarget Class. NOTE: Beware, this code relies on the
// fact that we can party on the CDropTarget Class directly, knowing that
// the class is reconstructed below as a result of the GetDropTarget()
// when the real IDropTarget ptrs change.
if (!fChangedWin31 && IsWin31DropTarget(hWndTemp) && _fUseWin31) { fChangedWin31 = TRUE;
hWndWin31Drop = hWndTemp;
if (_pDropTarget) { ((CDropTarget*)_pDropTarget)->_hwnd31 = hWndTemp; } }
// if have a droptarget, and handle Win31 break.
if (lpCurDropTarget && (!_fUseWin31 || fChangedWin31)) { break; }
hWndTemp = GetParent(hWndTemp);
if (hWndTemp) { GetWindowThreadProcessId(hWndTemp, &dwTempProcessID); } }
// only update the drop target if the target has actually changed.
// HACK ALERT: We must explicitly check _hwndLast for -1 because Excel does not
// use OLE drag drop internally. When the cursor is moved outside the Overlapped
// Excel window, DoDragDrop is called. At this point _pRealDropTarget == NULL
// and lpCurDropTarget == NULL, and the no-smoking cursor does not appear.
// the _pRealDropTarget==NULL relies on the fact that lpCurDropTarget==NULL. This
// is true because the first case would short-circuit the rest of the condition
// otherwise
if ( (lpCurDropTarget != _pRealDropTarget) || (_hwndLast == (HWND)-1) || (hWndNewDrop != hWndOldDrop) || (_pRealDropTarget == NULL)) { DDDebugOut((DEB_ITRACE, "%p lpCurDropTarget != lpOldDropTarget\n", this)); // The window that we are working on has changed
_hwndLast = hwndCur; _pRealDropTarget = lpCurDropTarget;
//Allow the owner of the window to take foreground if it tries to.
if (dwCurrentProcessId) AllowSetForegroundWindow(dwCurrentProcessId);
// Assume that neither current or previous window are drop aware
BOOL fCurAndLastNotDropAware = TRUE;
if (_pDropTarget != NULL) { // There was a previous drop target
// Last window was drag/drop aware
fCurAndLastNotDropAware = FALSE;
// Tell the drop target we are leaving & release it
_pDropTarget->DragLeave(); _pDropTarget->Release(); _pDropTarget = NULL; }
// Set up effects for query of target
*_pdwEffect = _dwOKEffects;
hr = GetDropTarget(hWndWin31Drop,hWndNewDrop);
if (_pDropTarget != NULL) { // This window is drop awarre
fCurAndLastNotDropAware = FALSE;
// Errors from this call are ignored. We interpret them
// as the drop being disallowed. Since we don't really
// use this information here but in the DragOver call
// we make shortly, we just use this call to notify
// the application that we are beginning a drag operation.
if (!HandleFeedBack(hr)) { goto UpdateTarget_exit; } } else { // Tell the source that nothing happened
// only use DROPEFFECT_NONE if there is no new drop target.
hr = _pDropSource->GiveFeedback(*_pdwEffect = DROPEFFECT_NONE);
if (hr != NOERROR) { if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr)) { _pcddcDefault->SetCursorNone(); } else { // Unexpected error -- we will give up drag/drop.
DDDebugOut((DEB_ERROR, "CDragOperation::UpdateTarget 1st GiveFeedback FAILED %x\n", hr)); _hrDragResult = hr; fResult = FALSE; goto UpdateTarget_exit; } } }
if (fCurAndLastNotDropAware) { // Neither new or old window know about drag/drop so set
// cursor accordingly.
_pcddcDefault->SetCursorNone(); } } else { // The window that we are working on has changed
_hwndLast = hwndCur; } }
if (hrQuery != NOERROR) { // Query asked for a drop
fResult = FALSE; _hrDragResult = hrQuery; }
UpdateTarget_exit:
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::UpdateTarget ( %lx )\n", this, fResult));
return fResult; }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::HandleFeedBack
//
// Synopsis: Handle feedback and update of cursor
//
// Arguments: [hr] - hresult from previous operation on drop target.
//
// Returns: TRUE - continue drag operation
// FALSE - error
//
// Algorithm: If previous operation on the target failed, map this to a
// disallowed drop. Then ask the source for feedback. If it
// so requests, then update the cursor. If an unexpected
// error occurs, let caller know that loop should break.
//
// History: dd-mmm-yy Author Comment
// 19-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::HandleFeedBack(HRESULT hr) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleFeedBack ( %x )\n", this, hr));
BOOL fResult = TRUE;
if (hr != NOERROR) {
// target not responding for some reason; treat
// as if drop not possible, but don't preserve
// the reason why.
*_pdwEffect = DROPEFFECT_NONE; }
// If bogus return from drag over, then make sure results are appropriate.
// However, if we are in a WOW we need to do things a little differently
// to maintain complete compatability with Win 3.1. In 16-bit OLE 2.0,
// the *_pdwEffect value is not changed when displaying feedback (i.e.,
// the result of the & is not stored back into *_pdwEffect in Win 3.1...
// in straight NT we do). Not storing the results back into *_pdwEffect
// when InWow() is a hack specifically for Visio, and even more
// specifically, for dragging from Visio's palette of "items" to an
// Excel spreadsheet.
if (IsWOWThread()) { hr = _pDropSource->GiveFeedback( *_pdwEffect & (_dwOKEffects | DROPEFFECT_SCROLL)); } else { *_pdwEffect &= (_dwOKEffects | DROPEFFECT_SCROLL);
hr = _pDropSource->GiveFeedback(*_pdwEffect); }
if(hr != NOERROR) { // Either we want to change the cursor or some unexpected
// error has occurred.
if (DRAGDROP_S_USEDEFAULTCURSORS == GetScode(hr)) { _pcddcDefault->SetCursor(*_pdwEffect); } else { DDDebugOut((DEB_ERROR, "CDragOperation::HandleFeedBack Feedback FAILED %x\n", hr));
fResult = FALSE;
_hrDragResult = hr; } }
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleFeedBack ( %lx )\n", this, fResult));
return fResult; }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::DragOver
//
// Synopsis: Tell the target we are dragging over and process the result
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: Call the target's drag over if there is one and then
// get the sources feedback to update the cursor accordingly.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::DragOver(void) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::DragOver ( )\n", this));
// Default the result of the function to continue the loop for
// drag and drop.
BOOL fResult = TRUE;
// Local holder for errors.
HRESULT hr;
if (_pDropTarget != NULL) { // Keep effect in a local variable to save indirections
// in this routine.
*_pdwEffect = _dwOKEffects;
hr = _pDropTarget->DragOver(_grfKeyState, _cpt.GetPOINTL(), _pdwEffect);
// Get feedback from source & update cursor if necessary
fResult = HandleFeedBack(hr); }
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::DragOver ( %lx )\n", this, fResult));
return fResult; }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::HandleMessages
//
// Synopsis: Handle windows messages
//
// Returns: TRUE - continue drag operation
// FALSE - error or time to drop
//
// Algorithm: Check for any windows message. If the message is a mouse
// message then record the new position of the mouse. If it
// is a key message, the record whether escape has been pushed.
// If this is any other message, then dispatch it. Repeat this
// process until the scroll interval has been exceeded.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
BOOL CDragOperation::HandleMessages(void) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::HandleMessages ( )\n", this));
// Message buffer
MSG msg;
// Default result of function to continue
BOOL fResult = TRUE;
// Capture all messages (i.e. modal loop).
// Process all input messages, dispatch other messages
//
// Note:we must NOT loop here until a hardware message comes in
// scrolling will not work.
// * yielding is important since other apps need to run
// * look for mouse messages first since these are the most
// impotant
// Flag for whether we peeked a message
BOOL fMsg;
//
// Sundown - The SetTimer return value can be truncated.
// We are passing NULL as HWND and Win32 will returned
// a value not greater than 4GB...
// If a check is required we could consider a temporary
// UINT_PTR value and do an ASSERT on its value...
//
UINT uTimer = (UINT)SetTimer(NULL, 0, s_wScrollInt, NULL);
do { fMsg = FALSE;
// Note: the order of peek is important - further messages can show up
// in the last peek
// If we looked for mouse messages first, we might never pick up
// WM_QUIT or keyboard messages (because by the time we finished
// processing the mouse message another might be on the queue).
// So, we check for WM_QUIT and keyboard messages first.
if (PeekMessage(&msg, 0, WM_QUIT, WM_QUIT, PM_REMOVE | PM_NOYIELD) || PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE | PM_NOYIELD) || PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP, PM_REMOVE | PM_NOYIELD) || PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) || PeekMessage(&msg, 0, WM_NCMOUSEFIRST, WM_NCMOUSELAST, PM_REMOVE | PM_NOYIELD) || PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) { fMsg = TRUE;
if (msg.message == WM_QUIT) { // Quit message so we are done.
PostQuitMessage((int) msg.wParam);
// We are going exiting so the error doesn't matter too much
_hrDragResult = ResultFromScode(E_UNSPEC);
// Make sure we break out of the loop
fResult = FALSE; } else if ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) || (msg.message >= WM_SYSKEYDOWN && msg.message <= WM_SYSKEYUP)) { // Pull all keyboard messages from the queue - this keeps
// the keyboard state in sync with the user's actions
// We use a do/while so that we process the message we've
// already peeked.
do { // We only really pay attention to the escape key and dump
// any other key board messages.
if ((msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) && msg.wParam == VK_ESCAPE) { // Esc pressed: Cancel
_fEscapePressed = TRUE; } } while (PeekMessage(&msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE | PM_NOYIELD) || PeekMessage(&msg, 0, WM_SYSKEYDOWN, WM_SYSKEYUP, PM_REMOVE | PM_NOYIELD));
DWORD grfKeyState; // temp variable for key state
// get the key state don't change the button states!!
grfKeyState = GetControlKeysState(FALSE) | (_grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
// if the keyboard state is unchanged, then don't exit
// this loop (as that will result in DragOver being called).
// If we call DragOver for each keyboard message, then
// performance is unacceptably slow.
if ((grfKeyState == _grfKeyState) && !_fEscapePressed) { fMsg = FALSE; } else { DDDebugOut((DEB_ITRACE, "Updating key state\n")); _grfKeyState = grfKeyState; } } else if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) { // we may not have the focus (e.g. if we are the Chicago
// shell). Therefore, we won't ever get any WM_KEYDOWN
// messages. Double check the esc key status here
if( GetKeyState(VK_ESCAPE) < 0 ) { _fEscapePressed = TRUE; }
// We got a mouse move message - we skip all the mouse messages
// till we get to the last one. The point here is that
// because of the length of DragOver calls, we can get behind
// in processing messages which causes odd things to happen
// on the screen.
if (WM_MOUSEMOVE == msg.message) { MSG msg2;
// Keep processing mouse move messages till there
// aren't any more.
// if PeekMessage returns true update the original msg.
while(PeekMessage(&msg2, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { msg = msg2; }
}
// Record position of the mouse
_cpt.Set(msg.pt.x, msg.pt.y);
// set mouse button state here
_grfKeyState = GetControlKeysStateOfParam(msg.wParam);
} else if (msg.message >= WM_NCMOUSEFIRST && msg.message <= WM_NCMOUSELAST) { // Nothing we need to do for these NC mouse actions
NULL; } else if ( (msg.message == WM_TIMER) && (msg.wParam == uTimer) ) { // Our timer was triggered. We need to recheck the keyboard
// state just in case it has changed. This is important for
// the Chicago shell--if it doesn't have focus, then we won't
// get any WM_KEYDOWN message (just mouse moves).
_grfKeyState = GetControlKeysState(FALSE) | (_grfKeyState & (MK_LBUTTON | MK_RBUTTON | MK_MBUTTON));
if( GetKeyState(VK_ESCAPE) < 0 ) { _fEscapePressed = TRUE; }
// go ahead and fall out of the loop so we call DragOver
// (our timeout expired).
} else { // Dispatch all other messages
DispatchMessage(&msg); fMsg = FALSE; } } else { WaitMessage(); }
// we have to leave the loop periodicially since apps
// might rely on on it the DragOver is called freqeuntly.
} while (!fMsg);
// Get rid of the timer we created for the loop
KillTimer(NULL, uTimer);
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::HandleMessages ( %lx )\n", this, fResult));
return fResult; }
//+-------------------------------------------------------------------------
//
// Function: CDragOperation::CompleteDrop
//
// Synopsis: Complete the drag/drop operation
//
// Returns: Result of operation
//
// Algorithm: If there is a target and we have decided to drop, then
// drop. Otherwise, release the target and return whatever
// the other result of the operation was.
//
// History: dd-mmm-yy Author Comment
// 04-Apr-94 Ricksa Created
//
//--------------------------------------------------------------------------
HRESULT CDragOperation::CompleteDrop(void) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDragOperation::CompleteDrop ( )\n", this));
// Stop the mouse capture in case a dialog box is thrown up.
ReleaseCapture();
if (_pDropTarget != NULL) { // Caller is Drag/Drop aware
// and indicated it might accept drop
// The drop source replies DRAG_S_DROP if the user has
// released the left mouse button. However, we may be over
// a drop target which has refused a drop (via the feedback
// DROPEFFECT_NONE). Thus, both the drop source and drop
// target need to agree before we commit the drop.
if ((DRAGDROP_S_DROP == GetScode(_hrDragResult)) && (*_pdwEffect != DROPEFFECT_NONE)) { // We are going to try to drop
*_pdwEffect = _dwOKEffects;
HRESULT hr = _pDropTarget->Drop(_pDataObject, _grfKeyState, _cpt.GetPOINTL(), _pdwEffect);
if (FAILED(hr)) { // If drop actually failed in the last stage, let the
// caller know that this happened.
_hrDragResult = hr; }
} else { *_pdwEffect = DROPEFFECT_NONE; _pDropTarget->DragLeave();
}
_pDropTarget->Release(); _pDropTarget = NULL; } else { *_pdwEffect = DROPEFFECT_NONE; }
DDDebugOut((DEB_ITRACE, "%p OUT CDragOperation::CompleteDrop ( %lx )\n", this, _hrDragResult));
return _hrDragResult; }
//+-------------------------------------------------------------------------
//
// Function: RegisterDragDrop
//
// Synopsis: Registers a drop target
//
// Arguments: [hwnd] -- a handle to the drop target window
// [pDropTarget] -- the IDropTarget interface for the window
//
// Returns: HRESULT
//
// Algorithm: We ask compobj (via AssignEndpoinProperty) to put an
// endpoint ID publicly available on the window handle. Then
// we put the IDropTarget pointer on the window as a private
// property (see the notes at the beginning of this file).
//
// History: dd-mmm-yy Author Comment
// 06-Apr-94 ricksa Added tracing
// 16-Jan-94 alexgo pDropTarget is now AddRef'ed
// 11-Jan-94 alexgo added VDATEHEAP, converted to RPC-style
// drag drop.
// 06-Dec-93 alexgo commented
//
// Notes: By AddRef'ing the pDropTarget pointer, we are changing
// the semantics of the 16bit code (which did not do an
// AddRef).
//
//--------------------------------------------------------------------------
#pragma SEG(RegisterDragDrop)
STDAPI RegisterDragDrop(HWND hwnd, LPDROPTARGET pDropTarget) { HRESULT hresult = NOERROR; BOOL fDelayDrop = FALSE;
VDATEHEAP();
OLETRACEIN((API_RegisterDragDrop, PARAMFMT("hwnd= %h, pDropTarget= %p"), hwnd, pDropTarget));
DDDebugOut((DEB_ITRACE, "%p _IN RegisterDragDrop ( %lx %p )\n", NULL, hwnd, pDropTarget));
if (!IsValidInterface(pDropTarget)) { hresult = E_INVALIDARG; } else if (!IsWindow(hwnd)) { hresult = DRAGDROP_E_INVALIDHWND; } else { CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropTarget,(IUnknown **)&pDropTarget);
if (GetProp(hwnd, (LPCWSTR)g_aDropTarget)) { hresult = DRAGDROP_E_ALREADYREGISTERED; } else if (!SetProp(hwnd, (LPCWSTR)g_aDropTarget, (HANDLE)pDropTarget)) { hresult = E_OUTOFMEMORY; } else { DWORD dwAssignAptID;
Win4Assert(NOERROR == hresult);
// HACK: We need to add this atom every time RegisterDragDrop
// is called because 16-bit Word does not call RevokeDragDrop
// and user will automatically clean-up this atom if Word is the
// first app run, and then exited before another app calls
// RegisterDragDrop.
g_aEndPointAtom = GlobalAddAtom(ENDPOINT_PROP_NAME);
// See if Delayed Drop can be set up.
fDelayDrop = FALSE;
if (g_aDropTargetMarshalHwnd && IsApartmentInitialized()) { HWND hwndClipboard = GetPrivateClipboardWindow(CLIP_CREATEIFNOTTHERE);
if (hwndClipboard) { fDelayDrop = SetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd,hwndClipboard); } }
// if can't delay marshal then marshal immediately.
if (!fDelayDrop) { hresult = AssignEndpointProperty(hwnd); }
if (NOERROR == hresult) { pDropTarget->AddRef(); } else { // We don't free h. It's not a handle at all.
HANDLE h = RemoveProp(hwnd, (LPCWSTR)g_aDropTarget); } } }
DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n", NULL, hresult));
OLETRACEOUT((API_RegisterDragDrop, hresult));
return hresult; }
//+-------------------------------------------------------------------------
//
// Function: RevokeDragDrop
//
// Synopsis: Unregisters a window as a drop target
//
// Arguments: [hwnd] -- the window to unregister
//
// Returns: HRESULT
//
// Algorithm: Removes the two window properties set by
// RegisterDragDrop
//
// History: dd-mmm-yy Author Comment
// 06-Apr-94 ricksa added tracing
// 16-Jan-94 alexgo added a Release to the drag drop
// pointer to match the AddRef in
// RegisterDragDrop.
// 11-Jan-94 alexgo converted to RPC-style drag drop,
// added VDATEHEAP macro
// 06-Dec-93 alexgo commented
//
// Notes: the DropTarget->Release call changes the semantics of
// this function from the 16bit version (see Notes: for
// RegisterDragDrop).
//
//--------------------------------------------------------------------------
#pragma SEG(RevokeDragDrop)
STDAPI RevokeDragDrop(HWND hwnd) { HRESULT hr = NOERROR; LPDROPTARGET pDropTarget; BOOL fReleaseDropTarget = TRUE;
VDATEHEAP();
OLETRACEIN((API_RevokeDragDrop, PARAMFMT("hwnd= %h"), hwnd));
DDDebugOut((DEB_ITRACE, "%p _IN RevokeDragDrop ( %lx )\n", NULL, hwnd));
if (!IsWindow(hwnd)) { hr = DRAGDROP_E_INVALIDHWND; } else if ((pDropTarget = (LPDROPTARGET)RemoveProp(hwnd, (LPCWSTR)g_aDropTarget)) == NULL) { hr = DRAGDROP_E_NOTREGISTERED; } else { fReleaseDropTarget = TRUE;
if (GetProp(hwnd, (LPCWSTR) g_aEndPointAtom)) // see if there is an endpoint.
{ DWORD dwAssignAptID;
// Ask compobj to remove the endpoint ID it placed on the window.
if(SUCCEEDED(UnAssignEndpointProperty(hwnd,&dwAssignAptID))) { // Note: AptID == ThreadID in Apartment model.
if( (dwAssignAptID != GetCurrentThreadId()) && (IsApartmentInitialized()) ) { fReleaseDropTarget = FALSE; } }
Win4Assert(NULL == GetProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd)); } else { HWND hwndClipbrd;
hwndClipbrd = (HWND) RemoveProp(hwnd,(LPCWSTR) g_aDropTargetMarshalHwnd); Win4Assert(hwndClipbrd);
fReleaseDropTarget = (IsApartmentInitialized() && (hwndClipbrd != GetPrivateClipboardWindow(CLIP_QUERY )) ) ? FALSE : TRUE; }
// Release our reference to the object since we are no longer using it.
// NOTE: AddRef came from RegisterDragDrop
// Warning: Only call Release if we are in the same thread that Registered the DropTarget
// Or we are FreeThreading.
// This mirrors the atom added in RegisterDragDrop
GlobalDeleteAtom(g_aEndPointAtom);
if (fReleaseDropTarget) { pDropTarget->Release(); hr = NOERROR; // Always return NOERROR even if UnAssignEndPoint Failed
} else { LEDebugOut((DEB_WARN, "WARNING:Revoke Called on Different Thread than Register!!\n")); hr = RPC_E_WRONG_THREAD; } }
DDDebugOut((DEB_ITRACE, "%p OUT RegisterDragDrop ( %lx )\n", NULL, hr));
OLETRACEOUT((API_RevokeDragDrop, hr));
return hr; }
//+-------------------------------------------------------------------------
//
// Function: DoDragDrop
//
// Synopsis: The main drag'n'drop loop
//
// Effects:
//
// Arguments: [pDataObject] -- the object to drag
// [pDropSource] -- the drop source
// [dwOKEffects] -- effects flags (stuff to draw)
// [pdwEffect] -- what actually happened in
// the drag drop attempt
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm: See the notes at the beginning of the file
//
// History: dd-mmm-yy Author Comment
// 25-Nov-96 gopalk Fail the call if OleInitialize has not
// been called
// 05-Dec-94 JohannP added stack switching for WIN95
// 11-Jan-94 alexgo added VDATEHEAP macro, converted to
// the RPC-style drag drop.
// 31-Dec-93 erikgav chicago port
// 06-Dec-93 alexgo formatted
//
// Notes: Under Win95 SSAPI(DoDragDrop) gets expanded to SSDoDragDrop.
// This function is called by DoDragDrop (in stkswtch.cxx)
// which switches to the 16 bit stack first.
// IMPORTANT: this function has to be executed on the 16 bit
// since call back via USER might occur.
//--------------------------------------------------------------------------
#pragma SEG(DoDragDrop)
STDAPI SSAPI(DoDragDrop)(LPDATAOBJECT pDataObject, LPDROPSOURCE pDropSource, DWORD dwOKEffects, DWORD *pdwEffect) { OLETRACEIN((API_DoDragDrop, PARAMFMT("pDataObject=%p, pDropSource=%p, dwOKEffects=%x, pdwEffect=%p"), pDataObject, pDropSource, dwOKEffects, pdwEffect)); DDDebugOut((DEB_ITRACE, "%p _IN DoDragDrop (%p %p %lx %p )\n", NULL, pDataObject, pDropSource, dwOKEffects, pdwEffect));
HRESULT hr = NOERROR;
#ifndef _MAC
// Validation checks
VDATEHEAP(); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDataObject,(IUnknown **)&pDataObject); CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IDropSource,(IUnknown **)&pDropSource); if(!IsValidPtrOut(pdwEffect, sizeof(DWORD)) || !IsValidInterface(pDropSource) || !IsValidInterface(pDataObject)) hr = E_INVALIDARG;
// Check if the thread has called oleinitialize
if(!IsOleInitialized()) hr = CO_E_NOTINITIALIZED;
if(hr == NOERROR) { // Create the object that does all the work.
CDragOperation drgop(pDataObject, pDropSource, dwOKEffects, pdwEffect, hr);
// Did the constructor succeeded?
if(SUCCEEDED(hr)) { // Loop till worker object tells us to stop
for(;;) { // Update target based on new window position
if(!drgop.UpdateTarget()) { // Error so we are done
break; }
// Notify
if(!drgop.DragOver()) { break; }
// Handle any messages we get in the mean time
if(!drgop.HandleMessages()) { break; }
} // end for loop
hr = drgop.CompleteDrop(); } } #endif // !_MAC
DDDebugOut((DEB_ITRACE, "%p OUT DoDragDrop ( %lx )\n", NULL, hr)); OLETRACEOUT((API_DoDragDrop, hr));
return hr; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::CDropTarget
//
// Synopsis: constructor for the CDropTarget class
//
// Effects:
//
// Arguments: [hwnd31] -- the hwnd of the Win3.1 drop target
// may be NULL
// [hwndOLE] -- the hwnd of the OLE drop target
// [dwEffectLast] -- the last effect given the the current
// drop target that we are to emulate
// [pdo] -- a pointer to the main drag drop class
// [hDDInfo] -- handle to cached drag drag info
//
//
// Requires: hwnd31 *must* be a handle to a valid Win3.1 drop source
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm: initializes variables
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
// 08-Jan-95
//
// Notes: there are two ways of determining if a given hwnd is
// a valid Win3.1 drop target:
// 1. send a WM_QUERYDROPOBJECT message for a TRUE/FALSE
// reply
// 2. check the extended style bits for WS_EX_ACCEPTFILES
//
// if ptarget is non-NULL, then the specific window to which
// it belongs is *not* guaranteed to be the same window as
// hwndtarget. hwndtarget is the window that is registered as
// a Win3.1 target. All that is guaranteed is that the ole
// target and hwndtarget are in the same window hierarchy.
//
//--------------------------------------------------------------------------
CDropTarget::CDropTarget( HWND hwnd31, HWND hwndOLE, DWORD dwEffectLast, CDragOperation *pdo, DDInfo hDDInfo ) { _crefs = 1;
_hwndOLE = hwndOLE; _hwnd31 = hwnd31; _hDDInfo = hDDInfo;
_dwEffectLast = dwEffectLast;
_pdo = pdo; // pointer to the current drag operation class
#if DBG ==1
// now do some checking (see Notes above)
if( hwnd31 ) { LONG exstyle;
exstyle = GetWindowLong(hwnd31, GWL_EXSTYLE);
// strictly speaking, an app could process the WM_QUERYDROPOBJECT
// message itself (and thus, not set the extended style bits).
// However, this should be considered an application bug; the
// documentation states that apps should call DragAcceptFiles,
// which will set the WS_EX_ACCEPTFILES bit
Assert( (exstyle & WS_EX_ACCEPTFILES) ); }
#endif // DBG ==1
}
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::~CDropTarget
//
// Synopsis: frees the cached drag drop info handle
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: void
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 08-Jan-95 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
CDropTarget::~CDropTarget() { if( _hDDInfo ) { FreeDragDropInfo(_hDDInfo); } }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::QueryInterface
//
// Synopsis: returns available interfaces on this object
//
// Effects:
//
// Arguments: [riid] -- the requested interface
// [ppv] -- where to put the interface
//
// Requires:
//
// Returns: E_UNEXPECTED
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: CDropTarget is only used internally by OLE's drag drop code.
// It should never do a QI.
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::QueryInterface( REFIID riid, LPVOID * ppv ) { (void)riid; // unused;
(void)ppv; // unused;
AssertSz(0, "Unexpected QI to CDropTarget");
return E_UNEXPECTED; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::AddRef
//
// Synopsis: increments the reference count
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: ULONG, the new reference count
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDropTarget::AddRef( void ) { VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::AddRef ( )\n", this));
_crefs++;
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::AddRef ( %ld )\n", this, _crefs));
return _crefs; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::Release
//
// Synopsis: decrements the reference count
//
// Effects: may delete 'this' object
//
// Arguments: void
//
// Requires:
//
// Returns: ULONG, the new reference count
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm:
//
// History: dd-mmm-yy Author Comment
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CDropTarget::Release( void ) { ULONG crefs;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Release ( )\n", this));
crefs = --_crefs;
if( crefs == 0) { DDDebugOut((DEB_ITRACE, "DELETING CDropTarget %p\n", this)); delete this; }
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Release ( %ld )\n", this, crefs));
return crefs; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragEnter
//
// Synopsis: sets the window up for drag drop
//
// Effects:
//
// Arguments: [pDataObject] -- the data object to drop
// [grfKeyState] -- the current keyboard state
// [pt] -- the cursor point
// [pdwEffect] -- where to return the drag drop effect
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: should never be called. DragEnter is always called
// via GetDropTarget
//
// History: dd-mmm-yy Author Comment
// 08-Nov-93 alexgo eliminated
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragEnter( IDataObject * pDataObject, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect ) { AssertSz(0, "DragEnter unexpectedly called!");
return E_UNEXPECTED; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragOver
//
// Synopsis: called while the mouse is over a given window
//
// Effects:
//
// Arguments: [grfKeyState] -- the state of the keyboard
// [ptl] -- the position of the cursor
// [pdwEffect] -- the drag drop effect
//
// Requires:
//
// Returns: NOERROR
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: If an OLE target is available, then we forward the call.
// If the target says DROPEFFECT_NONE, then we go ahead
// and return DROPEFFECT_COPY if a Win31 target window is
// available.
//
// If there is no OLE target and we have a Win3.1 target,
// then we go ahead and return DROPEFFECT_COPY.
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragOver( DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { HRESULT hresult = NOERROR;
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragOver ( %lx , %lx " ", %lx )\n", this, grfKeyState, &ptl, *pdwEffect));
if( _hwndOLE ) { hresult = PrivDragDrop(_hwndOLE, DRAGOP_OVER, NULL, NULL, grfKeyState, ptl, pdwEffect, NULL, &_hDDInfo);
_dwEffectLast = *pdwEffect;
if( _hwnd31 ) { // we only want to stomp on the effect if the DragOver call
// succeeded. If the call failed, then just assume that a
// Win3.1 drop would fail as well.
if( hresult == NOERROR && *pdwEffect == DROPEFFECT_NONE ) { *pdwEffect = DROPEFFECT_COPY; } } } else if ( _hwnd31 ) { *pdwEffect = DROPEFFECT_COPY; }
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragOver ( %lx ) [ " "%lx ]\n", this, hresult, *pdwEffect));
return hresult; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::DragLeave
//
// Synopsis: called when the cursor leaves the current target window
//
// Effects:
//
// Arguments: void
//
// Requires:
//
// Returns: NOERROR
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: Forwards the DragLeave call to the OLE-drop target
// (if it exists).
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::DragLeave() { HRESULT hresult = NOERROR; static POINTL ptl = {0, 0};
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::DragLeave ( )\n", this));
if( _hwndOLE ) { hresult = PrivDragDrop(_hwndOLE, DRAGOP_LEAVE, NULL, NULL, NULL, ptl, NULL, NULL, &_hDDInfo); }
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::DragLeave ( %lx )\n", this, hresult));
return hresult; }
//+-------------------------------------------------------------------------
//
// Member: CDropTarget::Drop
//
// Synopsis: called if the user lets go of the mouse button while
// over a drop target
//
// Effects:
//
// Arguments: [pDataObject] -- the data object to use
// [grfKeyState] -- the keyboard state
// [ptl] -- the current mouse position
// [pdwEffect] -- where to return cursor effect feedback
//
// Requires:
//
// Returns: HRESULT
//
// Signals:
//
// Modifies:
//
// Derivation: IDropTarget
//
// Algorithm: If there is an OLE-target available, then we first forward
// the drop request to it. If the call fails
// (or DROPEFFECT_NONE is returned), then we try the Win31
// drop by posting a WM_DROPFILES message to the Win31 target
// window (ifit exists).
//
// History: dd-mmm-yy Author Comment
// 08-Nov-94 alexgo converted to PrivDragDrop protocol
// 20-Oct-94 alexgo author
//
// Notes:
//
//--------------------------------------------------------------------------
STDMETHODIMP CDropTarget::Drop( IDataObject *pDataObject, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect ) { STGMEDIUM medium; FORMATETC formatetc; HRESULT hresult = E_FAIL; IFBuffer DOBuffer = _pdo->GetDOBuffer();
VDATEHEAP();
DDDebugOut((DEB_ITRACE, "%p _IN CDropTarget::Drop ( %p , %lx , ", "%p , %lx )\n", this, pDataObject, grfKeyState, &ptl, *pdwEffect));
// we don't forward Drop calls to the target if the last effect
// is DROPEFFECT_NONE. It is important that we check for this because
// to DoDragDrop 'normally' would not call Drop if the last effect
// was DROPEFFECT_NONE. However, this target wrapper will stomp
// pdwEffect and return DROPEFFECT_COPY instead of DROPEFFECT_NONE.
if( _hwndOLE && _dwEffectLast != DROPEFFECT_NONE ) { hresult = PrivDragDrop(_hwndOLE, DRAGOP_DROP, DOBuffer, pDataObject, grfKeyState, ptl, pdwEffect, GetPrivateClipboardWindow(CLIP_QUERY), &_hDDInfo); } else if( _hwndOLE ) { // if the 'real' drop effect is NONE, then we need to call
// DragLeave here before going on to post the WM_DROPFILES
// message. Otherwise, the app that is both an OLE and Win31
// and has been returning DROPEFFECT_NONE will never get a
// Drop or DragLeave call (which is necessary to terminate
// the OLE2 drag protocol). Capone in particular is sensitive
// to this.
*pdwEffect = DROPEFFECT_NONE; hresult = DragLeave(); }
if( (hresult != NOERROR || *pdwEffect == DROPEFFECT_NONE) && (hresult != S_FALSE) && (_hwnd31) ) { medium.tymed = TYMED_NULL; INIT_FORETC(formatetc); formatetc.cfFormat = CF_HDROP; formatetc.tymed = TYMED_HGLOBAL;
hresult = pDataObject->GetData(&formatetc, &medium);
if( hresult == NOERROR ) { // we need to fixup the mouse point coordinates in the CF_HDROP
// data. The point point should be in client coordinates
// (whereas IDropTarget::Drop takes screen coordinates)
DROPFILES *pdf = (DROPFILES *)GlobalLock(medium.hGlobal); POINT pt;
pt.x = ptl.x; pt.y = ptl.y;
if( pdf ) {
// we also need to set the non-client (NC) flag of the
// dropfile data. This lets the app do different behaviour
// depending on whether the drop point is in the client or
// non-client area (Word6, for example, opens the file if on
// non-client area, otherwise makes a package object).
pdf->fNC = IsNCDrop(_hwnd31, pt);
if( ScreenToClient(_hwnd31, &pt) ) { pdf->pt.x = pt.x; pdf->pt.y = pt.y; } else { LEDebugOut((DEB_WARN, "WARNING: CF_HDROP pt coords" "not updated!!\n")); ; // don't do anything
}
GlobalUnlock(medium.hGlobal); } else { LEDebugOut((DEB_WARN, "WARNING: OUT OF MEMORY!\n")); ; // don't do anything
}
if( PostMessage(_hwnd31, WM_DROPFILES, (WPARAM)medium.hGlobal, 0) ) { *pdwEffect = DROPEFFECT_COPY; } else { // PostMessage failed, so free the data
ReleaseStgMedium(&medium); *pdwEffect = DROPEFFECT_NONE; } } }
DDDebugOut((DEB_ITRACE, "%p OUT CDropTarget::Drop ( %lx ) [ %lx ]\n", this, hresult, *pdwEffect));
return hresult; }
|