#include "droptgt.h" #define TF_DRAGDROP TF_BAND #define MAX_DROPTARGETS 3 class CDropTargetWrap : public IDropTarget { public: // *** IUnknown *** virtual STDMETHODIMP_(ULONG) AddRef(void); virtual STDMETHODIMP_(ULONG) Release(void); virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); // *** IDropTarget methods *** virtual STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); virtual STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); virtual STDMETHODIMP DragLeave(void); virtual STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); CDropTargetWrap(IDropTarget** ppdtg, HWND hwnd); protected: ~CDropTargetWrap(); private: int _cRef; int _count; IDropTarget* _rgpdt[MAX_DROPTARGETS]; DWORD _rgdwEffect[MAX_DROPTARGETS]; HWND _hwnd; }; CDropTargetWrap::CDropTargetWrap(IDropTarget** ppdt, HWND hwnd) : _hwnd(hwnd) { _cRef = 1; for (int i = 0; i < MAX_DROPTARGETS; i++, ppdt++) { if (*ppdt) { _rgpdt[_count] = *ppdt; _rgpdt[_count]->AddRef(); _count++; } } } CDropTargetWrap::~CDropTargetWrap() { for (int i = 0 ; i < _count ; i++) { _rgpdt[i]->Release(); } } IDropTarget* DropTargetWrap_CreateInstance(IDropTarget* pdtPrimary, IDropTarget* pdtSecondary, HWND hwnd, IDropTarget* pdt3) { // no point in wrapping nothing... if (pdtPrimary || pdtSecondary || pdt3) { IDropTarget* pdt[MAX_DROPTARGETS] = { pdtPrimary, pdtSecondary, pdt3 }; CDropTargetWrap* pdtw = new CDropTargetWrap(pdt, hwnd); if (pdtw) { return SAFECAST(pdtw, IDropTarget*); } } return NULL; } HRESULT CDropTargetWrap::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDropTargetWrap, IDropTarget), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CDropTargetWrap::AddRef(void) { _cRef++; return _cRef; } ULONG CDropTargetWrap::Release(void) { _cRef--; if (_cRef > 0) return _cRef; delete this; return 0; } /*---------------------------------------------------------- Purpose: IDropTarget::DragEnter method The *pdwEffect that is returned is the first valid value of all the drop targets' returned effects. */ HRESULT CDropTargetWrap::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { DWORD dwEffectOut = DROPEFFECT_NONE; for (int i = 0 ; i < _count ; i++) { _rgdwEffect[i] = *pdwEffect; if (SUCCEEDED(_rgpdt[i]->DragEnter(pdtobj, grfKeyState, ptl, &_rgdwEffect[i]))) { if (dwEffectOut == DROPEFFECT_NONE) { dwEffectOut = _rgdwEffect[i]; } } else { _rgdwEffect[i] = DROPEFFECT_NONE; } } *pdwEffect = dwEffectOut; return(S_OK); } /*---------------------------------------------------------- Purpose: IDropTarget::DragOver method */ HRESULT CDropTargetWrap::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { DWORD dwEffectOut = DROPEFFECT_NONE; for (int i = 0 ; i < _count ; i++) { _rgdwEffect[i] = *pdwEffect; if (SUCCEEDED(_rgpdt[i]->DragOver(grfKeyState, ptl, &_rgdwEffect[i]))) { if (dwEffectOut == DROPEFFECT_NONE) dwEffectOut = _rgdwEffect[i]; } else { _rgdwEffect[i] = DROPEFFECT_NONE; } } *pdwEffect = dwEffectOut; return(S_OK); } /*---------------------------------------------------------- Purpose: IDropTarget::DragLeave method */ HRESULT CDropTargetWrap::DragLeave(void) { for (int i = 0 ; i < _count ; i++) { _rgpdt[i]->DragLeave(); } return(S_OK); } /*---------------------------------------------------------- Purpose: IDropTarget::Drop method */ HRESULT CDropTargetWrap::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { DWORD dwEffectOut = DROPEFFECT_NONE; int i; BOOL fDropTried = FALSE; for (i = 0 ; (DROPEFFECT_NONE == dwEffectOut) && i < _count ; i++) { if ((_rgdwEffect[i] && *pdwEffect) && !fDropTried) { dwEffectOut = *pdwEffect; _rgpdt[i]->Drop(pdtobj, grfKeyState, pt, &dwEffectOut); fDropTried = TRUE; } else { _rgpdt[i]->DragLeave(); } } *pdwEffect = dwEffectOut; return(S_OK); } //============================================================================= // CDelegateDropTarget // // This class implements IDropTarget given an IDelegateDropTargetCB interface. // It handles all hit testing, caching, and scrolling for you. // //============================================================================= #undef CDropTargetWrap CDelegateDropTarget::CDelegateDropTarget() { TraceMsg(TF_SHDLIFE, "ctor CDelegateDropTarget %x", this); } CDelegateDropTarget::~CDelegateDropTarget() { TraceMsg(TF_SHDLIFE, "dtor CDelegateDropTarget %x", this); ASSERT(!_pDataObj); ATOMICRELEASE(_pDataObj); ASSERT(!_pdtCur); ATOMICRELEASE(_pdtCur); } HRESULT CDelegateDropTarget::Init() { HRESULT hres = GetWindowsDDT(&_hwndLock, &_hwndScroll); // We lock _hwndLock and do scrolling against _hwndScroll. // These can be different hwnds, but certain restrictions apply: if (_hwndLock != _hwndScroll) { BOOL fValid = IsChild(_hwndLock, _hwndScroll); if (!fValid) { TraceMsg(TF_DRAGDROP, "ctor CDelegateDropTarget: invalid windows %x and %x!", _hwndLock, _hwndScroll); _hwndLock = _hwndScroll = NULL; } } return hres; } void CDelegateDropTarget::_ReleaseCurrentDropTarget() { if (_pdtCur) { _pdtCur->DragLeave(); ATOMICRELEASE(_pdtCur); } } /*---------------------------------------------------------- Purpose: IDropTarget::DragEnter method */ HRESULT CDelegateDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect) { // We can be re-entered due to ui on thread if (_pDataObj != NULL) { TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter called a second time!"); *pdwEffect = DROPEFFECT_NONE; return S_OK; } TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragEnter with *pdwEffect=%x", *pdwEffect); ASSERT(!_pDataObj); _pDataObj = pdtobj; _pDataObj->AddRef(); // cache state // // wait until first DragOver to get valid info // _fPrime = FALSE; _dwEffectOut = DROPEFFECT_NONE; // set up auto-scroll info // ASSERT(pdtobj); _DragEnter(_hwndLock, ptl, pdtobj); DAD_InitScrollData(&_asd); _ptLast.x = _ptLast.y = 0x7fffffff; // put bogus value to force redraw HitTestDDT(HTDDT_ENTER, NULL, NULL, NULL); return S_OK; } /*---------------------------------------------------------- Purpose: IDropTarget::DragOver method */ HRESULT CDelegateDropTarget::DragOver(DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect) { HRESULT hres = S_OK; DWORD_PTR itemNew; POINT pt; DWORD dwEffectScroll = 0; DWORD dwEffectOut = 0; BOOL fSameImage = FALSE; DWORD dwCustDropEffect = 0; if (_pDataObj == NULL) { ASSERT(0); // DragEnter should be called before. return E_FAIL; } // convert to window coords pt.x = ptl.x; pt.y = ptl.y; ScreenToClient(_hwndScroll, &pt); if (DAD_AutoScroll(_hwndScroll, &_asd, &pt)) dwEffectScroll = DROPEFFECT_SCROLL; // // If we are dragging over on a different item, get its IDropTarget // interface or adjust itemNew to -1. // if (SUCCEEDED(HitTestDDT(HTDDT_OVER, &pt, &itemNew, &dwCustDropEffect)) && (itemNew != _itemOver || !_fPrime)) { _fPrime = TRUE; _ReleaseCurrentDropTarget(); _itemOver = itemNew; GetObjectDDT(_itemOver, IID_IDropTarget, (LPVOID*)&_pdtCur); if (_pdtCur) { // There's an IDropTarget for this hit, use it dwEffectOut = *pdwEffect; hres = _pdtCur->DragEnter(_pDataObj, grfKeyState, ptl, &dwEffectOut); if (FAILED(hres)) dwEffectOut = DROPEFFECT_NONE; } else { // No IDropTarget, no effect dwEffectOut = DROPEFFECT_NONE; } } else { // // No change in the selection. We assume that *pdwEffect stays // the same during the same drag-loop as long as the key state doesn't change. // if ((_grfKeyState != grfKeyState) && _pdtCur) { dwEffectOut = *pdwEffect; hres = _pdtCur->DragOver(grfKeyState, ptl, &dwEffectOut); TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver DragOver()d id:%d dwEffect:%4x hres:%d", _itemOver, dwEffectOut, hres); } else { // Same item and same key state. Use the previous dwEffectOut. dwEffectOut = _dwEffectOut; fSameImage = TRUE; } } _grfKeyState = grfKeyState; // store these for the next Drop _dwEffectOut = dwEffectOut; // and DragOver // Is the Custdrop effect valid ? if (dwCustDropEffect != DROPEFFECT_NONE) { //Yes then set the effect to Custdrop effect along with scroll effect *pdwEffect = dwCustDropEffect | dwEffectScroll; } else { //No , set the effect to dwEffectOut along with scroll effect *pdwEffect = dwEffectOut | dwEffectScroll; } TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragOver (*pdwEffect=%x)", *pdwEffect); if (!(fSameImage && pt.x==_ptLast.x && pt.y==_ptLast.y)) { _DragMove(_hwndLock, ptl); _ptLast.x = ptl.x; _ptLast.y = ptl.y; } return hres; } /*---------------------------------------------------------- Purpose: IDropTarget::DragLeave method */ HRESULT CDelegateDropTarget::DragLeave() { HitTestDDT(HTDDT_LEAVE, NULL, NULL, NULL); _ReleaseCurrentDropTarget(); TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::DragLeave"); ATOMICRELEASE(_pDataObj); DAD_DragLeave(); return S_OK; } /*---------------------------------------------------------- Purpose: IDropTarget::Drop method */ HRESULT CDelegateDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect) { HRESULT hres = S_OK; BOOL bDropHandled = FALSE; TraceMsg(TF_DRAGDROP, "CDelegateDropTarget::Drop (*pdwEffect=%x)", *pdwEffect); // // According to AlexGo (OLE), this is by-design. We should make it sure // that we use pdtobj instead of pdtobj. // //ASSERT(pdtobj == _pDataObj); pdtobj->AddRef(); _pDataObj->Release(); _pDataObj = pdtobj; // // Note that we don't use the drop position intentionally, // so that it matches to the last destination feedback. // if (_pdtCur) { // use this local because if _pdtCur::Drop does a UnlockWindow // then hits an error and needs to put up a dialog, // we could get re-entered IDropTarget *pdtCur = _pdtCur; _pdtCur = NULL; // HACK ALERT!!!! // // If we don't call LVUtil_DragEnd here, we'll be able to leave // dragged icons visible when the menu is displayed. However, because // we are calling IDropTarget::Drop() which may create some modeless // dialog box or something, we can not ensure the locked state of // the list view -- LockWindowUpdate() can lock only one window at // a time. Therefore, we skip this call only if the _pdtCur // is a subclass of CIDLDropTarget, assuming its Drop calls // CDefView::DragEnd (or CIDLDropTarget_DragDropMenu) appropriately. // #if 0 // later if (!IsIDLDropTarget(pdtCur)) #endif { // // This will hide the dragged image. // DAD_DragLeave(); // // We need to reset the drag image list so that the user // can start another drag&drop while we are in this // Drop() member function call. // // NOTE: we don't have to worry about the DAD_DragLeave // (called from during the DragLeave call at the end of // this function) cancelling the potential above-mentioned // drag&drop loop. If such a beast is going on, it should // complete before pdtCur->Drop returns. // DAD_SetDragImage(NULL, NULL); } if (S_FALSE != OnDropDDT(pdtCur, _pDataObj, &grfKeyState, pt, pdwEffect)) pdtCur->Drop(_pDataObj, grfKeyState, pt, pdwEffect); else pdtCur->DragLeave(); // should be okay even if OnDrop did this already pdtCur->Release(); } else { // // We come here if Drop is called without DragMove (with DragEnter). // *pdwEffect = DROPEFFECT_NONE; } // // Clean up everything (OLE won't call DragLeave after Drop). // DragLeave(); return hres; } // ****************************************************************** // dummy drop target to only call DAD_DragEnterEx() on DragEnter(); // ****************************************************************** HRESULT CDropDummy::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CDropDummy, IDropTarget), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CDropDummy::AddRef(void) { _cRef++; return _cRef; } ULONG CDropDummy::Release(void) { _cRef--; if (_cRef > 0) return _cRef; delete this; return 0; } /*---------------------------------------------------------- Purpose: IDropTarget::DragEnter method simply call DAD_DragEnterEx2() to get custom drag cursor drawing. */ HRESULT CDropDummy::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { ASSERT(pdtobj); _DragEnter(_hwndLock, ptl, pdtobj); *pdwEffect = DROPEFFECT_NONE; return(S_OK); } HRESULT CDropDummy::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { _DragMove(_hwndLock, ptl); *pdwEffect = DROPEFFECT_NONE; return S_OK; }