/****************************** Module Header ******************************\
* Module Name: GENERIC.C
*
* Handles all API routines for the generic sub-dll of the ole dll.
* Since the data format is unknown, all the routines are written with the
* assumption that all the relevant data is placed in a single global data
* segment. Note that this assumption is not valid for metafiles, bitmaps, and
* and there can always be some other formats with such idiosyncracies. To
* accommodate those cases the rendering dll writer should replace the relevant
* routines after the creation of the generic object. If for a given class this
* assumption (about data format) is valid then the dll writer need to replace
* only the Draw and QueryBounds functions.
*
* Created: November-1990
*
* Copyright (c) 1990, 1991 Microsoft Corporation
*
* History:
*
*  Srinik, Raor  (11/05/90) Designed, coded
*  Curts created NT version
*
\***************************************************************************/

#include <windows.h>
#include "dll.h"
#include "pict.h"

char aMacText[4] = {'T', 'E', 'X', 'T'};
char aMacRtf[4]  = "RTF";

extern OLESTATUS FARINTERNAL wCreateDummyMetaFile (LPOBJECT_MF, int, int);

#ifdef WIN16
#pragma alloc_text(_TEXT, GenSaveToStream, GenLoadFromStream, GetBytes, PutBytes, PutStrWithLen, PutAtomIntoStream, GenQueryBounds)
#endif

OLEOBJECTVTBL    vtblGEN  = {

        ErrQueryProtocol,   // check whether the speced protocol is supported

        GenRelease,         // Release
        ErrShow,            // Show
        ErrPlay,            // plat
        GenGetData,         // Get the object data
        GenSetData,         // Set the object data
        ErrSetTargetDevice, //

        ErrSetBounds,       // set viewport bounds
        GenEnumFormat,      // enumerate supported formats
        ErrSetColorScheme,  //
        GenRelease,         // delete
        ErrSetHostNames,    //

        GenSaveToStream,    // write to file
        GenClone,           // clone object
        ErrCopyFromLink,    // Create embedded from Link

        GenEqual,           // compares the given objects for data equality

        GenCopy,            // copy to clip

        GenDraw,            // draw the object

        ErrActivate,        // open
        ErrExecute,         // excute
        ErrClose,           // Stop
        ErrUpdate,          // Update
        ErrReconnect,       // Reconnect

        ErrObjectConvert,   // convert object to specified type

        ErrGetUpdateOptions, // update options
        ErrSetUpdateOptions, // update options

        ObjRename,          // Change Object name
        ObjQueryName,       // Get current object name

        GenQueryType,       // Object type
        GenQueryBounds,     // QueryBounds
        ObjQuerySize,       // Find the size of the object
        ErrQueryOpen,       // Query open
        ErrQueryOutOfDate,  // query whether object is current

        ErrQueryRelease,     // release related stuff
        ErrQueryRelease,
        ErrQueryReleaseMethod,

        ErrRequestData,    // requestdata
        ErrObjectLong,     // objectLong
        GenChangeData      // change data of the existing object
};


OLESTATUS  FARINTERNAL GenRelease (LPOLEOBJECT lpoleobj)
{
    LPOBJECT_GEN lpobj = (LPOBJECT_GEN)lpoleobj;
    HOBJECT      hobj;

    if (lpobj->hData) {
        GlobalFree (lpobj->hData);
        lpobj->hData = NULL;
    }

    if (lpobj->aClass)
        GlobalDeleteAtom (lpobj->aClass);

    if (lpobj->head.lhclientdoc)
        DocDeleteObject ((LPOLEOBJECT) lpobj);

    if (hobj = lpobj->head.hobj){
        lpobj->head.hobj = NULL;
        GlobalUnlock (hobj);
        GlobalFree (hobj);
    }

    return OLE_OK;
}



OLESTATUS FARINTERNAL GenSaveToStream (
    LPOLEOBJECT     lpoleobj,
    LPOLESTREAM     lpstream
){
    DWORD        dwFileVer = GetFileVersion(lpoleobj);
    LPOBJECT_GEN lpobj     = (LPOBJECT_GEN)lpoleobj;
    LPSTR        lpData;
    OLESTATUS    retVal    = OLE_OK;
    DWORD        dwClipFormat = 0;
    char         formatName[MAX_STR];

    if (!lpobj->hData)
        return OLE_ERROR_BLANK;

    if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG)))
        return OLE_ERROR_STREAM;

    if (PutBytes (lpstream, (LPSTR) &lpobj->head.ctype, sizeof(LONG)))
        return  OLE_ERROR_STREAM;

    if (PutAtomIntoStream (lpstream, lpobj->aClass))
        return OLE_ERROR_STREAM;

    if (lpobj->cfFormat < 0xC000)
        // then it is a predefined format
        dwClipFormat = lpobj->cfFormat;

    if (PutBytes (lpstream, (LPSTR) &dwClipFormat, sizeof(DWORD)))
        return OLE_ERROR_STREAM;

    if (!dwClipFormat) {
        if (!GetClipboardFormatName (lpobj->cfFormat, (LPSTR) formatName,
                        sizeof(formatName)))
            return OLE_ERROR_FORMAT;

        if (PutStrWithLen (lpstream, formatName))
            return OLE_ERROR_STREAM;
    }

    if (!lpobj->sizeBytes)
        return OLE_ERROR_BLANK;

    if (PutBytes (lpstream, (LPSTR) &lpobj->sizeBytes, sizeof(DWORD)))
        return OLE_ERROR_STREAM;

    if (!(lpData = GlobalLock (lpobj->hData)))
        return OLE_ERROR_MEMORY;

    if (PutBytes (lpstream, lpData, lpobj->sizeBytes))
        retVal = OLE_ERROR_STREAM;

    GlobalUnlock (lpobj->hData);
    return retVal;
}


OLESTATUS FARINTERNAL  GenClone (
    LPOLEOBJECT       lpoleobjsrc,
    LPOLECLIENT       lpclient,
    LHCLIENTDOC       lhclientdoc,
    OLE_LPCSTR        lpobjname,
    LPOLEOBJECT FAR * lplpoleobj
){
    LPOBJECT_GEN lpobjsrc = (LPOBJECT_GEN)lpoleobjsrc;

    if (!lpobjsrc->hData)
        return OLE_ERROR_BLANK;

    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;

    if (!(*lplpoleobj = (LPOLEOBJECT)GenCreateObject (lpobjsrc->hData, lpclient,
                            FALSE, lhclientdoc,
                            (LPSTR)lpobjname, lpobjsrc->head.ctype)))
        return OLE_ERROR_MEMORY;
    else {
        ((LPOBJECT_GEN)(*lplpoleobj))->cfFormat = lpobjsrc->cfFormat;
        ((LPOBJECT_GEN)(*lplpoleobj))->aClass = DuplicateAtom (lpobjsrc->aClass);
        return OLE_OK;
    }
}



OLESTATUS FARINTERNAL  GenEqual (
    LPOLEOBJECT lpoleobj1,
    LPOLEOBJECT lpoleobj2
){
    LPOBJECT_GEN lpobj1 = (LPOBJECT_GEN)lpoleobj1;
    LPOBJECT_GEN lpobj2 = (LPOBJECT_GEN)lpoleobj2;

    if (CmpGlobals (lpobj1->hData, lpobj2->hData))
        return OLE_OK;

    return  OLE_ERROR_NOT_EQUAL;
}



OLESTATUS FARINTERNAL GenCopy (LPOLEOBJECT lpoleobj)
{
    LPOBJECT_GEN lpobj = (LPOBJECT_GEN)lpoleobj;
    HANDLE  hData;

    if (!lpobj->hData)
        return OLE_ERROR_BLANK;

    if (!(hData = DuplicateGlobal (lpobj->hData, GMEM_MOVEABLE)))
        return OLE_ERROR_MEMORY;

    SetClipboardData (lpobj->cfFormat, hData);
    return OLE_OK;
}


OLESTATUS FARINTERNAL GenLoadFromStream (
    LPOLESTREAM         lpstream,
    LPOLECLIENT         lpclient,
    LHCLIENTDOC         lhclientdoc,
    LPSTR               lpobjname,
    LPOLEOBJECT FAR *   lplpobj,
    LONG                objType,
    ATOM                aClass,
    OLECLIPFORMAT       cfFormat
){
    LPOBJECT_GEN    lpobj = NULL;
    OLESTATUS       retVal = OLE_ERROR_STREAM;
    HANDLE          hData;
    LPSTR           lpData;
    DWORD           dwClipFormat;
    char            formatName[MAX_STR];
    LONG            length;

    if (!(*lplpobj = (LPOLEOBJECT) (lpobj = GenCreateBlank(lhclientdoc,
                                                    lpobjname, objType,
                                                    aClass)))) {
        if (aClass)
            GlobalDeleteAtom(aClass);
        return OLE_ERROR_MEMORY;
    }

    if (GetBytes (lpstream, (LPSTR) &dwClipFormat, sizeof (DWORD)))
        goto errLoad;

    // If object is from MAC then we will keep the data intact if the data
    // format is either TEXT or RTF
    if (HIWORD(dwVerFromFile) == OS_MAC) {
        if (dwClipFormat ==  *((DWORD *) aMacText))
            lpobj->cfFormat = CF_TEXT;
        else if (dwClipFormat == *((DWORD *) aMacRtf))
            lpobj->cfFormat = (OLECLIPFORMAT)RegisterClipboardFormat ((LPSTR) "Rich Text Format");
        else
            lpobj->cfFormat = 0;
    }
    else {
        // object is created on windows
        if (!dwClipFormat) {
            // this is new file format. format name string follows
            if (GetBytes (lpstream, (LPSTR) &length, sizeof (LONG))
                    || GetBytes (lpstream, (LPSTR)formatName, length)
                    || (!(lpobj->cfFormat = (OLECLIPFORMAT)RegisterClipboardFormat ((LPSTR) formatName))))
                goto errLoad;
        }
        else if ((lpobj->cfFormat = (WORD) dwClipFormat) >= 0xc000) {
            // if format is not predefined and file format is old, then use
            // what value is passed to you through "cfFormat" argument
            lpobj->cfFormat = cfFormat;
        }
    }

    if (GetBytes (lpstream, (LPSTR) &lpobj->sizeBytes, sizeof (DWORD)))
        goto errLoad;

    lpobj->head.lpclient = lpclient;

    retVal = OLE_ERROR_MEMORY;
    if (!(hData = GlobalAlloc (GMEM_MOVEABLE, lpobj->sizeBytes)))
        goto errLoad;

    if (!(lpData = GlobalLock (hData)))
        goto errMem;

    if (GetBytes (lpstream, lpData, lpobj->sizeBytes)) {
        retVal = OLE_ERROR_STREAM;
        GlobalUnlock (hData);
        goto errMem;
    }

    lpobj->hData = hData;
    GlobalUnlock (hData);

    // if the object is from MAC then we want delete this and create blank
    // metafile object, which draws a rectangle
    if ((HIWORD(dwVerFromFile) == OS_MAC) && !lpobj->cfFormat) {
        LPOBJECT_MF lpobjMf;

        OleDelete ((LPOLEOBJECT)lpobj);  // delete generic object

        // Now create a dummy metafile object which draws a rectangle of size
        // 1" x 1". Note that 1" = 2540 HIMETRIC units
        lpobjMf = MfCreateBlank (lhclientdoc, lpobjname, objType);
        lpobjMf->head.cx = lpobjMf->mfp.xExt = 2540;
        lpobjMf->head.cy = - (lpobjMf->mfp.yExt = 2540);
        if ((retVal = wCreateDummyMetaFile (lpobjMf, lpobjMf->mfp.xExt,
                                    lpobjMf->mfp.yExt)) != OLE_OK) {
            OleDelete ((LPOLEOBJECT) lpobjMf);
            return retVal;
        }
    }

    return OLE_OK;

errMem:
    GlobalFree (hData);

errLoad:
    OleDelete ((LPOLEOBJECT)lpobj);
    *lplpobj = NULL;
    return OLE_ERROR_STREAM;

}




LPOBJECT_GEN INTERNAL GenCreateObject (
    HANDLE      hData,
    LPOLECLIENT lpclient,
    BOOL        fDelete,
    LHCLIENTDOC lhclientdoc,
    LPCSTR      lpobjname,
    LONG        objType
){
    LPOBJECT_GEN     lpobj;

    if (!hData)
        return NULL;

    if (lpobj = GenCreateBlank (lhclientdoc, (LPSTR)lpobjname, objType, (ATOM)0)) {
        if (GenChangeData ((LPOLEOBJECT)lpobj, hData, lpclient, fDelete) != OLE_OK) {
            GenRelease ((LPOLEOBJECT)lpobj);
            lpobj = NULL;
        }
    }

    return lpobj;
}


// If the routine fails then the object will be left with it's old data.
// If fDelete is TRUE, then hNewData will be deleted whether the routine
// is successful or not.

OLESTATUS FARINTERNAL GenChangeData (
    LPOLEOBJECT     lpoleobj,
    HANDLE          hSrcData,
    LPOLECLIENT     lpclient,
    BOOL            fDelete
){
    LPOBJECT_GEN lpobj = (LPOBJECT_GEN)lpoleobj;
    HANDLE      hDestData;

    if (!fDelete) {
        if (!(hDestData = DuplicateGlobal (hSrcData, GMEM_MOVEABLE)))
            return OLE_ERROR_MEMORY;
    }
    else {
        // change the ownership to yourself
        if (!(hDestData = GlobalReAlloc(hSrcData,0L,GMEM_MODIFY|GMEM_SHARE))){
            hDestData = DuplicateGlobal (hSrcData, GMEM_MOVEABLE);
            GlobalFree (hSrcData);
            if (!hDestData)
                return OLE_ERROR_MEMORY;
        }
    }

    lpobj->head.lpclient = lpclient;
    if (lpobj->hData)
        GlobalFree (lpobj->hData);
    lpobj->hData = hDestData;
    lpobj->sizeBytes = (DWORD)GlobalSize (hDestData);

    return OLE_OK;
}



LPOBJECT_GEN FARINTERNAL GenCreateBlank(
    LHCLIENTDOC lhclientdoc,
    LPSTR       lpobjname,
    LONG        objType,
    ATOM        aClass
){
    HOBJECT         hobj;
    LPOBJECT_GEN    lpobj;

    if ((hobj = GlobalAlloc (GMEM_MOVEABLE|GMEM_ZEROINIT,sizeof (OBJECT_GEN)))
            == NULL)
        return NULL;

    if (!(lpobj = (LPOBJECT_GEN) GlobalLock (hobj))){
        GlobalFree (hobj);
        return NULL;
    }

    lpobj->head.objId[0]    = 'L';
    lpobj->head.objId[1]    = 'E';
    lpobj->head.mm          = MM_TEXT;
    lpobj->head.ctype       = objType;
    lpobj->head.lpvtbl      = (LPOLEOBJECTVTBL)&vtblGEN;
    lpobj->head.iTable      = INVALID_INDEX;
    lpobj->head.hobj        = hobj;
    lpobj->aClass           = aClass;

    if (objType == CT_STATIC)
        DocAddObject ((LPCLIENTDOC) lhclientdoc,
            (LPOLEOBJECT) lpobj, lpobjname);

    return lpobj;
}


OLESTATUS FARINTERNAL GenPaste (
    LPOLECLIENT         lpclient,
    LHCLIENTDOC         lhclientdoc,
    LPSTR               lpobjname,
    LPOLEOBJECT FAR *   lplpobj,
    LPSTR               lpClass,
    OLECLIPFORMAT       cfFormat,
    LONG                objType
){
    HANDLE  hData = NULL;

    *lplpobj = NULL;
    if (!cfFormat)
        return OLE_ERROR_FORMAT;

    if (!(hData = GetClipboardData(cfFormat)))
        return OLE_ERROR_MEMORY;

    if (!(*lplpobj = (LPOLEOBJECT) GenCreateObject (hData, lpclient,
                                        FALSE, lhclientdoc,
                                        lpobjname, objType)))
        return OLE_ERROR_MEMORY;

    ((LPOBJECT_GEN)(*lplpobj))->cfFormat = cfFormat;
    ((LPOBJECT_GEN)(*lplpobj))->aClass = GlobalAddAtom (lpClass);
    return OLE_OK;

}



OLESTATUS FARINTERNAL GenQueryType (
    LPOLEOBJECT lpobj,
    LPLONG      lptype
){
    UNREFERENCED_PARAMETER(lpobj);
    UNREFERENCED_PARAMETER(lptype);

    return OLE_ERROR_GENERIC;;
}



OLESTATUS FARINTERNAL GenSetData (
    LPOLEOBJECT   lpoleobj,
    OLECLIPFORMAT cfFormat,
    HANDLE        hData
){
    LPOBJECT_GEN  lpobj = (LPOBJECT_GEN)lpoleobj;

    if (lpobj->cfFormat != cfFormat)
        return OLE_ERROR_FORMAT;

    if (!hData)
        return OLE_ERROR_BLANK;

    GlobalFree (lpobj->hData);
    lpobj->hData = hData;
    lpobj->sizeBytes = (DWORD)GlobalSize (hData);
    return OLE_OK;
}


OLESTATUS FARINTERNAL GenGetData (
    LPOLEOBJECT     lpoleobj,
    OLECLIPFORMAT   cfFormat,
    LPHANDLE        lphandle
){
    LPOBJECT_GEN    lpobj = (LPOBJECT_GEN)lpoleobj;

    if (cfFormat != lpobj->cfFormat)
        return OLE_ERROR_FORMAT;

    if (!(*lphandle = lpobj->hData))
        return OLE_ERROR_BLANK;

    return OLE_OK;

}


OLECLIPFORMAT FARINTERNAL GenEnumFormat (
    LPOLEOBJECT   lpoleobj,
    OLECLIPFORMAT cfFormat
){
    LPOBJECT_GEN  lpobj = (LPOBJECT_GEN)lpoleobj;

    if (!cfFormat)
        return lpobj->cfFormat;

    return 0;
}


OLESTATUS FARINTERNAL GenQueryBounds (
    LPOLEOBJECT     lpoleobj,
    LPRECT          lpRc
){
    LPOBJECT_GEN    lpobj = (LPOBJECT_GEN)lpoleobj;

    lpRc->right     = 0;
    lpRc->left      = 0;
    lpRc->top       = 0;
    lpRc->bottom    = 0;
    return OLE_ERROR_GENERIC;
}