//+---------------------------------------------------------------------
//
//  File:       dvutils.cxx
//
//  Contents:   Helper functions for implementing IDataObject and IViewObject
//
//----------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

//
//  Globals
//

UINT OleClipFormat[OCF_LAST+1];         // array of OLE standard clipboard formats

// these are the names of the standard OLE clipboard formats that need to
// be registered.
LPTSTR OleClipNames[OCF_LAST+1] =
{
    TEXT("ObjectLink"),
    TEXT("OwnerLink"),
    TEXT("Native"),
    TEXT("FileName"),
    TEXT("NetworkName"),
    TEXT("DataObject"),
    TEXT("Embedded Object"),
    TEXT("Embed Source"),
    TEXT("Link Source"),
    TEXT("Link Source Descriptor"),
    TEXT("Object Descriptor"),
    TEXT("OleDraw")
};


//+---------------------------------------------------------------
//
//  Function:   RegisterOleClipFormats
//
//  Synopsis:   Initializes the OleClipFormat table of standard
//              OLE clipboard formats.
//
//  Notes:      The OleClipFormat table is a table of registered,
//              standard, OLE-related clipboard formats.  The table
//              is indexed by the OLECLIPFORMAT enumeration.
//              Before this table can be used it must be initialized
//              via this function.
//              This function is usually called in the WinMain or
//              LibMain of the module using the OleClipFormat table.
//
//----------------------------------------------------------------

void
RegisterOleClipFormats(void)
{
    for (int i = 0; i<= OCF_LAST; i++)
        OleClipFormat[i] = RegisterClipboardFormat(OleClipNames[i]);
}

//+---------------------------------------------------------------
//
//  Function:   IsCompatibleDevice, public
//
//  Synopsis:   Compares two DEVICETARGET structures and returns
//              TRUE if they are compatible.
//
//  Arguments:  [ptdLeft] -- A pointer to a device target
//              [ptdRight] -- Another pointer to a device target
//
//  Notes:      The target devices are compatible if they are both
//              NULL or if they are identical.  Otherwise they are
//              incompatible.
//
//----------------------------------------------------------------

BOOL
IsCompatibleDevice(DVTARGETDEVICE FAR* ptdLeft, DVTARGETDEVICE FAR* ptdRight)
{
    // same address of td; must be same (handles NULL case)
    if (ptdLeft == ptdRight)
        return TRUE;

#if 1
    
//fix for NTbug 20692 - reason: see bad assumption below
    if ((ptdRight == DVTARGETIGNORE) || (ptdLeft == DVTARGETIGNORE))
        return TRUE;
    
#endif    

//        The following is a wrong assumption for device independant formats
//        like CF_DIB and CF_METAFILE
    
    // if ones NULL, and the others not then they are incompatible
    if ((ptdRight == NULL) || (ptdLeft == NULL))
        return FALSE;

    // different sizes, not equal
    if (ptdLeft->tdSize != ptdRight->tdSize)
        return FALSE;

    return _fmemcmp(ptdLeft, ptdRight, (size_t)ptdLeft->tdSize) == 0;
}

//+---------------------------------------------------------------
//
//  Function:   IsCompatibleFormat, public
//
//  Synopsis:   Compares two FORMATETC structures and returns
//              TRUE if they are compatible.
//
//  Arguments:  [f1] -- A FORMATETC structure
//              [f2] -- Another FORMATETC structure
//
//  Notes:      This function ignores the lindex member of the
//              FORMATETCs.
//
//----------------------------------------------------------------

BOOL
IsCompatibleFormat(FORMATETC& f1, FORMATETC& f2)
{
    return f1.cfFormat == f2.cfFormat
            && IsCompatibleDevice(f1.ptd, f2.ptd)
            && (f1.dwAspect & f2.dwAspect) != 0L
            && (f1.tymed & f2.tymed) != 0;
}

//+---------------------------------------------------------------
//
//  Function:   FindCompatibleFormat
//
//  Synopsis:   Searches a table of FORMATETC structures and
//              returns the index of the first entry that is
//              compatible with a specified FORMATETC.
//
//  Arguments:  [FmtTable] -- the table of FORMATETCs
//              [iSize] -- the number of entries in the format table
//              [formatetc] -- the FORMATETC we are comparing for compatibility
//
//  Returns:    The index into the table of the compatible format, or
//              -1 if no compatible format was found.
//
//  Notes:      This function is typically used in conjunction with
//              IDataObject methods that need to check if a requested format
//              is available.
//
//----------------------------------------------------------------

int
FindCompatibleFormat(FORMATETC FmtTable[], int iSize, FORMATETC& formatetc)
{
    // look through the table for a compatible format
    for (int i = 0; i < iSize; i++)
    {
        if (IsCompatibleFormat(formatetc, FmtTable[i]))
            return i;
    }
    return -1;
}


//+---------------------------------------------------------------
//
//  Function:   GetObjectDescriptor
//
//  Synopsis:   Extracts an OBJECTDESCRIPTOR from an IDataObject,
//              if available.
//
//  Arguments:  [pDataObj] -- data object from which to extract an object descriptor
//              [pDescOut] -- object descriptor structure to fill in
//
//  Returns:    Success iff the object descriptor could be extracted.
//              This does not copy out the dwFullUserTypeName or
//              dwSrcOfCopy strings.
//
//----------------------------------------------------------------

HRESULT
GetObjectDescriptor(LPDATAOBJECT pDataObj, LPOBJECTDESCRIPTOR pDescOut)
{
    HRESULT r;
    HGLOBAL hglobal = GlobalAlloc(GMEM_SHARE, sizeof(OBJECTDESCRIPTOR)+256);
    LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(hglobal);
    if (pObjDesc == NULL)
    {
        DOUT(TEXT("o2base/dvutils/GetObjectDescriptor failed\r\n"));
        r = E_OUTOFMEMORY;
    }
    else
    {
        FORMATETC formatetc =
            { (WORD)OleClipFormat[OCF_OBJECTDESCRIPTOR],
                NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

        STGMEDIUM stgmedium;
        stgmedium.tymed = TYMED_HGLOBAL;
        stgmedium.hGlobal = hglobal;
        stgmedium.pUnkForRelease = NULL;

        if (OK(r = pDataObj->GetDataHere(&formatetc, &stgmedium)))
        {
            if (pDescOut != NULL)
            {
                // note: in the future we may wish to copy out the strings
                // into two out parameters.  This would be used in
                // implementing the Paste Special dialog box.
                *pDescOut = *pObjDesc;
                pDescOut->dwFullUserTypeName = 0;
                pDescOut->dwSrcOfCopy = 0;
            }
        }
        GlobalUnlock(hglobal);
        GlobalFree(hglobal);
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Function:   UpdateObjectDescriptor
//
//  Synopsis:   Updates the pointl and dwDrawAspects of an OBJECTDESCRIPTOR
//              on a data object
//
//  Arguments:  [pDataObj] -- the data object to update
//              [ptl] -- the pointl to update in the object descriptor
//              [dwAspect] -- the draw aspect to update in the object descriptor
//
//  Returns:    Success iff the object descriptor could be updated
//
//  Notes:      This method is for IDataObjects used in drag-drop.
//              The object being dragged supplies the object descriptor but only
//              the container knows where the point that the mouse button went
//              down relative to the corner of the object, and what aspect
//              of the object the container is displaying.
//              The container uses this method to fill in that missing information.
//              This performs a GetDataHere on the object to get a filled-in
//              object descriptor.  It then updates the pointl and dwDrawAspect
//              fields and uses SetData to update the object.
//
//----------------------------------------------------------------

HRESULT
UpdateObjectDescriptor(LPDATAOBJECT pDataObj, POINTL& ptl, DWORD dwAspect)
{
    HRESULT r;
    HGLOBAL hglobal = GlobalAlloc(GMEM_SHARE, sizeof(OBJECTDESCRIPTOR)+256);
    LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(hglobal);
    if (pObjDesc == NULL)
    {
        DOUT(TEXT("o2base/dvutils/UpdateObjectDescriptor failed\r\n"));
        r = E_OUTOFMEMORY;
    }
    else
    {
        FORMATETC formatetc =
            { (WORD)OleClipFormat[OCF_OBJECTDESCRIPTOR],
                NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

        STGMEDIUM stgmedium;
        stgmedium.tymed = TYMED_HGLOBAL;
        stgmedium.hGlobal = hglobal;
        stgmedium.pUnkForRelease = NULL;

        if (OK(r = pDataObj->GetDataHere(&formatetc, &stgmedium)))
        {
            pObjDesc->pointl = ptl;
            pObjDesc->dwDrawAspect = dwAspect;
            r = pDataObj->SetData(&formatetc, &stgmedium, FALSE);
        }
        GlobalUnlock(hglobal);
        GlobalFree(hglobal);
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Function:   DrawMetafile
//
//  Synopsis:   Creates a metafile from an IViewObject using the Draw method
//
//  Arguments:  [pVwObj] -- the view object
//              [rc] -- rectangle on the view object to draw
//              [dwAspect] -- aspect of the view object to draw, typically
//                           content or icon
//              [pHMF] -- place where handle to the metafile is drawn
//
//  Returns:    Success iff the metafile was drawn successfully.
//
//----------------------------------------------------------------

HRESULT
DrawMetafile(LPVIEWOBJECT pVwObj,
        RECT& rc,
        DWORD dwAspect,
        HMETAFILE FAR* pHMF)
{
    HRESULT hr;
    HMETAFILE hmf = NULL;
    HDC hdc;

    if ((hdc = CreateMetaFile(NULL)) == NULL)
    {
        DOUT(TEXT("o2base/dvutils/DrawMetafile failed to create metafile\r\n"));
        hr = E_OUTOFMEMORY;
    }
    else
    {
        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowOrgEx(hdc, 0, 0, NULL);
        SetWindowExtEx(hdc, rc.right, rc.bottom, NULL);

        hr = OleDraw(pVwObj, dwAspect, hdc, &rc);

        hmf = CloseMetaFile(hdc);

        if (!OK(hr))
        {
            DeleteMetaFile(hmf);
            hmf = NULL;
        }

        if (hmf == NULL)
        {
            DOUT(TEXT("o2base/dvutils/DrawMetafile failed\r\n"));
            hr = E_OUTOFMEMORY;
        }
    }
    *pHMF = hmf;
    return hr;
}


