/*++

Copyright (C) 1993-1999 Microsoft Corporation

Module Name:

    idataobj.cpp

Abstract:

    Implementation of the IDataObject interface.

--*/

#include "polyline.h"
#include "unkhlpr.h"

// CImpIDataObject interface implmentation
IMPLEMENT_CONTAINED_INTERFACE(CPolyline, CImpIDataObject)


/*
 * CImpIDataObject::GetData
 *
 * Purpose:
 *  Retrieves data described by a specific FormatEtc into a StgMedium
 *  allocated by this function.  Used like GetClipboardData.
 *
 * Parameters:
 *  pFE             LPFORMATETC describing the desired data.
 *  pSTM            LPSTGMEDIUM in which to return the data.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP 
CImpIDataObject::GetData(
    LPFORMATETC pFE, 
    LPSTGMEDIUM pSTM)
{
    CLIPFORMAT      cf=pFE->cfFormat;
    IStream         *pIStream;
    HRESULT         hr;
    HDC             hDevDC = NULL;

    //Check the aspects we support.
    if (!(DVASPECT_CONTENT & pFE->dwAspect))
        return ResultFromScode(DATA_E_FORMATETC);

    pSTM->pUnkForRelease=NULL;

    //Run creates the window to use as a basis for extents
    m_pObj->m_pImpIRunnableObject->Run(NULL);

    //Go render the appropriate data for the format.
    switch (cf)
        {
        case CF_METAFILEPICT:
            pSTM->tymed=TYMED_MFPICT;
            hDevDC = CreateTargetDC (NULL, pFE->ptd );
            if (hDevDC) {
                hr = m_pObj->RenderMetafilePict(&pSTM->hGlobal, hDevDC);
                ::DeleteDC(hDevDC);
            }
            else {
                hr = ResultFromScode(E_FAIL);
            }
            return hr;

        case CF_BITMAP:
            pSTM->tymed=TYMED_GDI;
            hDevDC = CreateTargetDC (NULL, pFE->ptd );
            if (hDevDC) {
                hr = m_pObj->RenderBitmap((HBITMAP *)&pSTM->hGlobal, hDevDC);
                ::DeleteDC(hDevDC);
            }
            else {
                hr = ResultFromScode(E_FAIL);
            }
            return hr;

        default:
            if (cf == m_pObj->m_cf)
                {
                hr = CreateStreamOnHGlobal(NULL, TRUE, &pIStream);
                if (FAILED(hr))
                    return ResultFromScode(E_OUTOFMEMORY);

                hr = m_pObj->m_pCtrl->SaveToStream(pIStream);

                if (FAILED(hr))
                    {
                    pIStream->Release();
                    return hr;
                    }

                pSTM->tymed = TYMED_ISTREAM;
                pSTM->pstm = pIStream;
                return NOERROR;
                }

            break;
        }

    return ResultFromScode(DATA_E_FORMATETC);
    }




/*
 * CImpIDataObject::GetDataHere
 *
 * Purpose:
 *  Renders the specific FormatEtc into caller-allocated medium
 *  provided in pSTM.
 *
 * Parameters:
 *  pFE             LPFORMATETC describing the desired data.
 *  pSTM            LPSTGMEDIUM providing the medium into which
 *                  wer render the data.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::GetDataHere(LPFORMATETC pFE, LPSTGMEDIUM pSTM) 
{
    CLIPFORMAT  cf;
    HRESULT     hr;

    /*
     * The only reasonable time this is called is for
     * CFSTR_EMBEDSOURCE and TYMED_ISTORAGE (and later for
     * CFSTR_LINKSOURCE).  This means the same as
     * IPersistStorage::Save.
     */

    cf=(CLIPFORMAT)RegisterClipboardFormat(CFSTR_EMBEDSOURCE);

    //Aspect is unimportant to us here, as is lindex and ptd.
    if (cf == pFE->cfFormat && (TYMED_ISTORAGE & pFE->tymed))
    {
        //We have an IStorage we can write into.
        pSTM->tymed=TYMED_ISTORAGE;
        pSTM->pUnkForRelease=NULL;

        hr = m_pObj->m_pImpIPersistStorage->Save(pSTM->pstg, FALSE);
        m_pObj->m_pImpIPersistStorage->SaveCompleted(NULL);
        return hr;
    }

    return ResultFromScode(DATA_E_FORMATETC);
}



/*
 * CImpIDataObject::QueryGetData
 *
 * Purpose:
 *  Tests if a call to GetData with this FormatEtc will provide
 *  any rendering; used like IsClipboardFormatAvailable.
 *
 * Parameters:
 *  pFE             LPFORMATETC describing the desired data.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::QueryGetData(LPFORMATETC pFE) 
{
    CLIPFORMAT  cf=pFE->cfFormat;
    BOOL        fRet=FALSE;

    //Check the aspects we support.
    if (!(DVASPECT_CONTENT & pFE->dwAspect))
        return ResultFromScode(DATA_E_FORMATETC);

    switch (cf)
    {
        case CF_METAFILEPICT:
            fRet=(BOOL)(pFE->tymed & TYMED_MFPICT);
            break;

        case CF_BITMAP:
            fRet=(BOOL)(pFE->tymed & TYMED_GDI);
            break;

        default:
            //Check our own format.
            fRet=((cf==m_pObj->m_cf)
                && (BOOL)(pFE->tymed & (TYMED_ISTREAM) ));
            break;
    }

    return fRet ? NOERROR : ResultFromScode(DATA_E_FORMATETC);
}


/*
 * CImpIDataObject::GetCanonicalFormatEtc
 *
 * Purpose:
 *  Provides the caller with an equivalent FormatEtc to the one
 *  provided when different FormatEtcs will produce exactly the
 *  same renderings.
 *
 * Parameters:
 *  pFEIn            LPFORMATETC of the first description.
 *  pFEOut           LPFORMATETC of the equal description.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::GetCanonicalFormatEtc
    (LPFORMATETC /* pFEIn */, LPFORMATETC pFEOut)
{
    if (NULL==pFEOut)
        return ResultFromScode(E_INVALIDARG);

    pFEOut->ptd=NULL;
    return ResultFromScode(DATA_S_SAMEFORMATETC);
}



/*
 * CImpIDataObject::SetData
 *
 * Purpose:
 *  Places data described by a FormatEtc and living in a StgMedium
 *  into the object.  The object may be responsible to clean up the
 *  StgMedium before exiting.
 *
 * Parameters:
 *  pFE             LPFORMATETC describing the data to set.
 *  pSTM            LPSTGMEDIUM containing the data.
 *  fRelease        BOOL indicating if this function is responsible
 *                  for freeing the data.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::SetData(
    LPFORMATETC pFE , 
    LPSTGMEDIUM pSTM, 
    BOOL fRelease
    )
{
    CLIPFORMAT  cf=pFE->cfFormat;
    HRESULT     hr;

    //Check for our own clipboard format and DVASPECT_CONTENT
    if ((cf!=m_pObj->m_cf) || !(DVASPECT_CONTENT & pFE->dwAspect))
        return ResultFromScode(DATA_E_FORMATETC);

    // The medium must be a stream
    if (TYMED_ISTREAM != pSTM->tymed)
        return ResultFromScode(DATA_E_FORMATETC);

    hr = m_pObj->m_pCtrl->LoadFromStream(pSTM->pstm);

    if (fRelease)
        ReleaseStgMedium(pSTM);

    return hr;
}


/*
 * CImpIDataObject::EnumFormatEtc
 *
 * Purpose:
 *  Returns an IEnumFORMATETC object through which the caller can
 *  iterate to learn about all the data formats this object can
 *  provide through either GetData[Here] or SetData.
 *
 * Parameters:
 *  dwDir           DWORD describing a data direction, either
 *                  DATADIR_SET or DATADIR_GET.
 *  ppEnum          LPENUMFORMATETC * in which to return the
 *                  pointer to the enumerator.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::EnumFormatEtc(
    DWORD dwDir, 
    LPENUMFORMATETC *ppEnum
    )
{
    return m_pObj->m_pDefIDataObject->EnumFormatEtc(dwDir, ppEnum);
}




/*
 * CImpIDataObject::DAdvise
 * CImpIDataObject::DUnadvise
 * CImpIDataObject::EnumDAdvise
 */

STDMETHODIMP CImpIDataObject::DAdvise(
    LPFORMATETC pFE, 
    DWORD dwFlags, 
    LPADVISESINK pIAdviseSink, 
    LPDWORD pdwConn
    )
{
    HRESULT  hr;

    // Check if requested format is supported
    hr = QueryGetData(pFE);
    if (FAILED(hr))
        return hr;

    if (NULL == m_pObj->m_pIDataAdviseHolder)
    {
        hr = CreateDataAdviseHolder(&m_pObj->m_pIDataAdviseHolder);

        if (FAILED(hr))
            return ResultFromScode(E_OUTOFMEMORY);
    }

    hr = m_pObj->m_pIDataAdviseHolder->Advise(this, 
                                              pFE, 
                                              dwFlags, 
                                              pIAdviseSink, 
                                              pdwConn);

    return hr;
}


STDMETHODIMP CImpIDataObject::DUnadvise(DWORD dwConn)
{
    HRESULT  hr;

    if (NULL==m_pObj->m_pIDataAdviseHolder)
        return ResultFromScode(E_FAIL);

    hr=m_pObj->m_pIDataAdviseHolder->Unadvise(dwConn);

    return hr;
}



STDMETHODIMP CImpIDataObject::EnumDAdvise(LPENUMSTATDATA *ppEnum)
{
    HRESULT  hr;

    if (NULL==m_pObj->m_pIDataAdviseHolder)
        return ResultFromScode(E_FAIL);

    hr=m_pObj->m_pIDataAdviseHolder->EnumAdvise(ppEnum);
    return hr;
}