826 lines
22 KiB
C++
826 lines
22 KiB
C++
#pragma once
|
|
#include <shlobj.h>
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
|
|
class CEnumFormatEtc : public IEnumFORMATETC
|
|
{
|
|
public:
|
|
CEnumFormatEtc(const vector<FORMATETC> & vFormatEtc)
|
|
{
|
|
m_nRefCount = 0;
|
|
m_nIndex = 0;
|
|
m_vFormatEtc = vFormatEtc;
|
|
}
|
|
|
|
protected:
|
|
vector<FORMATETC> m_vFormatEtc;
|
|
int m_nRefCount;
|
|
int m_nIndex;
|
|
|
|
public:
|
|
// IUnknown members
|
|
STDMETHOD(QueryInterface)
|
|
(REFIID refiid, void FAR * FAR * ppvObject)
|
|
{
|
|
*ppvObject = (refiid == IID_IUnknown || refiid == IID_IEnumFORMATETC) ? this : nullptr;
|
|
|
|
if (*ppvObject != nullptr)
|
|
((LPUNKNOWN)*ppvObject)->AddRef();
|
|
|
|
return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)
|
|
(void)
|
|
{
|
|
return ++m_nRefCount;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)
|
|
(void)
|
|
{
|
|
int nRefCount = --m_nRefCount;
|
|
if (nRefCount == 0)
|
|
delete this;
|
|
return nRefCount;
|
|
}
|
|
|
|
// IEnumFORMATETC members
|
|
STDMETHOD(Next)
|
|
(ULONG celt, LPFORMATETC lpFormatEtc, ULONG FAR * pceltFetched)
|
|
{
|
|
if (pceltFetched != nullptr)
|
|
*pceltFetched = 0;
|
|
|
|
ULONG cReturn = celt;
|
|
|
|
if (celt <= 0 || lpFormatEtc == nullptr || m_nIndex >= (int)m_vFormatEtc.size())
|
|
return S_FALSE;
|
|
|
|
if (pceltFetched == nullptr && celt != 1) // pceltFetched can be nullptr only for 1 item request
|
|
return S_FALSE;
|
|
|
|
while (m_nIndex < (int)m_vFormatEtc.size() && cReturn > 0)
|
|
{
|
|
*lpFormatEtc++ = m_vFormatEtc[m_nIndex++];
|
|
cReturn--;
|
|
}
|
|
|
|
if (pceltFetched != nullptr)
|
|
*pceltFetched = celt - cReturn;
|
|
|
|
return cReturn == 0 ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHOD(Skip)
|
|
(ULONG celt)
|
|
{
|
|
if ((m_nIndex + (int)celt) >= (int)m_vFormatEtc.size())
|
|
return S_FALSE;
|
|
m_nIndex += celt;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Reset)
|
|
(void)
|
|
{
|
|
m_nIndex = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Clone)
|
|
(IEnumFORMATETC FAR * FAR * ppCloneEnumFormatEtc)
|
|
{
|
|
if (ppCloneEnumFormatEtc == nullptr)
|
|
return E_POINTER;
|
|
|
|
*ppCloneEnumFormatEtc = new CEnumFormatEtc(m_vFormatEtc);
|
|
((CEnumFormatEtc *)*ppCloneEnumFormatEtc)->AddRef();
|
|
((CEnumFormatEtc *)*ppCloneEnumFormatEtc)->m_nIndex = m_nIndex;
|
|
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
class CDropSource : public IDropSource
|
|
{
|
|
public:
|
|
CDropSource()
|
|
{
|
|
m_nRefCount = 0;
|
|
}
|
|
|
|
protected:
|
|
int m_nRefCount;
|
|
|
|
public:
|
|
// IUnknown members
|
|
STDMETHOD(QueryInterface)
|
|
(REFIID refiid, void FAR * FAR * ppvObject)
|
|
{
|
|
*ppvObject = (refiid == IID_IUnknown || refiid == IID_IDropSource) ? this : nullptr;
|
|
|
|
if (*ppvObject != nullptr)
|
|
((LPUNKNOWN)*ppvObject)->AddRef();
|
|
|
|
return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)
|
|
(void)
|
|
{
|
|
return ++m_nRefCount;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)
|
|
(void)
|
|
{
|
|
int nRefCount = --m_nRefCount;
|
|
if (nRefCount == 0)
|
|
delete this;
|
|
return nRefCount;
|
|
}
|
|
|
|
// IDropSource members
|
|
STDMETHOD(QueryContinueDrag)
|
|
(BOOL bEscapePressed, DWORD dwKeyState)
|
|
{
|
|
if (bEscapePressed)
|
|
return DRAGDROP_S_CANCEL;
|
|
|
|
if (!(dwKeyState & (MK_LBUTTON | MK_RBUTTON)))
|
|
return DRAGDROP_S_DROP;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(GiveFeedback)
|
|
(DWORD /*dwEffect*/)
|
|
{
|
|
return DRAGDROP_S_USEDEFAULTCURSORS;
|
|
}
|
|
};
|
|
|
|
class CDataObject : public IDataObject
|
|
{
|
|
public:
|
|
CDataObject(CDropSource * pDropSource)
|
|
{
|
|
m_nRefCount = 0;
|
|
m_pDropSource = pDropSource;
|
|
m_bSwappedButtons = GetSystemMetrics(SM_SWAPBUTTON);
|
|
}
|
|
|
|
virtual ~CDataObject()
|
|
{
|
|
for (vector<STGMEDIUM>::iterator posStgMedium = m_vStgMedium.begin(); posStgMedium != m_vStgMedium.end(); posStgMedium++)
|
|
ReleaseStgMedium(&(*posStgMedium));
|
|
}
|
|
|
|
protected:
|
|
CDropSource * m_pDropSource;
|
|
int m_nRefCount;
|
|
BOOL m_bSwappedButtons;
|
|
|
|
vector<FORMATETC> m_vFormatEtc;
|
|
vector<STGMEDIUM> m_vStgMedium;
|
|
|
|
public:
|
|
// IUnknown members
|
|
STDMETHOD(QueryInterface)
|
|
(REFIID refiid, void FAR * FAR * ppvObject)
|
|
{
|
|
*ppvObject = (refiid == IID_IUnknown || refiid == IID_IDataObject) ? this : nullptr;
|
|
|
|
if (*ppvObject != nullptr)
|
|
((LPUNKNOWN)*ppvObject)->AddRef();
|
|
|
|
return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)
|
|
(void)
|
|
{
|
|
return ++m_nRefCount;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)
|
|
(void)
|
|
{
|
|
int nRefCount = --m_nRefCount;
|
|
if (nRefCount == 0)
|
|
delete this;
|
|
return nRefCount;
|
|
}
|
|
|
|
// IDataObject members
|
|
STDMETHOD(GetData)
|
|
(FORMATETC __RPC_FAR * pformatetcIn, STGMEDIUM __RPC_FAR * pmedium)
|
|
{
|
|
if (pformatetcIn == nullptr || pmedium == nullptr)
|
|
return E_INVALIDARG;
|
|
|
|
ZeroMemory(pmedium, sizeof(STGMEDIUM));
|
|
|
|
for (int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++)
|
|
{
|
|
if (pformatetcIn->tymed & m_vFormatEtc[nFormatEtc].tymed && pformatetcIn->dwAspect == m_vFormatEtc[nFormatEtc].dwAspect && pformatetcIn->cfFormat == m_vFormatEtc[nFormatEtc].cfFormat)
|
|
{
|
|
if (m_vStgMedium[nFormatEtc].tymed == TYMED_NULL)
|
|
return OnRenderData(m_vFormatEtc[nFormatEtc], pmedium, (GetAsyncKeyState(m_bSwappedButtons ? VK_RBUTTON : VK_LBUTTON) >= 0)) ? S_OK : DV_E_FORMATETC;
|
|
|
|
CopyMedium(pmedium, m_vStgMedium[nFormatEtc], m_vFormatEtc[nFormatEtc]);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return DV_E_FORMATETC;
|
|
}
|
|
|
|
STDMETHOD(GetDataHere)
|
|
(FORMATETC __RPC_FAR * /*pformatetc*/, STGMEDIUM __RPC_FAR * /*pmedium*/)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(QueryGetData)
|
|
(FORMATETC __RPC_FAR * pformatetc)
|
|
{
|
|
if (pformatetc == nullptr)
|
|
return E_INVALIDARG;
|
|
|
|
if (!(pformatetc->dwAspect & DVASPECT_CONTENT))
|
|
return DV_E_DVASPECT;
|
|
|
|
HRESULT hResult = DV_E_TYMED;
|
|
for (int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++)
|
|
{
|
|
if (!(pformatetc->tymed & m_vFormatEtc[nFormatEtc].tymed))
|
|
{
|
|
hResult = DV_E_TYMED;
|
|
continue;
|
|
}
|
|
|
|
if (pformatetc->cfFormat == m_vFormatEtc[nFormatEtc].cfFormat)
|
|
return S_OK;
|
|
|
|
hResult = DV_E_CLIPFORMAT;
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
STDMETHOD(GetCanonicalFormatEtc)
|
|
(FORMATETC __RPC_FAR * /*pformatectIn*/, FORMATETC __RPC_FAR * pformatetcOut)
|
|
{
|
|
return pformatetcOut == nullptr ? E_INVALIDARG : DATA_S_SAMEFORMATETC;
|
|
}
|
|
|
|
STDMETHOD(SetData)
|
|
(FORMATETC __RPC_FAR * pformatetc, STGMEDIUM __RPC_FAR * pmedium, BOOL bRelease)
|
|
{
|
|
if (pformatetc == nullptr || pmedium == nullptr)
|
|
return E_INVALIDARG;
|
|
|
|
m_vFormatEtc.push_back(*pformatetc);
|
|
|
|
STGMEDIUM StgMedium = *pmedium;
|
|
|
|
if (!bRelease)
|
|
CopyMedium(&StgMedium, *pmedium, *pformatetc);
|
|
|
|
m_vStgMedium.push_back(StgMedium);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(EnumFormatEtc)
|
|
(DWORD dwDirection, IEnumFORMATETC __RPC_FAR * __RPC_FAR * ppenumFormatEtc)
|
|
{
|
|
if (ppenumFormatEtc == nullptr)
|
|
return E_POINTER;
|
|
|
|
switch (dwDirection)
|
|
{
|
|
case DATADIR_GET:
|
|
*ppenumFormatEtc = new CEnumFormatEtc(m_vFormatEtc);
|
|
((CEnumFormatEtc *)*ppenumFormatEtc)->AddRef();
|
|
return S_OK;
|
|
default:
|
|
*ppenumFormatEtc = nullptr;
|
|
return E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
STDMETHOD(DAdvise)
|
|
(FORMATETC __RPC_FAR * /*pformatetc*/, DWORD /*advf*/, IAdviseSink __RPC_FAR * /*pAdvSink*/, DWORD __RPC_FAR * /*pdwConnection*/)
|
|
{
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
STDMETHOD(DUnadvise)
|
|
(DWORD /*dwConnection*/)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHOD(EnumDAdvise)
|
|
(IEnumSTATDATA __RPC_FAR * __RPC_FAR * /*ppenumAdvise*/)
|
|
{
|
|
return OLE_E_ADVISENOTSUPPORTED;
|
|
}
|
|
|
|
void CopyMedium(STGMEDIUM * pMedDest, STGMEDIUM & MedSrc, FORMATETC & FmtSrc)
|
|
{
|
|
switch (MedSrc.tymed)
|
|
{
|
|
case TYMED_HGLOBAL:
|
|
pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(MedSrc.hGlobal, FmtSrc.cfFormat, NULL);
|
|
break;
|
|
case TYMED_GDI:
|
|
pMedDest->hBitmap = (HBITMAP)OleDuplicateData(MedSrc.hBitmap, FmtSrc.cfFormat, NULL);
|
|
break;
|
|
case TYMED_MFPICT:
|
|
pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(MedSrc.hMetaFilePict, FmtSrc.cfFormat, NULL);
|
|
break;
|
|
case TYMED_ENHMF:
|
|
pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(MedSrc.hEnhMetaFile, FmtSrc.cfFormat, NULL);
|
|
break;
|
|
case TYMED_FILE:
|
|
pMedDest->lpszFileName = (LPOLESTR)OleDuplicateData(MedSrc.lpszFileName, FmtSrc.cfFormat, NULL);
|
|
break;
|
|
case TYMED_ISTREAM:
|
|
pMedDest->pstm = MedSrc.pstm;
|
|
MedSrc.pstm->AddRef();
|
|
break;
|
|
case TYMED_ISTORAGE:
|
|
pMedDest->pstg = MedSrc.pstg;
|
|
MedSrc.pstg->AddRef();
|
|
break;
|
|
}
|
|
|
|
pMedDest->tymed = MedSrc.tymed;
|
|
pMedDest->pUnkForRelease = nullptr;
|
|
|
|
if (MedSrc.pUnkForRelease != nullptr)
|
|
{
|
|
pMedDest->pUnkForRelease = MedSrc.pUnkForRelease;
|
|
MedSrc.pUnkForRelease->AddRef();
|
|
}
|
|
}
|
|
|
|
virtual BOOL OnRenderData(FORMATETC & /*FormatEtc*/, STGMEDIUM * /*pStgMedium*/, BOOL /*bDropComplete*/)
|
|
{
|
|
return FALSE;
|
|
}
|
|
};
|
|
|
|
class CDropTarget : public IDropTarget
|
|
{
|
|
public:
|
|
CDropTarget(HWND hTargetWnd)
|
|
{
|
|
m_hTargetWnd = hTargetWnd;
|
|
m_nRefCount = 0;
|
|
m_bAllowDrop = FALSE;
|
|
m_pDropTargetHelper = nullptr;
|
|
ZeroMemory(&m_FormatEtc, sizeof(FORMATETC));
|
|
ZeroMemory(&m_StgMedium, sizeof(STGMEDIUM));
|
|
|
|
if (FAILED(CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID *)&m_pDropTargetHelper)))
|
|
m_pDropTargetHelper = nullptr;
|
|
}
|
|
|
|
virtual ~CDropTarget()
|
|
{
|
|
if (m_pDropTargetHelper != nullptr)
|
|
{
|
|
m_pDropTargetHelper->Release();
|
|
m_pDropTargetHelper = nullptr;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
HWND m_hTargetWnd;
|
|
int m_nRefCount;
|
|
struct IDropTargetHelper * m_pDropTargetHelper;
|
|
vector<FORMATETC> m_vFormatEtc;
|
|
BOOL m_bAllowDrop;
|
|
FORMATETC m_FormatEtc;
|
|
STGMEDIUM m_StgMedium;
|
|
|
|
public:
|
|
// IUnknown members
|
|
STDMETHOD(QueryInterface)
|
|
(REFIID refiid, void FAR * FAR * ppvObject)
|
|
{
|
|
*ppvObject = (refiid == IID_IUnknown || refiid == IID_IDropTarget) ? this : nullptr;
|
|
|
|
if (*ppvObject != nullptr)
|
|
((LPUNKNOWN)*ppvObject)->AddRef();
|
|
|
|
return *ppvObject == nullptr ? E_NOINTERFACE : S_OK;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, AddRef)
|
|
(void)
|
|
{
|
|
return ++m_nRefCount;
|
|
}
|
|
|
|
STDMETHOD_(ULONG, Release)
|
|
(void)
|
|
{
|
|
int nRefCount = --m_nRefCount;
|
|
if (nRefCount == 0)
|
|
delete this;
|
|
return nRefCount;
|
|
}
|
|
|
|
STDMETHOD(DragEnter)
|
|
(IDataObject __RPC_FAR * pDataObject, DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR * pdwEffect)
|
|
{
|
|
if (pDataObject == nullptr)
|
|
return E_INVALIDARG;
|
|
|
|
if (m_pDropTargetHelper != nullptr)
|
|
m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObject, (LPPOINT)&pt, *pdwEffect);
|
|
|
|
ZeroMemory(&m_FormatEtc, sizeof(FORMATETC));
|
|
if (m_StgMedium.tymed != TYMED_NULL)
|
|
ReleaseStgMedium(&m_StgMedium);
|
|
ZeroMemory(&m_StgMedium, sizeof(STGMEDIUM));
|
|
|
|
for (int nFormatEtc = 0; nFormatEtc < (int)m_vFormatEtc.size(); nFormatEtc++)
|
|
{
|
|
STGMEDIUM StgMedium;
|
|
m_bAllowDrop = (pDataObject->GetData(&m_vFormatEtc[nFormatEtc], &StgMedium) == S_OK);
|
|
|
|
if (m_bAllowDrop)
|
|
{
|
|
// Store drag data for later use in DragOver
|
|
m_FormatEtc = m_vFormatEtc[nFormatEtc];
|
|
m_StgMedium = StgMedium;
|
|
|
|
// Get client cursor position
|
|
CWindow hWnd(m_hTargetWnd);
|
|
CPoint point(pt.x, pt.y);
|
|
hWnd.ScreenToClient(&point);
|
|
|
|
*pdwEffect = OnDragEnter(m_FormatEtc, m_StgMedium, dwKeyState, point);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
QueryDrop(dwKeyState, pdwEffect);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(DragOver)
|
|
(DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR * pdwEffect)
|
|
{
|
|
if (m_pDropTargetHelper)
|
|
m_pDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);
|
|
|
|
if (m_bAllowDrop && m_FormatEtc.cfFormat != CF_NULL && m_StgMedium.tymed != TYMED_NULL)
|
|
{
|
|
// Get client cursor position
|
|
CWindow hWnd(m_hTargetWnd);
|
|
CPoint point(pt.x, pt.y);
|
|
hWnd.ScreenToClient(&point);
|
|
|
|
*pdwEffect = OnDragOver(m_FormatEtc, m_StgMedium, dwKeyState, point);
|
|
}
|
|
|
|
QueryDrop(dwKeyState, pdwEffect);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(DragLeave)
|
|
(void)
|
|
{
|
|
if (m_pDropTargetHelper)
|
|
m_pDropTargetHelper->DragLeave();
|
|
|
|
OnDragLeave();
|
|
|
|
m_bAllowDrop = FALSE;
|
|
|
|
ZeroMemory(&m_FormatEtc, sizeof(FORMATETC));
|
|
if (m_StgMedium.tymed != TYMED_NULL)
|
|
ReleaseStgMedium(&m_StgMedium);
|
|
ZeroMemory(&m_StgMedium, sizeof(STGMEDIUM));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD(Drop)
|
|
(IDataObject __RPC_FAR * pDataObject, DWORD dwKeyState, POINTL pt, DWORD __RPC_FAR * pdwEffect)
|
|
{
|
|
if (pDataObject == nullptr)
|
|
return E_INVALIDARG;
|
|
|
|
if (m_pDropTargetHelper)
|
|
m_pDropTargetHelper->Drop(pDataObject, (LPPOINT)&pt, *pdwEffect);
|
|
|
|
if (m_bAllowDrop && m_FormatEtc.cfFormat != CF_NULL && QueryDrop(dwKeyState, pdwEffect))
|
|
{
|
|
STGMEDIUM StgMedium;
|
|
if (pDataObject->GetData(&m_FormatEtc, &StgMedium) == S_OK)
|
|
{
|
|
// Get client cursor position
|
|
CWindow hWnd(m_hTargetWnd);
|
|
CPoint point(pt.x, pt.y);
|
|
hWnd.ScreenToClient(&point);
|
|
|
|
if (!OnDrop(m_FormatEtc, StgMedium, *pdwEffect, point))
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
|
|
ReleaseStgMedium(&StgMedium);
|
|
}
|
|
}
|
|
|
|
m_bAllowDrop = FALSE;
|
|
|
|
ZeroMemory(&m_FormatEtc, sizeof(FORMATETC));
|
|
if (m_StgMedium.tymed != TYMED_NULL)
|
|
ReleaseStgMedium(&m_StgMedium);
|
|
ZeroMemory(&m_StgMedium, sizeof(STGMEDIUM));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void AddSupportedFormat(FORMATETC & FormatEtc)
|
|
{
|
|
m_vFormatEtc.push_back(FormatEtc);
|
|
}
|
|
|
|
void AddSupportedFormat(CLIPFORMAT cfFormat)
|
|
{
|
|
FORMATETC FormatEtc;
|
|
ZeroMemory(&FormatEtc, sizeof(FORMATETC));
|
|
|
|
FormatEtc.cfFormat = cfFormat;
|
|
FormatEtc.dwAspect = DVASPECT_CONTENT;
|
|
FormatEtc.lindex = -1;
|
|
FormatEtc.tymed = TYMED_HGLOBAL;
|
|
|
|
AddSupportedFormat(FormatEtc);
|
|
}
|
|
|
|
BOOL QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
|
|
{
|
|
DWORD dwEffects = *pdwEffect;
|
|
|
|
if (!m_bAllowDrop)
|
|
{
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwEffect = (dwKeyState & MK_CONTROL) ? ((dwKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY) : ((dwKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : 0);
|
|
if (*pdwEffect == 0)
|
|
{
|
|
if (dwEffects & DROPEFFECT_COPY)
|
|
*pdwEffect = DROPEFFECT_COPY;
|
|
else if (dwEffects & DROPEFFECT_MOVE)
|
|
*pdwEffect = DROPEFFECT_MOVE;
|
|
else if (dwEffects & DROPEFFECT_LINK)
|
|
*pdwEffect = DROPEFFECT_LINK;
|
|
else
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
}
|
|
else if (!(*pdwEffect & dwEffects))
|
|
*pdwEffect = DROPEFFECT_NONE;
|
|
|
|
return (*pdwEffect != DROPEFFECT_NONE);
|
|
}
|
|
|
|
virtual DWORD OnDragEnter(FORMATETC & /*FormatEtc*/, STGMEDIUM & /*StgMedium*/, DWORD /*dwKeyState*/, CPoint /*point*/)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
virtual DWORD OnDragOver(FORMATETC & /*FormatEtc*/, STGMEDIUM & /*StgMedium*/, DWORD /*dwKeyState*/, CPoint /*point*/)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
virtual BOOL OnDrop(FORMATETC & /*FormatEtc*/, STGMEDIUM & /*StgMedium*/, DWORD /*dwEffect*/, CPoint /*point*/)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
virtual void OnDragLeave()
|
|
{
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class CDropTargetT : public CDropTarget
|
|
{
|
|
public:
|
|
CDropTargetT(HWND hTargetWnd) :
|
|
CDropTarget(hTargetWnd)
|
|
{
|
|
m_pDelegate = nullptr;
|
|
}
|
|
|
|
protected:
|
|
T * m_pDelegate;
|
|
|
|
public:
|
|
BOOL Register(T * pDelegate)
|
|
{
|
|
m_pDelegate = pDelegate;
|
|
return TRUE;
|
|
}
|
|
|
|
virtual DWORD OnDragEnter(FORMATETC & FormatEtc, STGMEDIUM & StgMedium, DWORD dwKeyState, CPoint point)
|
|
{
|
|
return m_pDelegate == nullptr ? DROPEFFECT_NONE : m_pDelegate->OnDragEnter(FormatEtc, StgMedium, dwKeyState, point);
|
|
}
|
|
|
|
virtual DWORD OnDragOver(FORMATETC & FormatEtc, STGMEDIUM & StgMedium, DWORD dwKeyState, CPoint point)
|
|
{
|
|
return m_pDelegate == nullptr ? DROPEFFECT_NONE : m_pDelegate->OnDragOver(FormatEtc, StgMedium, dwKeyState, point);
|
|
}
|
|
|
|
virtual BOOL OnDrop(FORMATETC & FormatEtc, STGMEDIUM & StgMedium, DWORD dwEffect, CPoint point)
|
|
{
|
|
return m_pDelegate == nullptr ? FALSE : m_pDelegate->OnDrop(FormatEtc, StgMedium, dwEffect, point);
|
|
}
|
|
|
|
virtual void OnDragLeave()
|
|
{
|
|
if (m_pDelegate != nullptr)
|
|
m_pDelegate->OnDragLeave();
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class CDataObjectT : public CDataObject
|
|
{
|
|
public:
|
|
CDataObjectT(CDropSource * pDropSource) :
|
|
CDataObject(pDropSource)
|
|
{
|
|
m_pDelegate = FALSE;
|
|
}
|
|
|
|
protected:
|
|
T * m_pDelegate;
|
|
|
|
public:
|
|
BOOL Register(T * pDelegate)
|
|
{
|
|
m_pDelegate = pDelegate;
|
|
return TRUE;
|
|
}
|
|
|
|
virtual BOOL OnRenderData(FORMATETC & FormatEtc, STGMEDIUM * pStgMedium, BOOL bDropComplete)
|
|
{
|
|
return m_pDelegate == nullptr ? FALSE : m_pDelegate->OnRenderData(FormatEtc, pStgMedium, bDropComplete);
|
|
}
|
|
};
|
|
|
|
template <class T>
|
|
class CDragDrop
|
|
{
|
|
public:
|
|
CDragDrop()
|
|
{
|
|
m_pDropSource = nullptr;
|
|
m_pDataObject = nullptr;
|
|
m_pDropTarget = nullptr;
|
|
m_hTargetWnd = nullptr;
|
|
}
|
|
|
|
virtual ~CDragDrop()
|
|
{
|
|
if (m_pDropSource != nullptr)
|
|
m_pDropSource->Release();
|
|
if (m_pDataObject != nullptr)
|
|
m_pDataObject->Release();
|
|
}
|
|
|
|
protected:
|
|
CDropSource * m_pDropSource;
|
|
CDataObjectT<T> * m_pDataObject;
|
|
CDropTargetT<T> * m_pDropTarget;
|
|
HWND m_hTargetWnd;
|
|
|
|
public:
|
|
BOOL Register(T * pDelegate, BOOL bDropSource = TRUE)
|
|
{
|
|
m_hTargetWnd = pDelegate->m_hWnd;
|
|
|
|
// Instantiate new drop target object
|
|
m_pDropTarget = new CDropTargetT<T>(m_hTargetWnd);
|
|
m_pDropTarget->Register(pDelegate);
|
|
|
|
// Register drop target
|
|
if (FAILED(RegisterDragDrop(m_hTargetWnd, m_pDropTarget)))
|
|
{
|
|
delete m_pDropTarget;
|
|
m_pDropTarget = nullptr;
|
|
return FALSE;
|
|
}
|
|
|
|
// Is this a drop target only?
|
|
if (!bDropSource)
|
|
return TRUE;
|
|
|
|
// Instantiate new drop source object
|
|
m_pDropSource = new CDropSource;
|
|
m_pDropSource->AddRef();
|
|
|
|
m_pDataObject = new CDataObjectT<T>(m_pDropSource);
|
|
m_pDataObject->AddRef();
|
|
|
|
// Register drop source delegate for data render
|
|
return m_pDataObject->Register(pDelegate);
|
|
}
|
|
|
|
BOOL Revoke()
|
|
{
|
|
m_pDropTarget = nullptr;
|
|
return (RevokeDragDrop(m_hTargetWnd) == S_OK);
|
|
}
|
|
|
|
BOOL AddTargetFormat(CLIPFORMAT cfFormat)
|
|
{
|
|
if (m_pDropTarget == nullptr)
|
|
return FALSE;
|
|
m_pDropTarget->AddSupportedFormat(cfFormat);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL AddSourceFormat(CLIPFORMAT cfFormat)
|
|
{
|
|
if (m_pDataObject == nullptr)
|
|
return FALSE;
|
|
|
|
FORMATETC FormatEtc;
|
|
ZeroMemory(&FormatEtc, sizeof(FORMATETC));
|
|
|
|
FormatEtc.cfFormat = cfFormat;
|
|
FormatEtc.dwAspect = DVASPECT_CONTENT;
|
|
FormatEtc.lindex = -1;
|
|
FormatEtc.tymed = TYMED_HGLOBAL;
|
|
|
|
STGMEDIUM StgMedium;
|
|
ZeroMemory(&StgMedium, sizeof(STGMEDIUM));
|
|
|
|
return SUCCEEDED(m_pDataObject->SetData(&FormatEtc, &StgMedium, TRUE));
|
|
}
|
|
|
|
BOOL SetClipboard(FORMATETC & FormatEtc, STGMEDIUM & StgMedium)
|
|
{
|
|
if (m_pDataObject == nullptr)
|
|
return DROPEFFECT_NONE;
|
|
|
|
if (FAILED(m_pDataObject->SetData(&FormatEtc, &StgMedium, TRUE)))
|
|
return DROPEFFECT_NONE;
|
|
|
|
return (OleSetClipboard(m_pDataObject) == S_OK);
|
|
}
|
|
|
|
BOOL FlushClipboard()
|
|
{
|
|
return (OleFlushClipboard() == S_OK);
|
|
}
|
|
|
|
DWORD DoDragDrop(SHDRAGIMAGE * pDragImage = nullptr, DWORD dwValidEffects = DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK)
|
|
{
|
|
if (m_pDataObject == nullptr)
|
|
return DROPEFFECT_NONE;
|
|
|
|
IDragSourceHelper * pDragSourceHelper = nullptr;
|
|
|
|
// Instantiate drag source helper object
|
|
if (pDragImage != nullptr)
|
|
{
|
|
if (FAILED(CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (LPVOID *)&pDragSourceHelper)))
|
|
pDragSourceHelper = nullptr;
|
|
|
|
if (pDragSourceHelper != nullptr)
|
|
pDragSourceHelper->InitializeFromBitmap(pDragImage, m_pDataObject);
|
|
}
|
|
|
|
DWORD dwEffects = DROPEFFECT_NONE;
|
|
dwEffects = ::DoDragDrop(m_pDataObject, m_pDropSource, dwValidEffects, &dwEffects) == DRAGDROP_S_DROP ? DROPEFFECT_NONE : dwEffects;
|
|
|
|
// Destroy drag source helper object
|
|
if (pDragSourceHelper != nullptr)
|
|
pDragSourceHelper->Release();
|
|
|
|
return dwEffects;
|
|
}
|
|
};
|