/*
 * IDATAOBJ.CPP
 * Polyline Component Object Chapter 13
 *
 * Implementation of the IDataObject interface.
 *
 * Copyright (c)1993-1994 Microsoft Corporation, All Rights Reserved
 *
 * Kraig Brockschmidt, Software Design Engineer
 * Microsoft Systems Developer Relations
 *
 * Internet  :  kraigb@microsoft.com
 * Compuserve:  >INTERNET:kraigb@microsoft.com
 */


#include "polyline.h"


/*
 * CImpIDataObject::CImpIDataObject
 * CImpIDataObject::~CImpIDataObject
 *
 * Parameters (Constructor):
 *  pObj            PCPolyline of the object we're in.
 *  pUnkOuter       LPUNKNOWN to which we delegate.
 */

CImpIDataObject::CImpIDataObject(PCPolyline pObj
    , LPUNKNOWN pUnkOuter)
    {
    m_cRef=0;
    m_pObj=pObj;
    m_pUnkOuter=pUnkOuter;
    return;
    }

CImpIDataObject::~CImpIDataObject(void)
    {
    return;
    }




/*
 * CImpIDataObject::QueryInterface
 * CImpIDataObject::AddRef
 * CImpIDataObject::Release
 *
 * Purpose:
 *  IUnknown members for CImpIDataObject object.
 */

STDMETHODIMP CImpIDataObject::QueryInterface(REFIID riid, PPVOID ppv)
    {
    return m_pUnkOuter->QueryInterface(riid, ppv);
    }


STDMETHODIMP_(ULONG) CImpIDataObject::AddRef(void)
    {
    ++m_cRef;
    return m_pUnkOuter->AddRef();
    }

STDMETHODIMP_(ULONG) CImpIDataObject::Release(void)
    {
    --m_cRef;
    return m_pUnkOuter->Release();
    }





/*
 * 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)
    {
    UINT            cf=pFE->cfFormat;

    //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_pIRunnableObject->Run(NULL);

    //Go render the appropriate data for the format.
    switch (cf)
        {
        case CF_METAFILEPICT:
            pSTM->tymed=TYMED_MFPICT;
            return m_pObj->RenderMetafilePict(&pSTM->hGlobal);

        case CF_BITMAP:
            pSTM->tymed=TYMED_GDI;
            return m_pObj->RenderBitmap((HBITMAP *)&pSTM->hGlobal);

        default:
            if (cf==m_pObj->m_cf)
                {
                pSTM->tymed=TYMED_HGLOBAL;
                return m_pObj->RenderNative(&pSTM->hGlobal);
                }

            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)
    {
    UINT        cf;
    HRESULT     hr;

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

    cf=RegisterClipboardFormat(CF_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_pIPersistStorage->Save(pSTM->pstg, FALSE);
        m_pObj->m_pIPersistStorage->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)
    {
    UINT            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_HGLOBAL));
            break;
        }

    return fRet ? NOERROR : ResultFromScode(S_FALSE);
    }






/*
 * 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)
    {
    UINT            cf=pFE->cfFormat;
    BOOL            fRet=FALSE;
    PPOLYLINEDATA   ppl;

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

    /*
     * Data can only come from global memory containing a
     * POLYLINEDATA structure that we send to the Polyline's
     * DataSet, a now internal function used from here and
     * from IPersistStorage::Load.
     */

    if (TYMED_HGLOBAL!=pSTM->tymed)
        return ResultFromScode(DATA_E_FORMATETC);

    ppl=(PPOLYLINEDATA)GlobalLock(pSTM->hGlobal);

    if (NULL!=ppl)
        {
        m_pObj->DataSet(ppl, TRUE, TRUE);
        GlobalUnlock(pSTM->hGlobal);
        fRet=TRUE;
        }

    if (fRelease)
        ReleaseStgMedium(pSTM);

    return fRet ? NOERROR : ResultFromScode(DATA_E_FORMATETC);
    }






/*
 * 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;

    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;
    }
