// IDataObjectImpl.h: interface for the CIDataObjectImpl class.
/**************************************************************************
   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  1/2001
**************************************************************************/
#ifndef __DRAGDROPIMPL_H__
#define __DRAGDROPIMPL_H__
//#include <ShlDisp.h>
///////////////////////////////////////////////////////////////////////////////////////////////
class CEnumFormatEtc : public IEnumFORMATETC
{
   private:
     ULONG           m_cRefCount;
     CSimpleArray<FORMATETC>  m_pFmtEtc;
     int           m_iCur;

   public:
     CEnumFormatEtc(const CSimpleArray<FORMATETC>& ArrFE);
	 CEnumFormatEtc(const CSimpleArray<FORMATETC*>& ArrFE);
     //IUnknown members
     STDMETHOD(QueryInterface)(REFIID, void FAR* FAR*);
     STDMETHOD_(ULONG, AddRef)(void);
     STDMETHOD_(ULONG, Release)(void);

     //IEnumFORMATETC members
     STDMETHOD(Next)(ULONG, LPFORMATETC, ULONG FAR *);
     STDMETHOD(Skip)(ULONG);
     STDMETHOD(Reset)(void);
     STDMETHOD(Clone)(IEnumFORMATETC FAR * FAR*);
};

///////////////////////////////////////////////////////////////////////////////////////////////
class CIDropSource : public IDropSource
{
	long m_cRefCount;
public:
	bool m_bDropped;

	CIDropSource::CIDropSource():m_cRefCount(0),m_bDropped(false) {}
	//IUnknown
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);        
    virtual ULONG STDMETHODCALLTYPE AddRef( void);
    virtual ULONG STDMETHODCALLTYPE Release( void);
	//IDropSource
    virtual HRESULT STDMETHODCALLTYPE QueryContinueDrag( 
        /* [in] */ BOOL fEscapePressed,
        /* [in] */ DWORD grfKeyState);
    
    virtual HRESULT STDMETHODCALLTYPE GiveFeedback( 
        /* [in] */ DWORD dwEffect);
};

///////////////////////////////////////////////////////////////////////////////////////////////
class CIDataObject : public IDataObject//,public IAsyncOperation
{
	CIDropSource* m_pDropSource;
	long m_cRefCount;
	CSimpleArray<FORMATETC*> m_ArrFormatEtc;
    CSimpleArray<STGMEDIUM*> m_StgMedium;
public:
	CIDataObject(CIDropSource* pDropSource);
	~CIDataObject();
	void CopyMedium(STGMEDIUM* pMedDest, STGMEDIUM* pMedSrc, FORMATETC* pFmtSrc);
    //IUnknown
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);        
    virtual ULONG STDMETHODCALLTYPE AddRef( void);
    virtual ULONG STDMETHODCALLTYPE Release( void);

	//IDataObject
	virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetData( 
        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetcIn,
        /* [out] */ STGMEDIUM __RPC_FAR *pmedium);
    
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetDataHere( 
        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
        /* [out][in] */ STGMEDIUM __RPC_FAR *pmedium);
    
    virtual HRESULT STDMETHODCALLTYPE QueryGetData( 
        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc);
    
    virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc( 
        /* [unique][in] */ FORMATETC __RPC_FAR *pformatectIn,
        /* [out] */ FORMATETC __RPC_FAR *pformatetcOut);
    
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE SetData( 
        /* [unique][in] */ FORMATETC __RPC_FAR *pformatetc,
        /* [unique][in] */ STGMEDIUM __RPC_FAR *pmedium,
        /* [in] */ BOOL fRelease);
    
    virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc( 
        /* [in] */ DWORD dwDirection,
        /* [out] */ IEnumFORMATETC __RPC_FAR *__RPC_FAR *ppenumFormatEtc);
    
    virtual HRESULT STDMETHODCALLTYPE DAdvise( 
        /* [in] */ FORMATETC __RPC_FAR *pformatetc,
        /* [in] */ DWORD advf,
        /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink,
        /* [out] */ DWORD __RPC_FAR *pdwConnection);
    
    virtual HRESULT STDMETHODCALLTYPE DUnadvise( 
        /* [in] */ DWORD dwConnection);
    
    virtual HRESULT STDMETHODCALLTYPE EnumDAdvise( 
        /* [out] */ IEnumSTATDATA __RPC_FAR *__RPC_FAR *ppenumAdvise);

	//IAsyncOperation
    //virtual HRESULT STDMETHODCALLTYPE SetAsyncMode( 
    //    /* [in] */ BOOL fDoOpAsync)
	//{
	//	return E_NOTIMPL;
	//}
    //
    //virtual HRESULT STDMETHODCALLTYPE GetAsyncMode( 
    //    /* [out] */ BOOL __RPC_FAR *pfIsOpAsync)
	//{
	//	return E_NOTIMPL;
	//}
    //
    //virtual HRESULT STDMETHODCALLTYPE StartOperation( 
    //    /* [optional][unique][in] */ IBindCtx __RPC_FAR *pbcReserved)
	//{
	//	return E_NOTIMPL;
	//}
    //
    //virtual HRESULT STDMETHODCALLTYPE InOperation( 
    //    /* [out] */ BOOL __RPC_FAR *pfInAsyncOp)
	//{
	//	return E_NOTIMPL;
	//}
    //
    //virtual HRESULT STDMETHODCALLTYPE EndOperation( 
    //    /* [in] */ HRESULT hResult,
    //    /* [unique][in] */ IBindCtx __RPC_FAR *pbcReserved,
    //    /* [in] */ DWORD dwEffects)
	//{
	//	return E_NOTIMPL;
	//}
};

///////////////////////////////////////////////////////////////////////////////////////////////
class CIDropTarget : public IDropTarget
{
	DWORD m_cRefCount;
	bool m_bAllowDrop;
	struct IDropTargetHelper *m_pDropTargetHelper;
	CSimpleArray<FORMATETC> m_formatetc;
	FORMATETC* m_pSupportedFrmt;
protected:
	HWND m_hTargetWnd;
public:
	
	CIDropTarget(HWND m_hTargetWnd);
	virtual ~CIDropTarget();
	void AddSuportedFormat(FORMATETC& ftetc) { m_formatetc.Add(ftetc); }
	
	//return values: true - release the medium. false - don't release the medium 
	virtual bool OnDrop(FORMATETC* pFmtEtc, STGMEDIUM& medium,DWORD *pdwEffect, POINTL pt) = 0;

	virtual HRESULT STDMETHODCALLTYPE QueryInterface( 
		/* [in] */ REFIID riid,
		/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
	virtual ULONG STDMETHODCALLTYPE AddRef( void) { ATLTRACE("CIDropTarget::AddRef\n"); return ++m_cRefCount; }
	virtual ULONG STDMETHODCALLTYPE Release( void);

    bool QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect);
	virtual HRESULT STDMETHODCALLTYPE DragEnter(
        /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
        /* [in] */ DWORD grfKeyState,
        /* [in] */ POINTL pt,
        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
    virtual HRESULT STDMETHODCALLTYPE DragOver( 
        /* [in] */ DWORD grfKeyState,
        /* [in] */ POINTL pt,
        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
    virtual HRESULT STDMETHODCALLTYPE DragLeave( void);    
    virtual HRESULT STDMETHODCALLTYPE Drop(
        /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
        /* [in] */ DWORD grfKeyState,
        /* [in] */ POINTL pt,
        /* [out][in] */ DWORD __RPC_FAR *pdwEffect);
};

class CDragSourceHelper
{
	IDragSourceHelper* pDragSourceHelper;
public:
	CDragSourceHelper()
	{
		pDragSourceHelper = NULL;
		if(FAILED(CoCreateInstance(CLSID_DragDropHelper,
                        NULL,
                        CLSCTX_INPROC_SERVER,
                        IID_IDragSourceHelper,
                        (void**)&pDragSourceHelper)))
			pDragSourceHelper = NULL;
	}
	virtual ~CDragSourceHelper()
	{
		if( pDragSourceHelper!= NULL )
		{
			pDragSourceHelper->Release();
			pDragSourceHelper=NULL;
		}
	}
    
	// IDragSourceHelper
    HRESULT InitializeFromBitmap(HBITMAP hBitmap, 
		POINT& pt,	// cursor position in client coords of the window
		RECT& rc,	// selected item's bounding rect
		IDataObject* pDataObject,
		COLORREF crColorKey=GetSysColor(COLOR_WINDOW)// color of the window used for transparent effect.
		)
	{
		if(pDragSourceHelper == NULL)
			return E_FAIL;

		    SHDRAGIMAGE di;
            BITMAP      bm;
            GetObject(hBitmap, sizeof(bm), &bm);
            di.sizeDragImage.cx = bm.bmWidth;
            di.sizeDragImage.cy = bm.bmHeight;
            di.hbmpDragImage = hBitmap;
            di.crColorKey = crColorKey; 
            di.ptOffset.x = pt.x - rc.left;
            di.ptOffset.y = pt.y - rc.top;
        return pDragSourceHelper->InitializeFromBitmap(&di, pDataObject);
	}
    HRESULT InitializeFromWindow(HWND hwnd, POINT& pt,IDataObject* pDataObject)
	{		
		if(pDragSourceHelper == NULL)
			return E_FAIL;
		return pDragSourceHelper->InitializeFromWindow(hwnd, &pt, pDataObject);
	}
};
#endif //__DRAGDROPIMPL_H__