--- /dev/null
+/**************************************************************************
+ THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
+ ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+ PARTICULAR PURPOSE.
+ Author: Leon Finker 11/2000
+ Modifications: replaced ATL by STL, Martin Fuchs 7/2003
+**************************************************************************/
+
+// dragdropimp.cpp: implementation of the IDataObjectImpl class.
+//////////////////////////////////////////////////////////////////////
+#include <shlobj.h>
+#include <assert.h>
+
+#include "dragdropimpl.h"
+
+//////////////////////////////////////////////////////////////////////
+// IDataObjectImpl Class
+//////////////////////////////////////////////////////////////////////
+
+IDataObjectImpl::IDataObjectImpl(IDropSourceImpl* pDropSource):
+ m_cRefCount(0),
+ m_pDropSource(pDropSource)
+{
+}
+
+IDataObjectImpl::~IDataObjectImpl()
+{
+ for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
+ ReleaseStgMedium(it->_medium);
+}
+
+STDMETHODIMP IDataObjectImpl::QueryInterface(/* [in] */ REFIID riid,
+/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
+{
+ *ppvObject = NULL;
+ if (IID_IUnknown==riid || IID_IDataObject==riid)
+ *ppvObject=this;
+ /*if (riid == IID_IAsyncOperation)
+ *ppvObject=(IAsyncOperation*)this;*/
+ if (NULL!=*ppvObject)
+ {
+ ((LPUNKNOWN)*ppvObject)->AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) IDataObjectImpl::AddRef()
+{
+ return ++m_cRefCount;
+}
+
+STDMETHODIMP_(ULONG) IDataObjectImpl::Release()
+{
+ long nTemp = --m_cRefCount;
+
+ if (nTemp == 0)
+ delete this;
+
+ return nTemp;
+}
+
+STDMETHODIMP IDataObjectImpl::GetData(
+ /* [unique][in] */ FORMATETC __RPC_FAR *pformatetcIn,
+ /* [out] */ STGMEDIUM __RPC_FAR *pmedium)
+{
+ if (pformatetcIn == NULL || pmedium == NULL)
+ return E_INVALIDARG;
+
+ pmedium->hGlobal = NULL;
+
+ for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
+ {
+ if (pformatetcIn->tymed & it->_format->tymed &&
+ pformatetcIn->dwAspect == it->_format->dwAspect &&
+ pformatetcIn->cfFormat == it->_format->cfFormat)
+ {
+ CopyMedium(pmedium, it->_medium, it->_format);
+ return S_OK;
+ }
+ }
+
+ return DV_E_FORMATETC;
+}
+
+STDMETHODIMP IDataObjectImpl::GetDataHere(
+ /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
+ /* [out][in] */ STGMEDIUM __RPC_FAR *pmedium)
+{
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP IDataObjectImpl::QueryGetData(
+ /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc)
+{
+ if (pformatetc == NULL)
+ return E_INVALIDARG;
+
+ //support others if needed DVASPECT_THUMBNAIL //DVASPECT_ICON //DVASPECT_DOCPRINT
+ if (!(DVASPECT_CONTENT & pformatetc->dwAspect))
+ return (DV_E_DVASPECT);
+
+ HRESULT hr = DV_E_TYMED;
+
+ for(StorageArray::iterator it=_storage.begin(); it!=_storage.end(); ++it)
+ {
+ if (pformatetc->tymed & it->_format->tymed)
+ {
+ if (pformatetc->cfFormat == it->_format->cfFormat)
+ return S_OK;
+ else
+ hr = DV_E_CLIPFORMAT;
+ }
+ else
+ hr = DV_E_TYMED;
+ }
+ return hr;
+}
+
+STDMETHODIMP IDataObjectImpl::GetCanonicalFormatEtc(
+ /* [unique][in] */ FORMATETC __RPC_FAR *pformatectIn,
+ /* [out] */ FORMATETC __RPC_FAR *pformatetcOut)
+{
+ if (pformatetcOut == NULL)
+ return E_INVALIDARG;
+
+ return DATA_S_SAMEFORMATETC;
+}
+
+STDMETHODIMP IDataObjectImpl::SetData(
+ /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
+ /* [unique][in] */ STGMEDIUM __RPC_FAR *pmedium,
+ /* [in] */ BOOL fRelease)
+{
+ if (pformatetc == NULL || pmedium == NULL)
+ return E_INVALIDARG;
+
+ assert(pformatetc->tymed == pmedium->tymed);
+ FORMATETC* fetc=new FORMATETC;
+ STGMEDIUM* pStgMed = new STGMEDIUM;
+
+ if (fetc == NULL || pStgMed == NULL)
+ return E_OUTOFMEMORY;
+
+ ZeroMemory(fetc, sizeof(FORMATETC));
+ ZeroMemory(pStgMed, sizeof(STGMEDIUM));
+
+ *fetc = *pformatetc;
+
+ if (fRelease)
+ *pStgMed = *pmedium;
+ else
+ CopyMedium(pStgMed, pmedium, pformatetc);
+
+ DataStorage storage;
+
+ storage._format = fetc;
+ storage._medium = pStgMed;
+
+ _storage.push_back(storage);
+
+ return S_OK;
+}
+
+void IDataObjectImpl::CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc)
+{
+ switch(pMedSrc->tymed)
+ {
+ case TYMED_HGLOBAL:
+ pMedDest->hGlobal = (HGLOBAL)OleDuplicateData(pMedSrc->hGlobal, pFmtSrc->cfFormat, 0);
+ break;
+ case TYMED_GDI:
+ pMedDest->hBitmap = (HBITMAP)OleDuplicateData(pMedSrc->hBitmap, pFmtSrc->cfFormat, 0);
+ break;
+ case TYMED_MFPICT:
+ pMedDest->hMetaFilePict = (HMETAFILEPICT)OleDuplicateData(pMedSrc->hMetaFilePict, pFmtSrc->cfFormat, 0);
+ break;
+ case TYMED_ENHMF:
+ pMedDest->hEnhMetaFile = (HENHMETAFILE)OleDuplicateData(pMedSrc->hEnhMetaFile, pFmtSrc->cfFormat, 0);
+ break;
+ case TYMED_FILE:
+ pMedDest->lpszFileName = (LPOLESTR)OleDuplicateData(pMedSrc->lpszFileName, pFmtSrc->cfFormat, 0);
+ break;
+ case TYMED_ISTREAM:
+ pMedDest->pstm = pMedSrc->pstm;
+ pMedSrc->pstm->AddRef();
+ break;
+ case TYMED_ISTORAGE:
+ pMedDest->pstg = pMedSrc->pstg;
+ pMedSrc->pstg->AddRef();
+ break;
+ case TYMED_NULL:
+ default:
+ break;
+ }
+ pMedDest->tymed = pMedSrc->tymed;
+ pMedDest->pUnkForRelease = pMedSrc->pUnkForRelease;
+}
+
+STDMETHODIMP IDataObjectImpl::EnumFormatEtc(
+ /* [in] */ DWORD dwDirection,
+ /* [out] */ IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc)
+{
+ if (ppenumFormatEtc == NULL)
+ return E_POINTER;
+
+ *ppenumFormatEtc=NULL;
+ switch (dwDirection)
+ {
+ case DATADIR_GET:
+ *ppenumFormatEtc = new EnumFormatEtcImpl(_storage);
+
+ if (!*ppenumFormatEtc)
+ return E_OUTOFMEMORY;
+
+ (*ppenumFormatEtc)->AddRef();
+ break;
+
+ case DATADIR_SET:
+ default:
+ return E_NOTIMPL;
+ break;
+ }
+
+ return S_OK;
+}
+
+STDMETHODIMP IDataObjectImpl::DAdvise(
+ /* [in] */ FORMATETC __RPC_FAR *pformatetc,
+ /* [in] */ DWORD advf,
+ /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink,
+ /* [out] */ DWORD __RPC_FAR *pdwConnection)
+{
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+STDMETHODIMP IDataObjectImpl::DUnadvise(
+ /* [in] */ DWORD dwConnection)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE IDataObjectImpl::EnumDAdvise(
+ /* [out] */ IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise)
+{
+ return OLE_E_ADVISENOTSUPPORTED;
+}
+
+//////////////////////////////////////////////////////////////////////
+// IDropSourceImpl Class
+//////////////////////////////////////////////////////////////////////
+
+STDMETHODIMP IDropSourceImpl::QueryInterface(/* [in] */ REFIID riid,
+ /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
+{
+ *ppvObject = NULL;
+ if (IID_IUnknown==riid || IID_IDropSource==riid)
+ *ppvObject=this;
+
+ if (*ppvObject != NULL)
+ {
+ ((LPUNKNOWN)*ppvObject)->AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) IDropSourceImpl::AddRef()
+{
+ return ++m_cRefCount;
+}
+
+STDMETHODIMP_(ULONG) IDropSourceImpl::Release()
+{
+ long nTemp = --m_cRefCount;
+
+ assert(nTemp >= 0);
+
+ if (nTemp == 0)
+ delete this;
+
+ return nTemp;
+}
+
+STDMETHODIMP IDropSourceImpl::QueryContinueDrag(
+ /* [in] */ BOOL fEscapePressed,
+ /* [in] */ DWORD grfKeyState)
+{
+ if (fEscapePressed)
+ return DRAGDROP_S_CANCEL;
+ if (!(grfKeyState & (MK_LBUTTON|MK_RBUTTON)))
+ {
+ m_bDropped = true;
+ return DRAGDROP_S_DROP;
+ }
+
+ return S_OK;
+
+}
+
+STDMETHODIMP IDropSourceImpl::GiveFeedback(
+ /* [in] */ DWORD dwEffect)
+{
+ return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
+//////////////////////////////////////////////////////////////////////
+// EnumFormatEtcImpl Class
+//////////////////////////////////////////////////////////////////////
+
+EnumFormatEtcImpl::EnumFormatEtcImpl(const FormatArray& ArrFE)
+ : m_cRefCount(0),
+ m_iCur(0)
+{
+ for(FormatArray::const_iterator it=ArrFE.begin(); it!=ArrFE.end(); ++it)
+ m_pFmtEtc.push_back(*it);
+}
+
+EnumFormatEtcImpl::EnumFormatEtcImpl(const StorageArray& ArrFE)
+ : m_cRefCount(0),
+ m_iCur(0)
+{
+ for(StorageArray::const_iterator it=ArrFE.begin(); it!=ArrFE.end(); ++it)
+ m_pFmtEtc.push_back(*it->_format);
+}
+
+STDMETHODIMP EnumFormatEtcImpl::QueryInterface(REFIID refiid, void** ppv)
+{
+ *ppv = NULL;
+ if (IID_IUnknown==refiid || IID_IEnumFORMATETC==refiid)
+ *ppv=this;
+
+ if (*ppv != NULL)
+ {
+ ((LPUNKNOWN)*ppv)->AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP_(ULONG) EnumFormatEtcImpl::AddRef(void)
+{
+ return ++m_cRefCount;
+}
+
+STDMETHODIMP_(ULONG) EnumFormatEtcImpl::Release(void)
+{
+ long nTemp = --m_cRefCount;
+
+ assert(nTemp >= 0);
+
+ if (nTemp == 0)
+ delete this;
+
+ return nTemp;
+}
+
+STDMETHODIMP EnumFormatEtcImpl::Next( ULONG celt,LPFORMATETC lpFormatEtc, ULONG* pceltFetched)
+{
+ if (pceltFetched != NULL)
+ *pceltFetched=0;
+
+ ULONG cReturn = celt;
+
+ if (celt <= 0 || lpFormatEtc == NULL || m_iCur >= m_pFmtEtc.size())
+ return S_FALSE;
+
+ if (pceltFetched == NULL && celt != 1) // pceltFetched can be NULL only for 1 item request
+ return S_FALSE;
+
+ while (m_iCur < m_pFmtEtc.size() && cReturn > 0)
+ {
+ *lpFormatEtc++ = m_pFmtEtc[m_iCur++];
+ --cReturn;
+ }
+ if (pceltFetched != NULL)
+ *pceltFetched = celt - cReturn;
+
+ return (cReturn == 0) ? S_OK : S_FALSE;
+}
+
+STDMETHODIMP EnumFormatEtcImpl::Skip(ULONG celt)
+{
+ if ((m_iCur + int(celt)) >= m_pFmtEtc.size())
+ return S_FALSE;
+
+ m_iCur += celt;
+ return S_OK;
+}
+
+STDMETHODIMP EnumFormatEtcImpl::Reset(void)
+{
+ m_iCur = 0;
+ return S_OK;
+}
+
+STDMETHODIMP EnumFormatEtcImpl::Clone(IEnumFORMATETC** ppCloneEnumFormatEtc)
+{
+ if (ppCloneEnumFormatEtc == NULL)
+ return E_POINTER;
+
+ EnumFormatEtcImpl* newEnum = new EnumFormatEtcImpl(m_pFmtEtc);
+
+ if (!newEnum)
+ return E_OUTOFMEMORY;
+
+ newEnum->AddRef();
+ newEnum->m_iCur = m_iCur;
+ *ppCloneEnumFormatEtc = newEnum;
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////
+// IDropTargetImpl Class
+//////////////////////////////////////////////////////////////////////
+IDropTargetImpl::IDropTargetImpl(HWND hTargetWnd):
+ m_hTargetWnd(hTargetWnd),
+ m_cRefCount(0), m_bAllowDrop(false),
+ m_pDropTargetHelper(NULL), m_pSupportedFrmt(NULL)
+{
+ assert(m_hTargetWnd != NULL);
+
+ if (FAILED(CoCreateInstance(CLSID_DragDropHelper,NULL,CLSCTX_INPROC_SERVER,
+ IID_IDropTargetHelper,(LPVOID*)&m_pDropTargetHelper)))
+ m_pDropTargetHelper = NULL;
+}
+
+IDropTargetImpl::~IDropTargetImpl()
+{
+ if (m_pDropTargetHelper != NULL)
+ {
+ m_pDropTargetHelper->Release();
+ m_pDropTargetHelper = NULL;
+ }
+}
+
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::QueryInterface( /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
+{
+ *ppvObject = NULL;
+ if (IID_IUnknown==riid || IID_IDropTarget==riid)
+ *ppvObject=this;
+
+ if (*ppvObject != NULL)
+ {
+ ((LPUNKNOWN)*ppvObject)->AddRef();
+ return S_OK;
+ }
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE IDropTargetImpl::Release()
+{
+ long nTemp = --m_cRefCount;
+
+ assert(nTemp >= 0);
+
+ if (nTemp == 0)
+ delete this;
+
+ return nTemp;
+}
+
+bool IDropTargetImpl::QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect)
+{
+ DWORD dwOKEffects = *pdwEffect;
+
+ if (!m_bAllowDrop)
+ {
+ *pdwEffect = DROPEFFECT_NONE;
+ return false;
+ }
+ //CTRL+SHIFT -- DROPEFFECT_LINK
+ //CTRL -- DROPEFFECT_COPY
+ //SHIFT -- DROPEFFECT_MOVE
+ //no modifier -- DROPEFFECT_MOVE or whatever is allowed by src
+ *pdwEffect = (grfKeyState & MK_CONTROL) ?
+ ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY ):
+ ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : DROPEFFECT_NONE );
+ if (*pdwEffect == 0)
+ {
+ // No modifier keys used by user while dragging.
+ if (DROPEFFECT_COPY & dwOKEffects)
+ *pdwEffect = DROPEFFECT_COPY;
+ else if (DROPEFFECT_MOVE & dwOKEffects)
+ *pdwEffect = DROPEFFECT_MOVE;
+ else if (DROPEFFECT_LINK & dwOKEffects)
+ *pdwEffect = DROPEFFECT_LINK;
+ else
+ {
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+ }
+ else
+ {
+ // Check if the drag source application allows the drop effect desired by user.
+ // The drag source specifies this in DoDragDrop
+ if (!(*pdwEffect & dwOKEffects))
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+
+ return (DROPEFFECT_NONE == *pdwEffect)?false:true;
+}
+
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragEnter(
+ /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
+ /* [in] */ DWORD grfKeyState,
+ /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
+{
+ if (pDataObj == NULL)
+ return E_INVALIDARG;
+
+ if (m_pDropTargetHelper)
+ m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, (LPPOINT)&pt, *pdwEffect);
+ //IEnumFORMATETC* pEnum;
+ //pDataObj->EnumFormatEtc(DATADIR_GET,&pEnum);
+ //FORMATETC ftm;
+ //for()
+ //pEnum->Next(1,&ftm,0);
+ //pEnum->Release();
+ m_pSupportedFrmt = NULL;
+
+ for(FormatArray::iterator it=m_formatetc.begin(); it!=m_formatetc.end(); ++it)
+ {
+ m_bAllowDrop = (pDataObj->QueryGetData(&*it) == S_OK)? true: false;
+
+ if (m_bAllowDrop)
+ {
+ m_pSupportedFrmt = &*it;
+ break;
+ }
+ }
+
+ QueryDrop(grfKeyState, pdwEffect);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragOver(
+ /* [in] */ DWORD grfKeyState,
+ /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
+{
+ if (m_pDropTargetHelper)
+ m_pDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);
+ QueryDrop(grfKeyState, pdwEffect);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::DragLeave()
+{
+ if (m_pDropTargetHelper)
+ m_pDropTargetHelper->DragLeave();
+
+ m_bAllowDrop = false;
+ m_pSupportedFrmt = NULL;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::Drop(
+ /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
+ /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
+{
+ if (pDataObj == NULL)
+ return E_INVALIDARG;
+
+ if (m_pDropTargetHelper)
+ m_pDropTargetHelper->Drop(pDataObj, (LPPOINT)&pt, *pdwEffect);
+
+ if (QueryDrop(grfKeyState, pdwEffect))
+ {
+ if (m_bAllowDrop && m_pSupportedFrmt != NULL)
+ {
+ STGMEDIUM medium;
+
+ if (pDataObj->GetData(m_pSupportedFrmt, &medium) == S_OK)
+ {
+ if (OnDrop(m_pSupportedFrmt, medium, pdwEffect)) //does derive class wants us to free medium?
+ ReleaseStgMedium(&medium);
+ }
+ }
+ }
+
+ m_bAllowDrop = false;
+ *pdwEffect = DROPEFFECT_NONE;
+ m_pSupportedFrmt = NULL;
+
+ return S_OK;
+}