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

#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);

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


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,
        ErrQueryRelease,

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


OLESTATUS  FARINTERNAL GenRelease (lpobj)
LPOBJECT_GEN     lpobj;
{
    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 (lpobj, lpstream)
LPOBJECT_GEN    lpobj;
LPOLESTREAM     lpstream;
{
    LPSTR       lpData;
    OLESTATUS   retVal = OLE_OK;
    DWORD       dwClipFormat = NULL;     
    char        formatName[MAX_STR];
    
    if (!lpobj->hData)
        return OLE_ERROR_BLANK;
    
    if (PutBytes (lpstream, (LPSTR) &dwVerToFile, 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 (lpobjsrc, lpclient, lhclientdoc, lpobjname, lplpobj)
LPOBJECT_GEN        lpobjsrc;
LPOLECLIENT         lpclient;
LHCLIENTDOC         lhclientdoc;
LPSTR               lpobjname;
LPOBJECT_GEN  FAR * lplpobj;
{
    if (!lpobjsrc->hData)
        return OLE_ERROR_BLANK;
    
    if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
        return OLE_ERROR_HANDLE;
    
    if (!(*lplpobj = GenCreateObject (lpobjsrc->hData, lpclient, 
                            FALSE, lhclientdoc, 
                            lpobjname, lpobjsrc->head.ctype)))
        return OLE_ERROR_MEMORY;
    else {
        (*lplpobj)->cfFormat = lpobjsrc->cfFormat;
        (*lplpobj)->aClass = DuplicateAtom (lpobjsrc->aClass);
        return OLE_OK;
    }
}



OLESTATUS FARINTERNAL  GenEqual (lpobj1, lpobj2)
LPOBJECT_GEN lpobj1;
LPOBJECT_GEN lpobj2;
{
    if (CmpGlobals (lpobj1->hData, lpobj2->hData))
        return OLE_OK; 
        
    return  OLE_ERROR_NOT_EQUAL;
}



OLESTATUS FARINTERNAL GenCopy (lpobj)
LPOBJECT_GEN lpobj;
{
    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 (lpstream, lpclient, lhclientdoc, lpobjname, lplpobj, objType, aClass, cfFormat)
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 = RegisterClipboardFormat ((LPSTR) "Rich Text Format");
        else 
            lpobj->cfFormat = NULL;
    }
    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 = 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;
        }
        
        *lplpobj = (LPOLEOBJECT) lpobjMf; 
    }
    
    return OLE_OK;

errMem:
    GlobalFree (hData);
    
errLoad:        
    OleDelete ((LPOLEOBJECT)lpobj);
    *lplpobj = NULL;
    return OLE_ERROR_STREAM;
}




LPOBJECT_GEN INTERNAL GenCreateObject (hData, lpclient, fDelete, lhclientdoc, lpobjname, objType)
HANDLE      hData;
LPOLECLIENT lpclient;
BOOL        fDelete;
LHCLIENTDOC lhclientdoc;
LPSTR       lpobjname;
LONG        objType;
{
    LPOBJECT_GEN     lpobj;
    
    if (!hData)
        return NULL;
    
    if (lpobj = GenCreateBlank (lhclientdoc, lpobjname, objType, NULL)) {
        if (GenChangeData (lpobj, hData, lpclient, fDelete) != OLE_OK) {
            GenRelease (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 (lpobj, hSrcData, lpclient, fDelete)
LPOBJECT_GEN    lpobj;
HANDLE          hSrcData;
LPOLECLIENT     lpclient;
BOOL            fDelete;
{
    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 = GlobalSize (hDestData);
    
    return OLE_OK;
}



LPOBJECT_GEN FARINTERNAL GenCreateBlank(lhclientdoc, lpobjname, objType, aClass)
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 (lpclient, lhclientdoc, lpobjname, lplpobj, lpClass, cfFormat, objType)
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 (lpobj, lptype)
LPOLEOBJECT lpobj;
LPLONG      lptype;
{
    return OLE_ERROR_GENERIC;;
}



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

    if (lpobj->cfFormat != cfFormat)
        return OLE_ERROR_FORMAT;
    
    if (!hData)
        return OLE_ERROR_BLANK;
    
    GlobalFree (lpobj->hData);
    lpobj->hData = hData;
    lpobj->sizeBytes = GlobalSize (hData);
    return OLE_OK;
}


OLESTATUS FARINTERNAL GenGetData (lpobj, cfFormat, lphandle)
LPOBJECT_GEN    lpobj;
OLECLIPFORMAT   cfFormat;
LPHANDLE        lphandle;
{
    if (cfFormat != lpobj->cfFormat)
        return OLE_ERROR_FORMAT;

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

}


OLECLIPFORMAT FARINTERNAL GenEnumFormat (lpobj, cfFormat)
LPOBJECT_GEN    lpobj;
OLECLIPFORMAT   cfFormat;
{
    if (!cfFormat)
        return lpobj->cfFormat;
    
    return NULL;
}


OLESTATUS FARINTERNAL GenQueryBounds (lpobj, lpRc)
LPOBJECT_GEN    lpobj;
LPRECT          lpRc;
{
    lpRc->right     = 0;
    lpRc->left      = 0;
    lpRc->top       = 0;
    lpRc->bottom    = 0;
    return OLE_ERROR_GENERIC;
}