Click here to Skip to main content
15,893,722 members
Articles / Desktop Programming / WTL

ListCtrl - A WTL list control with Windows Vista style item selection

Rate me:
Please Sign up or sign in to vote.
4.91/5 (107 votes)
18 Apr 2006CPOL9 min read 520K   11.7K   259  
A flexible WTL list control that supports Windows Vista style selection and cell editing.
#pragma once

#include <vector>
#include <shlobj.h>

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 : NULL;
		
		if ( *ppvObject != NULL )
			( (LPUNKNOWN)*ppvObject )->AddRef();
		
		return *ppvObject == NULL ? 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 != NULL )
			*pceltFetched=0;

		ULONG cReturn = celt;

		if ( celt <= 0 || lpFormatEtc == NULL || m_nIndex >= (int)m_vFormatEtc.size() )
			return S_FALSE;

		if ( pceltFetched == NULL && celt != 1 ) // pceltFetched can be NULL 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 != NULL )
			*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 == NULL )
			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 : NULL;
		
		if ( *ppvObject != NULL )
			( (LPUNKNOWN)*ppvObject )->AddRef();
		
		return *ppvObject == NULL ? 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 : NULL;
		
		if ( *ppvObject != NULL )
			( (LPUNKNOWN)*ppvObject )->AddRef();
		
		return *ppvObject == NULL ? 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 == NULL || pmedium == NULL )
			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 == NULL )
			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 == NULL ? E_INVALIDARG : DATA_S_SAMEFORMATETC;
	}
    
    STDMETHOD(SetData)( FORMATETC __RPC_FAR *pformatetc, STGMEDIUM __RPC_FAR *pmedium, BOOL bRelease )
    {
		if ( pformatetc == NULL || pmedium == NULL )
			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 == NULL )
			return E_POINTER;

		switch ( dwDirection )
		{
			case DATADIR_GET:	*ppenumFormatEtc = new CEnumFormatEtc( m_vFormatEtc );
								( (CEnumFormatEtc*)*ppenumFormatEtc )->AddRef();
								return S_OK;
			default:			*ppenumFormatEtc = NULL;
								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 = NULL;
		
		if ( MedSrc.pUnkForRelease != NULL )
		{
			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 = NULL;
		ZeroMemory( &m_FormatEtc, sizeof( FORMATETC ) );
		ZeroMemory( &m_StgMedium, sizeof( STGMEDIUM ) );
		
		if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDropTargetHelper, (LPVOID*)&m_pDropTargetHelper ) ) )
			m_pDropTargetHelper = NULL;
	}
	
	virtual ~CDropTarget()
	{
		if ( m_pDropTargetHelper != NULL )
		{
			m_pDropTargetHelper->Release();
			m_pDropTargetHelper = NULL;
		}
	}

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 : NULL;
		
		if ( *ppvObject != NULL )
			( (LPUNKNOWN)*ppvObject )->AddRef();
		
		return *ppvObject == NULL ? 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 == NULL )
			return E_INVALIDARG;

		if ( m_pDropTargetHelper != NULL )
			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 == NULL )
			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 = NULL;
	}

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 == NULL ? DROPEFFECT_NONE : m_pDelegate->OnDragEnter( FormatEtc, StgMedium, dwKeyState, point );
	}
	
	virtual DWORD OnDragOver( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwKeyState, CPoint point )
	{
		return m_pDelegate == NULL ? DROPEFFECT_NONE : m_pDelegate->OnDragOver( FormatEtc, StgMedium, dwKeyState, point );
	}
	
	virtual BOOL OnDrop( FORMATETC& FormatEtc, STGMEDIUM& StgMedium, DWORD dwEffect, CPoint point )
	{
		return m_pDelegate == NULL ? FALSE : m_pDelegate->OnDrop( FormatEtc, StgMedium, dwEffect, point );
	}
	
	virtual void OnDragLeave()
	{
		if ( m_pDelegate != NULL )
			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 == NULL ? FALSE : m_pDelegate->OnRenderData( FormatEtc, pStgMedium, bDropComplete );
	}
};

template < class T >
class CDragDrop
{
public:
	CDragDrop()
	{
		m_pDropSource = NULL;
		m_pDataObject = NULL;
		m_pDropTarget = NULL;
		m_hTargetWnd = NULL;
	}
		
	virtual ~CDragDrop()
	{
		if ( m_pDropSource != NULL )
			m_pDropSource->Release();
		if ( m_pDataObject != NULL )
			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 ) ) )
		{
			m_pDropTarget = NULL;
			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 = NULL;
		return ( RevokeDragDrop( m_hTargetWnd ) == S_OK );
	}
	
	BOOL AddTargetFormat( CLIPFORMAT cfFormat )
	{
		if ( m_pDropTarget == NULL )
			return FALSE;
		m_pDropTarget->AddSupportedFormat( cfFormat );
		return TRUE;
	}	
	
	BOOL AddSourceFormat( CLIPFORMAT cfFormat )
	{
		if ( m_pDataObject == NULL )
			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 == NULL )
			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 = NULL, DWORD dwValidEffects = DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK )
	{
		if ( m_pDataObject == NULL )
			return DROPEFFECT_NONE;
			
		IDragSourceHelper *pDragSourceHelper = NULL;
		
		// instantiate drag source helper object
		if ( pDragImage != NULL )
		{
			if ( FAILED( CoCreateInstance( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (LPVOID*)&pDragSourceHelper ) ) )
				pDragSourceHelper = NULL;
			
			if ( pDragSourceHelper != NULL )
				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 != NULL )
			pDragSourceHelper->Release();
		
		return dwEffects;
	}	
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United Kingdom United Kingdom
Alan has been developing applications for a very long time (~10 years), but he's not bitter about this at all. My main area of expertise is C++. He lives in Sweden with his beautiful wife, daughter and son and enjoys climbing mountains and kayaking in his spare time (which isn't much).

Comments and Discussions