293 lines
9.3 KiB
C++
293 lines
9.3 KiB
C++
|
#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()
|
||
|
{
|
||
|
if (InterlockedDecrement(&m_cRef))
|
||
|
return m_cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|