#include "shellprv.h" #pragma hdrstop #include "datautil.h" #include "idlcomm.h" #include "idldrop.h" STDAPI_(BOOL) DoesDropTargetSupportDAD(IDropTarget *pdtgt) { IDropTargetWithDADSupport* pdt; if (pdtgt && SUCCEEDED(pdtgt->QueryInterface(IID_IDropTargetWithDADSupport, (void**)&pdt))) { pdt->Release(); return TRUE; } return FALSE; } CIDLDropTarget::CIDLDropTarget(HWND hwnd) : m_cRef(1), m_hwnd(hwnd) { } CIDLDropTarget::~CIDLDropTarget() { // if we hit this a lot maybe we should just release it AssertMsg(m_pdtobj == NULL, TEXT("didn't get matching DragLeave.")); if (m_pidl) ILFree(m_pidl); } HRESULT CIDLDropTarget::_Init(LPCITEMIDLIST pidl) { ASSERT(m_pidl == NULL); return pidl ? SHILClone(pidl, &m_pidl) : S_OK; } HWND CIDLDropTarget::_GetWindow() { return m_hwnd; } STDMETHODIMP CIDLDropTarget::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CIDLDropTarget, IUnknown), QITABENT(CIDLDropTarget, IDropTarget), QITABENT(CIDLDropTarget, IDropTargetWithDADSupport), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CIDLDropTarget::AddRef() { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CIDLDropTarget::Release() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } STDAPI GetClipFormatFlags(IDataObject *pdtobj, DWORD *pdwData, DWORD *pdwEffectPreferred); STDMETHODIMP CIDLDropTarget::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { ASSERT(m_pdtobj == NULL); // DragDrop protocol requires DragLeave, this should be empty // init our registerd data formats IDLData_InitializeClipboardFormats(); m_grfKeyStateLast = grfKeyState; m_dwEffectLastReturned = *pdwEffect; IUnknown_Set((IUnknown **)&m_pdtobj, (IUnknown *)pDataObj); GetClipFormatFlags(m_pdtobj, &m_dwData, &m_dwEffectPreferred); return S_OK; } // subclasses can prevetn us from assigning in the dwEffect by not passing in pdwEffect STDMETHODIMP CIDLDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { m_grfKeyStateLast = grfKeyState; if (pdwEffect) *pdwEffect = m_dwEffectLastReturned; return S_OK; } STDMETHODIMP CIDLDropTarget::DragLeave() { IUnknown_Set((IUnknown **)&m_pdtobj, NULL); return S_OK; } STDMETHODIMP CIDLDropTarget::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { return E_NOTIMPL; } struct { UINT uID; DWORD dwEffect; } const c_IDEffects[] = { DDIDM_COPY, DROPEFFECT_COPY, DDIDM_MOVE, DROPEFFECT_MOVE, DDIDM_CONTENTS_DESKCOMP, DROPEFFECT_LINK, DDIDM_LINK, DROPEFFECT_LINK, DDIDM_SCRAP_COPY, DROPEFFECT_COPY, DDIDM_SCRAP_MOVE, DROPEFFECT_MOVE, DDIDM_DOCLINK, DROPEFFECT_LINK, DDIDM_CONTENTS_COPY, DROPEFFECT_COPY, DDIDM_CONTENTS_MOVE, DROPEFFECT_MOVE, DDIDM_CONTENTS_LINK, DROPEFFECT_LINK, DDIDM_CONTENTS_DESKIMG, DROPEFFECT_LINK, DDIDM_SYNCCOPYTYPE, DROPEFFECT_COPY, // (order is important) DDIDM_SYNCCOPY, DROPEFFECT_COPY, DDIDM_OBJECT_COPY, DROPEFFECT_COPY, DDIDM_OBJECT_MOVE, DROPEFFECT_MOVE, }; // // Pops up the "Copy, Link, Move" context menu, so that the user can // choose one of them. // // in: // pdwEffect drop effects allowed // dwDefaultEffect default drop effect // hkeyBase/hkeyProgID extension hkeys // hmenuReplace replaces POPUP_NONDEFAULTDD. Can only contain: // DDIDM_MOVE, DDIDM_COPY, DDIDM_LINK menu ids // pt in screen // Returns: // S_OK -- Menu item is processed by one of extensions or canceled // S_FALSE -- Menu item is selected // HRESULT CIDLDropTarget::DragDropMenu(DWORD dwDefaultEffect, IDataObject *pdtobj, POINTL pt, DWORD *pdwEffect, HKEY hkeyProgID, HKEY hkeyBase, UINT idMenu, DWORD grfKeyState) { DRAGDROPMENUPARAM ddm = { dwDefaultEffect, pdtobj, { pt.x, pt.y}, pdwEffect, hkeyProgID, hkeyBase, idMenu, 0, grfKeyState }; return DragDropMenuEx(&ddm); } HRESULT CIDLDropTarget::DragDropMenuEx(DRAGDROPMENUPARAM *pddm) { HRESULT hr = E_OUTOFMEMORY; // assume error DWORD dwEffectOut = 0; // assume no-ope. HMENU hmenu = SHLoadPopupMenu(HINST_THISDLL, pddm->idMenu); if (hmenu) { UINT idCmd; UINT idCmdFirst = DDIDM_EXTFIRST; HDXA hdxa = HDXA_Create(); HDCA hdca = DCA_Create(); if (hdxa && hdca) { // PERF (toddb): Even though pddm->hkeyBase does not have the same value as // pddm->hkeyProgID they can both be the same registry key (HKCR\FOLDER, for example). // As a result we sometimes enumerate this key twice looking for the same data. As // this is sometimes a slow operation we should avoid this. The comparision // done below was never valid on NT and might not be valid on win9x. // // Add extended menu for "Base" class. // if (pddm->hkeyBase && pddm->hkeyBase != pddm->hkeyProgID) DCA_AddItemsFromKey(hdca, pddm->hkeyBase, STRREG_SHEX_DDHANDLER); // // Enumerate the DD handlers and let them append menu items. // if (pddm->hkeyProgID) DCA_AddItemsFromKey(hdca, pddm->hkeyProgID, STRREG_SHEX_DDHANDLER); idCmdFirst = HDXA_AppendMenuItems(hdxa, pddm->pdtobj, 1, &pddm->hkeyProgID, m_pidl, hmenu, 0, DDIDM_EXTFIRST, DDIDM_EXTLAST, 0, hdca); } // eliminate menu options that are not allowed by dwEffect for (int nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem) { if (GetMenuState(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND)!=(UINT)-1) { if (!(c_IDEffects[nItem].dwEffect & *(pddm->pdwEffect))) { RemoveMenu(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND); } else if (c_IDEffects[nItem].dwEffect == pddm->dwDefEffect) { SetMenuDefaultItem(hmenu, c_IDEffects[nItem].uID, MF_BYCOMMAND); } } } // // If this dragging is caused by the left button, simply choose // the default one, otherwise, pop up the context menu. If there // is no key state info and the original effect is the same as the // current effect, choose the default one, otherwise pop up the // context menu. // if ((m_grfKeyStateLast & MK_LBUTTON) || (!m_grfKeyStateLast && (*(pddm->pdwEffect) == pddm->dwDefEffect)) ) { idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0); // This one MUST be called here. Please read its comment block. DAD_DragLeave(); if (m_hwnd) SetForegroundWindow(m_hwnd); } else { // Note that SHTrackPopupMenu calls DAD_DragLeave(). idCmd = SHTrackPopupMenu(hmenu, TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN, pddm->pt.x, pddm->pt.y, 0, m_hwnd, NULL); } // We also need to call this here to release the dragged image. DAD_SetDragImage(NULL, NULL); // Check if the user selected one of add-in menu items. if (idCmd == 0) { hr = S_OK; // Canceled by the user, return S_OK } else if (InRange(idCmd, DDIDM_EXTFIRST, DDIDM_EXTLAST)) { // Yes. Let the context menu handler process it. CMINVOKECOMMANDINFOEX ici = { SIZEOF(CMINVOKECOMMANDINFOEX), 0L, m_hwnd, (LPSTR)MAKEINTRESOURCE(idCmd - DDIDM_EXTFIRST), NULL, NULL, SW_NORMAL, }; // record if shift or control was being held down SetICIKeyModifiers(&ici.fMask); // We may not want to ignore the error code. (Can happen when you use the context menu // to create new folders, but I don't know if that can happen here.). HDXA_LetHandlerProcessCommandEx(hdxa, &ici, NULL); hr = S_OK; } else { for (nItem = 0; nItem < ARRAYSIZE(c_IDEffects); ++nItem) { if (idCmd == c_IDEffects[nItem].uID) { dwEffectOut = c_IDEffects[nItem].dwEffect; break; } } // if hmenuReplace had menu commands other than DDIDM_COPY, // DDIDM_MOVE, DDIDM_LINK, and that item was selected, // this assert will catch it. (dwEffectOut is 0 in this case) ASSERT(nItem < ARRAYSIZE(c_IDEffects)); hr = S_FALSE; } if (hdca) DCA_Destroy(hdca); if (hdxa) HDXA_Destroy(hdxa); DestroyMenu(hmenu); pddm->idCmd = idCmd; } *(pddm->pdwEffect) = dwEffectOut; return hr; }