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


#include "dataobj.h"



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

CImpIDataObject::CImpIDataObject(PCDataObject 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        i, cItems;
    PRENDERING  pRen;
    DWORD       cb;
    HWND        hList;

    if (NULL==m_pObj->m_hList || NULL==pFE || NULL==pSTM)
        return ResultFromScode(DATA_E_FORMATETC);

    hList=m_pObj->m_hList;
    cItems=(UINT)SendMessage(hList, LB_GETCOUNT, 0, 0L);

    for (i=0; i < cItems; i++)
        {
        cb=SendMessage(hList, LB_GETTEXT, i, (LPARAM)&pRen);

        if (LB_ERR!=cb)
            {
            /*
             * Check if the requested FORMATETC is the same as one
             * that we already have. If so, then copy that STGMEDIUM
             * to pSTM and AddRef ourselves for pUnkForRelease.
             */
            if (pFE->cfFormat==pRen->fe.cfFormat
                && (pFE->tymed & pRen->fe.tymed)
                && pFE->dwAspect==pRen->fe.dwAspect)
                {
                *pSTM=pRen->stm;

                /*
                 * Must remember to AddRef any other objects
                 * in the STGMEDIUM:  storages and streams.
                 */
                if (TYMED_ISTORAGE==pSTM->tymed)
                    pSTM->pstg->AddRef();

                if (TYMED_ISTREAM==pSTM->tymed)
                    pSTM->pstm->AddRef();

                AddRef();
                return NOERROR;
                }
            }
        }

    return ResultFromScode(DATA_E_FORMATETC);
    }



/*
 * CImpIDataObject::GetDataHere
 *
 * Purpose:
 *  Copies a piece of data in this data object to another
 *  STGMEDIUM.  This is only supported for TYMED_ISTORAGE.
 *
 * Parameters:
 *  pFE             LPFORMATETC describing the desired data.
 *  pSTM            LPSTGMEDIUM pointing to the medium into which
 *                  we copy.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CImpIDataObject::GetDataHere(LPFORMATETC pFE
    , LPSTGMEDIUM pSTM)
    {
    UINT        i, cItems;
    PRENDERING  pRen;
    DWORD       cb;
    HWND        hList;

    if (NULL==m_pObj->m_hList || NULL==pFE || NULL==pSTM)
        return ResultFromScode(DATA_E_FORMATETC);

    //We only support IStorage
    if (!(TYMED_ISTORAGE & pFE->tymed))
        return ResultFromScode(DATA_E_FORMATETC);

    hList=m_pObj->m_hList;
    cItems=(UINT)SendMessage(hList, LB_GETCOUNT, 0, 0L);

    for (i=0; i < cItems; i++)
        {
        cb=SendMessage(hList, LB_GETTEXT, i, (LPARAM)&pRen);

        if (LB_ERR!=cb)
            {
            /*
             * When we find a matching FORMATETC, we know we're
             * only looking for IStorage (we checked above), so
             * use IStorage::CopyTo to make the copy.
             */
            if (pFE->cfFormat==pRen->fe.cfFormat
                && (pFE->tymed & pRen->fe.tymed)
                && pFE->dwAspect==pRen->fe.dwAspect)
                {
                pSTM->tymed=TYMED_ISTORAGE;
                return pRen->stm.pstg->CopyTo(NULL, NULL
                    , NULL, pSTM->pstg);
                }
            }
        }

    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        i, cItems;
    PRENDERING  pRen;
    DWORD       cb;
    HWND        hList;

    if (NULL==m_pObj->m_hList || NULL==pFE)
        return ResultFromScode(S_FALSE);

    hList=m_pObj->m_hList;
    cItems=(UINT)SendMessage(hList, LB_GETCOUNT, 0, 0L);

    for (i=0; i < cItems; i++)
        {
        cb=SendMessage(hList, LB_GETTEXT, i, (LPARAM)&pRen);

        if (LB_ERR!=cb)
            {
            /*
             * Check if the requested FORMATETC is the same as one
             * that we already have.
             */
            if (pFE->cfFormat==pRen->fe.cfFormat
                && (pFE->tymed & pRen->fe.tymed)
                && pFE->dwAspect==pRen->fe.dwAspect)
                {
                return NOERROR;
                }
            }
        }

    return ResultFromScode(S_FALSE);
    }




/*
 * 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)
    {
    PRENDERING      prn;

    //We have to remain responsible for the data.
    if (!fRelease)
        return ResultFromScode(E_FAIL);

    //If we're handed NULLs, that means clean out the list.
    if (NULL==pFE || NULL==pSTM)
        {
        m_pObj->Purge();
        return NOERROR;
        }

    /*
     * Here we take the rendering we're given and attach it to the
     * end of the list.  We save the original pSTM->pUnkForRelease
     * and replace it with our own such that each 'copy' of this
     * data is actually just a reference count.
     */

    prn=new RENDERING;

    if (NULL==prn)
        return ResultFromScode(E_OUTOFMEMORY);

    prn->fe=*pFE;
    prn->stm=*pSTM;
    prn->pUnkOrg=pSTM->pUnkForRelease;
    prn->stm.pUnkForRelease=this;

    SendMessage(m_pObj->m_hList, LB_ADDSTRING, 0, (LONG)prn);
    return NOERROR;
    }






/*
 * 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)
    {
    PCEnumFormatEtc     pEnum;

    *ppEnum=NULL;

    /*
     * From an external point of view there are no SET formats,
     * because we want to allow the user of this component object
     * to be able to stuff ANY format in via Set.  Only external
     * users will call EnumFormatEtc and they can only Get.
     */

    switch (dwDir)
        {
        case DATADIR_GET:
             pEnum=new CEnumFormatEtc(m_pUnkOuter);
             break;

        case DATADIR_SET:
        default:
            pEnum=NULL;
            break;
        }

    if (NULL==pEnum)
        return ResultFromScode(E_FAIL);
    else
        {
        //Let the enumerator copy our format list.
        if (!pEnum->FInit(m_pObj->m_hList))
            {
            delete pEnum;
            return ResultFromScode(E_FAIL);
            }

        pEnum->AddRef();
        }

    *ppEnum=pEnum;
    return NOERROR;
    }





/*
 * CImpIDataObject::GetCanonicalFormatEtc
 * CImpIDataObject::DAdvise
 * CImpIDataObject::DUnadvise
 * CImpIDataObject::EnumDAdvise
 *
 * Trivial member functions.
 */

STDMETHODIMP CImpIDataObject::GetCanonicalFormatEtc
    (LPFORMATETC pFEIn, LPFORMATETC pFEOut)
    {
    return ResultFromScode(DATA_S_SAMEFORMATETC);
    }

STDMETHODIMP CImpIDataObject::DAdvise(LPFORMATETC pFE
    , DWORD dwFlags, LPADVISESINK pIAdviseSink, LPDWORD pdwConn)
    {
    return ResultFromScode(E_FAIL);
    }

STDMETHODIMP CImpIDataObject::DUnadvise(DWORD dwConn)
    {
    return ResultFromScode(E_FAIL);
    }

STDMETHODIMP CImpIDataObject::EnumDAdvise(LPENUMSTATDATA *ppEnum)
    {
    return ResultFromScode(E_FAIL);
    }
