/*
  OLE SERVER DEMO           
  Obj.c             
                                                                     
  This file contains object methods and various object-related support 
  functions.
                                                                     
  (c) Copyright Microsoft Corp. 1990 - 1992 All Rights Reserved   
*/                                                                     

/* 
   Important Note:

   No method should ever dispatch a DDE message or allow a DDE message to
   be dispatched.
   Therefore, no method should ever enter a message dispatch loop.
   Also, a method should not show a dialog or message box, because the 
   processing of the dialog box messages will allow DDE messages to be
   dispatched.
*/


#define SERVERONLY
#include <windows.h>
#include <ole.h>

#include "srvrdemo.h"



// Static functions.
static HBITMAP GetBitmap (LPOBJ lpobj);
static HANDLE  GetLink (LPOBJ lpobj);
static HANDLE  GetMetafilePict (LPOBJ lpobj);
static HANDLE  GetEnhMetafile (LPOBJ lpobj);
static HANDLE  GetNative (LPOBJ lpobj);
static INT     GetObjNum (LPOBJ lpobj);
static HANDLE  GetText (LPOBJ lpobj);
static VOID    DrawObj (HDC hdc, LPOBJ lpobj, RECT rc, INT dctype);



/* CreateNewObj
 * ------------
 *
 * BOOL fDoc_Changed - The new value for the global variable fDocChanged.
 *                     When initializing a new document, we need to create 
 *                     a new object without the creation counting as a 
 *                     change to the document.
 *
 * RETURNS: A pointer to the new object
 *
 * 
 * CUSTOMIZATION: Re-implement
 *                Some applications (like Server Demo) have a finite number of
 *                fixed, distinct, non-overlapping objects.  Other applications
 *                allow the user to create an object from any section of the
 *                document.  For example, the user might select a portion of
 *                a bitmap from a paint program, or a few lines of text from
 *                a word processor.  This latter type of application probably
 *                will not have a function like CreateNewObj.
 *
 */
LPOBJ CreateNewObj (BOOL fDoc_Changed)
{
    HANDLE hObj = NULL;
    LPOBJ  lpobj = NULL;
    // index into an array of flags indicating if that object number is used.
    INT    ifObj = 0;    

    if ((hObj = LocalAlloc (LMEM_MOVEABLE|LMEM_ZEROINIT, sizeof (OBJ))) == NULL)
      return NULL;

    if ((lpobj = (LPOBJ) LocalLock (hObj)) == NULL)
    {
      LocalFree (hObj);
      return NULL;
    }

    // Fill the fields in the object structure.
    
    // Find an unused number.
    for (ifObj=1; ifObj <= cfObjNums; ifObj++)
    {
      if (docMain.rgfObjNums[ifObj]==FALSE)
      {
         docMain.rgfObjNums[ifObj]=TRUE;
         break;
      }
    }

    if (ifObj==cfObjNums+1)
    {
      // Cannot create any more objects.
      MessageBeep(0);
      return NULL;
    }

    wsprintf (lpobj->native.szName, "Object %d", ifObj);

    lpobj->aName            = GlobalAddAtom (lpobj->native.szName);
    lpobj->hObj             = hObj;
    lpobj->oleobject.lpvtbl = &objvtbl;
    lpobj->native.idmColor  = IDM_RED;    // Default color 
    lpobj->native.version   = version;
    lpobj->native.nWidth    = OBJECT_WIDTH;          // Default size
    lpobj->native.nHeight   = OBJECT_HEIGHT;
    SetHiMetricFields (lpobj);

    // Place object in a location corrsponding to its number, for aesthetics.
    lpobj->native.nX = (ifObj - 1) * 20;
    lpobj->native.nY = (ifObj - 1) * 20;

    if (!CreateWindow (
        "ObjClass",
        "Obj",
        WS_BORDER | WS_THICKFRAME | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
        lpobj->native.nX,
        lpobj->native.nY,
        lpobj->native.nWidth,
        lpobj->native.nHeight,
        hwndMain,
        NULL,
        hInst,
        (LPSTR) lpobj ))
         return FALSE;

    fDocChanged = fDoc_Changed;

    return lpobj;
}



/* CutOrCopyObj
 * ------------
 *
 * Put data onto clipboard in all the formats supported.  If the 
 * fOpIsCopy is TRUE, the operation is COPY, otherwise it is CUT.
 * This is important, because we cannot put the Object Link format
 * onto the clipboard if the object was cut from the document (there is
 * no longer anything to link to).
 *
 * BOOL fOpIsCopy - TRUE if the operation is COPY; FALSE if CUT
 * 
 * CUSTOMIZATION: None
 *
 *
 */
VOID CutOrCopyObj (BOOL fOpIsCopy)
{
    LPOBJ       lpobj;
    HANDLE      hData;
//	 UINT     hBit;

    if (OpenClipboard (hwndMain))
    {
        EmptyClipboard ();

        lpobj = SelectedObject();

        if ((hData = GetNative (lpobj)) != NULL)
            SetClipboardData(cfNative, hData);

        if ((hData = GetLink(lpobj)) != NULL)
            SetClipboardData(cfOwnerLink, hData);

        if (fOpIsCopy && docMain.doctype == doctypeFromFile)
        {
            // Can create a link if object exists in a file.
            if ((hData = GetLink(lpobj)) != NULL)
               SetClipboardData(cfObjectLink, hData);
        }

        if ((hData = GetEnhMetafile(lpobj)) != NULL)
        {
            SetClipboardData(CF_ENHMETAFILE, hData);
              GlobalFree(hData);
        }

        if ((hData = GetBitmap(lpobj)) != NULL)
        {
        //	  SetClipboardData(CF_BITMAP, GetBitmap(lpobj));
              SetClipboardData(CF_BITMAP, hData);
              DeleteObject(hData);
        }


        CloseClipboard ();
    }
}


/* DestroyObj
 * ----------
 *
 * Revoke an object, and free all memory that had been allocated for it.
 *
 * HWND hwnd - The object's window
 * 
 * CUSTOMIZATION: Re-implement, making sure you free all the memory that
 *                had been allocated for the OBJ structure and each of its
 *                fields.
 * 
 */
VOID DestroyObj (HWND hwnd)
{
   LPOBJ lpobj = HwndToLpobj (hwnd);

   if(lpobj->aName)
   {
      GlobalDeleteAtom (lpobj->aName);
      lpobj->aName = '\0';
   }

   if (lpobj->hpal) 
      DeleteObject (lpobj->hpal);
   // Allow the object's number to be reused.
   docMain.rgfObjNums [GetObjNum(lpobj)] = FALSE;


   // Free the memory that had been allocated for the object structure itself.
   LocalUnlock (lpobj->hObj);
   LocalFree (lpobj->hObj);
}



/* DrawObj
 * -------
 *
 * This function draws an object onto the screen, into a metafile, or into
 * a bitmap.
 * The object will always look the same.
 *
 * HDC    hdc    - The device context to render the object into
 * LPOBJ  lpobj  - The object to render
 * RECT   rc     - The rectangle bounds of the object
 * DCTYPE dctype - The type of device context.
 * 
 * CUSTOMIZATION: Server Demo specific
 *
 */
static VOID DrawObj (HDC hdc, LPOBJ lpobj, RECT rc, INT dctype)
{
   HPEN     hpen;
   HPEN     hpenOld;
   HPALETTE hpalOld = NULL;


   if (dctype == dctypeMetafile)
   {
      SetWindowOrgEx (hdc, 0, 0, NULL);
      // Paint entire object into the given rectangle.
      SetWindowExtEx (hdc, rc.right, rc.bottom, NULL);
   }
 
   if (lpobj->hpal)
   {
      hpalOld = SelectPalette (hdc, lpobj->hpal, TRUE);
      RealizePalette (hdc);
   }

   // Select brush of the color specified in the native data.
   SelectObject (hdc, hbrColor [lpobj->native.idmColor - IDM_RED] );

   hpen = CreatePen (PS_SOLID, 
                     /* Width */ (rc.bottom-rc.top) / 10,
                     /* Gray */ 0x00808080);
   hpenOld = SelectObject (hdc, hpen);

   // Draw rectangle with the gray pen and fill it in with the selected brush.
   Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);

   // Print name of object inside rectangle.
   SetBkMode (hdc, TRANSPARENT);
   SetTextAlign (hdc, TA_BASELINE | TA_CENTER);
   TextOut (hdc, 
            rc.right/2, 
            (rc.top+rc.bottom)/2, 
            lpobj->native.szName, 
            lstrlen (lpobj->native.szName));

   // Restore original objects
   SelectObject (hdc, 
                 (dctype == dctypeMetafile || dctype == dctypeEnhMetafile) 
                     ? GetStockObject (BLACK_PEN) : hpenOld);
   if (hpalOld)
   {
      SelectPalette (hdc, 
                     (dctype == dctypeMetafile || dctype == dctypeEnhMetafile) 
                        ? GetStockObject (DEFAULT_PALETTE) : hpalOld,
                     TRUE);
   }

   DeleteObject (hpen);
}



/* GetBitmap
 * ---------
 *
 * Return a handle to an object's picture data in bitmap format.
 *
 * LPOBJ lpobj - The object
 * 
 * RETURNS: A handle to the object's picture data
 * 
 * CUSTOMIZATION: Re-implement
 * 
 */
static HBITMAP GetBitmap (LPOBJ lpobj)
{
    HDC         hdcObj;
    HDC         hdcMem;
    RECT        rc;
    HBITMAP     hbitmap;
    HBITMAP    hbitmapOld;


    hdcObj = GetDC (lpobj->hwnd);
    // Create a memory device context.
    hdcMem = CreateCompatibleDC (hdcObj);
    GetClientRect (lpobj->hwnd, (LPRECT)&rc);
    // Create new bitmap object based on the bitmap of the OLE object.
    hbitmap = CreateCompatibleBitmap 
      (hdcObj, rc.right - rc.left, rc.bottom - rc.top);
    // Select new bitmap as the bitmap object for the memory device context.
    hbitmapOld = SelectObject (hdcMem, hbitmap);

    // Paint directly into the memory dc using the new bitmap object.
    DrawObj (hdcMem, lpobj, rc, dctypeBitmap);

    // Restore old bitmap object.
    hbitmap = SelectObject (hdcMem, hbitmapOld);
    DeleteDC (hdcMem);
    ReleaseDC (lpobj->hwnd, hdcObj);

    // convert width and height to HIMETRIC units
    rc.right  = rc.right - rc.left;
    rc.bottom = rc.bottom - rc.top;
    DeviceToHiMetric ( (LPPOINT) &rc.right );
    
    // Set the 1/10 of HIMETRIC units for the bitmap
    SetBitmapDimensionEx (hbitmap, (DWORD) (rc.right/10), (DWORD) (rc.bottom/10), NULL);

//    if (OpenClipboard (hwndMain))
//    {
//  //      EmptyClipboard ();
//          SetClipboardData(CF_BITMAP, hbitmap);
//          CloseClipboard();
//    }
	 return hbitmap;
}



/* GetLink
 * -------
 *
 * Return a handle to an object's object or owner link data.
 * Link information is in the form of three zero-separated strings,
 * terminated with two zero bytes:  CLASSNAME\0DOCNAME\0OBJNAME\0\0
 *
 * LPOBJ lpobj - The object 
 * 
 * RETURNS: A handle to the object's link data
 * 
 * CUSTOMIZATION: Re-implement
 *
 */
static HANDLE GetLink (LPOBJ lpobj)
{

    CHAR   sz[cchFilenameMax];
    LPSTR  lpszLink = NULL;
    HANDLE hLink = NULL;
    INT    cchLen;
    INT    i;

    // First make the class name.
    lstrcpy (sz, szClassName);
    cchLen = lstrlen (sz) + 1;

    // Then the document name.
    cchLen += GlobalGetAtomName 
               (docMain.aName, (LPSTR)sz + cchLen, 
                cchFilenameMax - cchLen) + 1;

    // Then the object name.
    lstrcpy (sz + cchLen, lpobj->native.szName);
    cchLen += lstrlen (lpobj->native.szName) + 1;

    // Add a second null to the end.
    sz[cchLen++] = 0;       


    hLink = GlobalAlloc (GMEM_DDESHARE | GMEM_ZEROINIT, cchLen);
    if (hLink == NULL)
      return NULL;
    if ((lpszLink = GlobalLock (hLink)) == NULL)
    {
      GlobalFree (hLink);
      return NULL;
    }

    for (i=0; i < cchLen; i++)
        lpszLink[i] = sz[i];

    GlobalUnlock (hLink);

    return hLink;
}



/* GetMetafilePict
 * ---------------
 *
 * Return a handle to an object's picture data in metafile format.
 *
 * LPOBJ lpobj - The object
 * 
 * RETURNS: A handle to the object's data in metafile format.
 *
 * CUSTOMIZATION: Re-implement
 *
 */
static HANDLE GetMetafilePict (LPOBJ lpobj)
{

    LPMETAFILEPICT  lppict = NULL;
    HANDLE          hpict = NULL;
    HANDLE          hMF = NULL;
    RECT            rc;
    HDC             hdc;

    hdc = CreateMetaFile(NULL);

    GetClientRect (lpobj->hwnd, (LPRECT)&rc);

    // Paint directly into the metafile.
    DrawObj (hdc, lpobj, rc, dctypeMetafile);

    // Get handle to the metafile.
    if ((hMF = CloseMetaFile (hdc)) == NULL)
      return NULL;

    if(!(hpict = GlobalAlloc (GMEM_DDESHARE, sizeof (METAFILEPICT))))
    {
        DeleteMetaFile (hMF);
        return NULL;
    }

    if ((lppict = (LPMETAFILEPICT)GlobalLock (hpict)) == NULL)
    {
        DeleteMetaFile (hMF);
        GlobalFree (hpict);
        return NULL;
    }

    rc.right  = rc.right - rc.left;
    rc.bottom = rc.bottom - rc.top;
    
    DeviceToHiMetric ( (LPPOINT) &rc.right);

    lppict->mm   =  MM_ANISOTROPIC;
    lppict->hMF  =  hMF;
    lppict->xExt =  rc.right;
    lppict->yExt =  rc.bottom;
    GlobalUnlock (hpict);
    return hpict;
}

/* GetEnhMetafile
 * ---------------
 *
 * Return a handle to an object's picture data in metafile format.
 *
 * LPOBJ lpobj - The object
 * 
 * RETURNS: A handle to the object's data in metafile format.
 *
 * CUSTOMIZATION: Re-implement
 *
 */
static HANDLE GetEnhMetafile (LPOBJ lpobj)
{

    LPMETAFILEPICT  lppict = NULL;
    HANDLE          hemf   = NULL;
    HANDLE          hMF    = NULL;
    RECT            rc;
    HDC             hdc, hdc2;


    GetClientRect (lpobj->hwnd, (LPRECT)&rc);

    rc.right   -= rc.left;
    rc.bottom  -= rc.top;
    rc.left     = rc.top  = 0;
	 
    DeviceToHiMetric ( (LPPOINT) &rc.right );
	 
    hdc = CreateEnhMetaFile ( NULL, NULL, &rc, NULL );
    
                                       //* this is necessary because
                                       //* we need to draw the object
                                       //* in device coordinates that are
                                       //* the same physical size as the HIMETRIC
                                       //* logical space used in CreateEnhMetaFile.
                                       //* In this case we have scaled the HIMETRIC
                                       //* units down in order to use the logical
                                       //* pixel ratio (which is recommended UI)
                                       //* so we therefore have to convert the
                                       //* scaled HIMETRIC units back to Device.
                                      
    hdc2 = GetDC(NULL);				

    SetMapMode(hdc2, MM_HIMETRIC);
    LPtoDP (hdc2, (LPPOINT)&rc.right, 1);
    if (rc.bottom < 0) rc.bottom *= -1;

    ReleaseDC(NULL,hdc2);

	DrawObj (hdc, lpobj, rc, dctypeMetafile);

    if ((hemf = (HANDLE)CloseEnhMetaFile (hdc)) == NULL)
      return NULL;

    return hemf;
}


/* GetNative
 * ---------
 *
 * Return a handle to an object's native data.
 *
 * LPOBJ lpobj - The object whose native data is to be retrieved.
 * 
 * RETURNS: a handle to the object's native data.
 *
 * CUSTOMIZATION: The line "*lpnative = lpobj->native;" will change to 
 *                whatever code is necessary to copy an object's native data.
 *
 */
static HANDLE GetNative (LPOBJ lpobj)
{
   LPNATIVE lpnative = NULL;
   HANDLE   hNative  = NULL;

   hNative = GlobalAlloc (GMEM_DDESHARE | GMEM_ZEROINIT, sizeof (NATIVE));
   if (hNative == NULL)
      return NULL;
   if ((lpnative = (LPNATIVE) GlobalLock (hNative)) == NULL)
   {
      GlobalFree (hNative);
      return NULL;
   }

   // Copy the native data.
   *lpnative = lpobj->native;

   GlobalUnlock (hNative);
   return hNative;
}



/* GetObjNum
 * ---------
 *
 * LPSTR lpobj - The object whose number is desired
 *
 * RETURNS: The number of the object, i.e., the numerical portion of its name.
 *
 * CUSTOMIZATION: Server Demo specific
 */
static INT GetObjNum (LPOBJ lpobj)
{
   LPSTR lpsz;
   INT n=0;

   lpsz = lpobj->native.szName + 7;
   while (*lpsz && *lpsz>='0' && *lpsz<='9')
      n = 10*n + *lpsz++ - '0';
   return n;
}



/* GetText
 * -------
 *
 * Return a handle to an object's data in text form.
 * This function simply returns the name of the object.
 *
 * LPOBJ lpobj - The object
 * 
 * RETURNS: A handle to the object's text.
 *
 * CUSTOMIZATION: Re-implement, if your application supports CF_TEXT as a 
 *                presentation format.
 *
 */
static HANDLE GetText (LPOBJ lpobj)
{
    HANDLE hText    = NULL;
    LPSTR  lpszText = NULL;

    if(!(hText = GlobalAlloc (GMEM_DDESHARE, sizeof (lpobj->native.szName))))
      return NULL;

    if (!(lpszText = GlobalLock (hText)))
      return NULL;

    lstrcpy (lpszText, lpobj->native.szName);

    GlobalUnlock (hText);

    return hText;
}



/* ObjDoVerb                OBJECT "DoVerb" METHOD
 * ---------
 *
 * This method is called by the client, through the library, to either
 * PLAY, or EDIT the object.  PLAY is implemented as a beep, and
 * EDIT will bring up the server and show the object for editing.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * WORD wVerb              - The verb acting on the object: PLAY or EDIT
 * BOOL fShow              - Should the object be shown?
 * BOOL fTakeFocus         - Should the object window get the focus?
 * 
 * RETURNS:        OLE_OK
 *
 * CUSTOMIZATION: Add any more verbs your application supports.
 *                Implement verbPlay if your application supports it.
 *
 */
OLESTATUS  APIENTRY ObjDoVerb 
   (LPOLEOBJECT lpoleobject, UINT wVerb, BOOL fShow, BOOL fTakeFocus)
{
    switch (wVerb) 
    {
         case verbPlay:
         {  // The application can do whatever is appropriate for the object.
            INT i;
            for (i=0; i<25;i++) MessageBeep (0);
            return OLE_OK;
         }

         case verbEdit:
            if (fShow)
               return objvtbl.Show (lpoleobject, fTakeFocus);
            else
               return OLE_OK;
         default:
            // Unknown verb.
            return OLE_ERROR_DOVERB;
    }
}



/* ObjEnumFormats        OBJECT "EnumFormats" METHOD
 * ---------------
 *
 * This method is used to enumerate all supported clipboard formats.
 * Terminate by returning NULL.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * OLECLIPFORMAT cfFormat  - The 'current' clipboard format
 * 
 * RETURNS: The 'next' clipboard format which is supported.
 *
 * CUSTOMIZATION: Verify that the list of formats this function 
 *                returns matches the list of formats your application 
 *                supports.
 *
 */
OLECLIPFORMAT  APIENTRY ObjEnumFormats
   (LPOLEOBJECT lpoleobject, OLECLIPFORMAT cfFormat)
{
      if (cfFormat == 0)
        return cfNative;

      if (cfFormat == cfNative)
         return cfOwnerLink;

      if (cfFormat == cfOwnerLink)
         return CF_ENHMETAFILE;

      if (cfFormat == CF_ENHMETAFILE)
         return CF_METAFILEPICT;

      if (cfFormat == CF_METAFILEPICT)
         return CF_BITMAP;

      if (cfFormat == CF_BITMAP)
         return cfObjectLink;

      if (cfFormat == cfObjectLink)
         return 0;

      return 0;
}



/* ObjGetData                OBJECT "GetData" METHOD
 * -----------
 *
 * Return the data requested for the specified object in the specified format.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * WORD cfFormat           - The data type requested in standard
 *                           clipboard format
 * LPHANDLE lphandle       - Pointer to handle to memory where data
 *                           will be stored
 * 
 * RETURNS: OLE_OK           if successful
 *          OLE_ERROR_MEMORY if there was an error getting the data.
 *          OLE_ERROR_FORMAT if the requested format is unknown.
 *
 * 
 * CUSTOMIZATION: Add any additional formats your application supports, and
 *                remove any formats it does not support.
 *
 */
OLESTATUS  APIENTRY ObjGetData
   (LPOLEOBJECT lpoleobject, OLECLIPFORMAT cfFormat, LPHANDLE lphandle)
{

   LPOBJ lpobj;

   lpobj = (LPOBJ) lpoleobject;

   if (cfFormat ==  cfNative)
   {
      if (!(*lphandle = GetNative (lpobj)))
         return OLE_ERROR_MEMORY;
      // The client has requested the data in native format, therefore
      // the data in the client and server are in sync.
      fDocChanged = FALSE;
      return OLE_OK; 
   }                

   if (cfFormat == CF_ENHMETAFILE)
   {
      if (!(*lphandle = GetEnhMetafile (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   if (cfFormat == CF_METAFILEPICT)
   {
      if (!(*lphandle = GetMetafilePict (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   if (cfFormat == CF_BITMAP)
   {
      if (!(*lphandle = (HANDLE)GetBitmap (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   if (cfFormat == CF_TEXT) 
   {
      if (!(*lphandle = GetText (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   if (cfFormat == cfObjectLink)
   {
      if (!(*lphandle = GetLink (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   if (cfFormat ==  cfOwnerLink)
   {
      if (!(*lphandle = GetLink (lpobj)))
         return OLE_ERROR_MEMORY;
      return OLE_OK; 
   }

   return OLE_ERROR_FORMAT;
}



/* ObjQueryProtocol                OBJECT "QueryProtocol" METHOD
 * ----------------
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * OLE_LPCSTR lpszProtocol      - The protocol name, either "StdFileEditing"
 *                           or "StdExecute"
 * 
 * RETURNS: If lpszProtocol is supported, return a pointer to an OLEOBJECT 
 *          structure with an appropriate method table for that protocol.
 *          Otherwise, return NULL.
 *
 * CUSTOMIZATION: Allow any additional protocols your application supports.
 *
 *
 */
LPVOID  APIENTRY ObjQueryProtocol 
   (LPOLEOBJECT lpoleobject, OLE_LPCSTR lpszProtocol)
{
   return lstrcmp (lpszProtocol, "StdFileEditing") ? NULL : lpoleobject ;
}



/* ObjRelease                OBJECT "Release" METHOD
 * -----------
 *
 * The server application should not destroy data when the library calls the 
 * ReleaseObj method.
 * The library calls the ReleaseObj method when no clients are connected 
 * to the object.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * 
 * RETURNS: OLE_OK
 * 
 * CUSTOMIZATION: Re-implement.  Do whatever needs to be done, if anything,
 *                when no clients are connected to an object.
 *
 */
OLESTATUS  APIENTRY ObjRelease (LPOLEOBJECT lpoleobject)
{
   INT i;
   /* No client is connected to the object so break all assocaiations
      between clients and the object. */
   for (i=0; i < clpoleclient; i++)
      ((LPOBJ)lpoleobject)->lpoleclient[i] = NULL;
   return OLE_OK;
}



/* ObjSetBounds        OBJECT "SetBounds" METHOD
 * ------------
 *
 * This method is called to set new bounds for an object.
 * The bounds are in HIMETRIC units.
 * A call to this method is ignored for linked objects because the size of
 * a linked object depends only on the source file.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * OLE_CONST RECT FAR* lprect           - The new bounds
 * 
 * RETURNS: OLE_OK
 *
 * CUSTOMIZATION: Re-implement
 *                How an object is sized is application-specific. (Server Demo
 *                uses MoveWindow.)
 *
 */
OLESTATUS  APIENTRY ObjSetBounds (LPOLEOBJECT lpoleobj, OLE_CONST RECT FAR * lprect)
{
   if (docMain.doctype == doctypeEmbedded)
   {
      RECT rect = *lprect;
      LPOBJ lpobj = (LPOBJ) lpoleobj;
      
      // the units are in HIMETRIC
      rect.right   = rect.right - rect.left;
      rect.bottom  = rect.top - rect.bottom;
      HiMetricToDevice ( (LPPOINT) &rect.right);
      MoveWindow (lpobj->hwnd, lpobj->native.nX, lpobj->native.nY, 
                  rect.right + 2 * GetSystemMetrics(SM_CXFRAME), 
                  rect.bottom + 2 * GetSystemMetrics(SM_CYFRAME), 
                  TRUE);
   }
   return OLE_OK;
}



/* ObjSetColorScheme                OBJECT "SetColorScheme" METHOD
 * -----------------
 *
 * The client calls this method to suggest a color scheme (palette) for
 * the server to use for the object.
 *
 * LPOLEOBJECT  lpoleobject       - The OLE object
 * OLE_CONST LOGPALETTE FAR * lppal             - Suggested palette
 *
 * RETURNS: OLE_ERROR_PALETTE if CreatePalette fails, 
 *          OLE_OK otherwise
 *
 * 
 * CUSTOMIZATION: If your application supports color schemes, then this 
 *                function is a good example of how to create and store
 *                a palette.
 *
 */
OLESTATUS  APIENTRY ObjSetColorScheme 
   (LPOLEOBJECT lpoleobject, OLE_CONST LOGPALETTE FAR *lppal)
{
   HPALETTE hpal = CreatePalette (lppal);
   LPOBJ lpobj   = (LPOBJ) lpoleobject;

   if (hpal==NULL)
      return OLE_ERROR_PALETTE;

   if (lpobj->hpal) 
      DeleteObject (lpobj->hpal);
   lpobj->hpal = hpal;
   return OLE_OK;
}



/* ObjSetData                OBJECT "SetData" METHOD
 * ----------
 *
 * This method is used to store data into the object in the specified
 * format.  This will be called with Native format after an embedded
 * object has been opened by the Edit method.
 *
 * LPOLEOBJECT lpoleobject      - The OLE object
 * WORD cfFormat                - Data type, i.e., clipboard format
 * HANDLE hdata                 - Handle to the data.
 * 
 * RETURNS:       OLE_OK if the data was stored properly
 *                OLE_ERROR_FORMAT if format was not cfNative.
 *                OLE_ERROR_MEMORY if memory could not be locked.
 * 
 * CUSTOMIZATION: The large then-clause will need to be re-implemented for
 *                your application.  You may wish to support additional
 *                formats besides cfNative.
 *
 */
OLESTATUS  APIENTRY ObjSetData 
   (LPOLEOBJECT lpoleobject, OLECLIPFORMAT cfFormat, HANDLE hdata)
{
    LPNATIVE lpnative;
    LPOBJ    lpobj;

    lpobj = (LPOBJ)lpoleobject;

    if (cfFormat != cfNative)
    {
      return OLE_ERROR_FORMAT;
    }

    lpnative = (LPNATIVE) GlobalLock (hdata);

    if (lpnative)
    {
        lpobj->native = *lpnative;
        if (lpobj->aName)
            GlobalDeleteAtom (lpobj->aName);
        lpobj->aName = GlobalAddAtom (lpnative->szName);
        // CreateNewObj made an "Object 1" but we may be changing its number.
        docMain.rgfObjNums[1] = FALSE;
        docMain.rgfObjNums [GetObjNum(lpobj)] = TRUE;

        MoveWindow (lpobj->hwnd, 0, 0,
//                    lpobj->native.nWidth + 2 * GetSystemMetrics(SM_CXFRAME), 
//                    lpobj->native.nHeight+ 2 * GetSystemMetrics(SM_CYFRAME),
                    lpobj->native.nWidth, 
                    lpobj->native.nHeight,

                    FALSE);
        GlobalUnlock (hdata);
    }
    // Server is responsible for deleting the data.
    GlobalFree(hdata);           
    return lpnative ? OLE_OK : OLE_ERROR_MEMORY;
}



/* ObjSetTargetDevice        OBJECT "SetTargetDevice" METHOD
 * -------------------
 *
 * This method is used to indicate the device type that an object
 * will be rendered on.  It is the server's responsibility to free hdata.
 *
 * LPOLEOBJECT lpoleobject - The OLE object
 * HANDLE hdata            - Handle to memory containing
 *                           a StdTargetDevice structure
 * 
 * RETURNS: OLE_OK
 * 
 * CUSTOMIZATION: Implement.  Server Demo currently does not do anything.
 *
 */
OLESTATUS  APIENTRY ObjSetTargetDevice (LPOLEOBJECT lpoleobject, HANDLE hdata)
{
    if (hdata == NULL)
    {
      // Rendering for the screen is requested.
    }
    else
    {
      LPSTR lpstd = (LPSTR) GlobalLock (hdata);
      // lpstd points to a StdTargetDevice structure.
      // Use it to do whatever is appropriate to generate the best results 
      // on the specified target device.
      GlobalUnlock (hdata);
      // Server is responsible for freeing the data.
      GlobalFree (hdata);  
    }
    return OLE_OK;
}



/* ObjShow                OBJECT "Show" METHOD
 * --------
 *
 * This method is used to display the object.  
 * The server application should be activated and brought to the top.
 * Also, in a REAL server application, the object should be scrolled
 * into view.  The object should be selected.
 *
 * LPOLEOBJECT lpoleobject - Pointer to the OLE object
 * BOOL fTakeFocus         - Should server window get the focus?
 * 
 * RETURNS:        OLE_OK
 *
 * 
 * CUSTOMIZATION: In your application, the document should be scrolled 
 *                to bring the object into view.  Server Demo brings the 
 *                object to the front, in case it is a linked object inside a 
 *                document with other objects obscuring it.
 *
 */
OLESTATUS  APIENTRY ObjShow (LPOLEOBJECT lpoleobject, BOOL fTakeFocus)
{
    LPOBJ lpobj;
    HWND hwndOldFocus;

    hwndOldFocus = GetFocus();
    lpobj = (LPOBJ) lpoleobject;
    
    if (fTakeFocus)
       SetForegroundWindow (lpobj->hwnd);

    ShowWindow(hwndMain, SW_SHOWNORMAL);

    SetFocus (fTakeFocus ? lpobj->hwnd : hwndOldFocus);
    return OLE_OK;
}



/* PaintObj
 * ---------
 *
 * This function is called by the WM_PAINT message to paint an object 
 * on the screen.  
 *
 * HWND hwnd - The object window in which to paint the object
 *
 * CUSTOMIZATION: Server Demo specific
 *
 */
VOID PaintObj (HWND hwnd)
{
    LPOBJ       lpobj;
    RECT        rc;
    HDC         hdc;
    PAINTSTRUCT paintstruct;

    BeginPaint (hwnd, &paintstruct);
    hdc = GetDC (hwnd);

    lpobj = HwndToLpobj (hwnd);
    GetClientRect (hwnd, (LPRECT) &rc);

    DrawObj (hdc, lpobj, rc, dctypeScreen);

    ReleaseDC (hwnd, hdc);
    EndPaint (hwnd, &paintstruct);
}



/* RevokeObj
 * ---------
 *
 * Call OleRevokeObject because the user has destroyed the object.
 *
 * LPOBJ lpobj - The object which has been destroyed
 *
 * 
 * CUSTOMIZATION: You will only need to call OleRevokeObject once if there
 *                is only one LPOLECLIENT in your OBJ structure, which there
 *                should be.
 *
 */
VOID RevokeObj (LPOBJ lpobj)
{
   INT i;

   for (i=0; i< clpoleclient; i++)
   {
      if (lpobj->lpoleclient[i])
         OleRevokeObject (lpobj->lpoleclient[i]);
      else 
         /* if lpobj->lpoleclient[i]==NULL then there are no more non-NULLs
            in the array. */
         break;
   }
}



/* SendObjMsg
 * ----------
 *
 * This function sends a message to a specific object.
 *
 * LPOBJ lpobj   - The object
 * WORD wMessage - The message to send
 * 
 * CUSTOMIZATION: You will only need to call CallBack once if there
 *                is only one LPOLECLIENT in your OBJ structure, which there
 *                should be.
 *
 */
VOID SendObjMsg (LPOBJ lpobj, WORD wMessage)
{
   INT i;
   for (i=0; i < clpoleclient; i++)
   {
      if (lpobj->lpoleclient[i])
      {
         // Call the object's Callback function.
         lpobj->lpoleclient[i]->lpvtbl->CallBack 
            (lpobj->lpoleclient[i], wMessage, (LPOLEOBJECT) lpobj);
      }
      else
         break;
   }
}



/* SizeObj
 * -------
 *
 * Change the size of an object.
 *
 * HWND hwnd  - The object's window
 * RECT rect  - The requested new size in device units
 * BOOL fMove - Should the object be moved? (or just resized?)
 *
 * CUSTOMIZATION: Server Demo specific
 *
 */
VOID SizeObj (HWND hwnd, RECT rect, BOOL fMove)
{
   LPOBJ lpobj;

   lpobj = HwndToLpobj (hwnd);
   if (fMove)
   {
      lpobj->native.nX   = rect.left;
      lpobj->native.nY   = rect.top;
   }
   lpobj->native.nWidth  = rect.right  - rect.left;
   lpobj->native.nHeight = rect.bottom - rect.top ;
   SetHiMetricFields (lpobj);
   InvalidateRect (hwnd, (LPRECT)NULL, TRUE);
   fDocChanged = TRUE;
   if (docMain.doctype == doctypeFromFile)
   {
      // If object is linked, update it in client now. 
      SendObjMsg (lpobj, OLE_CHANGED);
   }
}