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

    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 = GetMetafilePict(lpobj)) != NULL)
        {
            SetClipboardData(CF_METAFILEPICT, hData);
//            GlobalFree(hData);
        }

        if ((hData = GetBitmap(lpobj)) != NULL)
        {
            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 = NULL;
   }

   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)
   {
      SetWindowOrg (hdc, 0, 0);
      // Paint entire object into the given rectangle.
      SetWindowExt (hdc, rc.right, rc.bottom);
   }

   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)
                     ? GetStockObject (BLACK_PEN) : hpenOld);
   if (hpalOld)
   {
      SelectPalette (hdc,
                     (dctype == dctypeMetafile)
                        ? 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 (hwndMain, (LPPOINT) &rc.right);

    // Set the 1/10 of HIMETRIC units for the bitmap
    SetBitmapDimension (hbitmap, (int) (rc.right/10), (int) (rc.bottom/10));

    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 (hwndMain, (LPPOINT) &rc.right);

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



/* 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 FAR PASCAL __export ObjDoVerb
   (LPOLEOBJECT lpoleobject, WORD 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 FAR PASCAL __export ObjEnumFormats
   (LPOLEOBJECT lpoleobject, OLECLIPFORMAT cfFormat)
{
      if (cfFormat == 0)
        return cfNative;

      if (cfFormat == cfNative)
         return cfOwnerLink;

      if (cfFormat == cfOwnerLink)
         return CF_METAFILEPICT;

      if (cfFormat == CF_METAFILEPICT)
         return CF_BITMAP;

      if (cfFormat == CF_BITMAP)
         return cfObjectLink;

      if (cfFormat == cfObjectLink)
         return NULL;

      return NULL;
}



/* 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 FAR PASCAL __export ObjGetData
   (LPOLEOBJECT lpoleobject, WORD 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_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
 * LPSTR 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 FAR PASCAL __export ObjQueryProtocol
   (LPOLEOBJECT lpoleobject, LPSTR 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 FAR PASCAL __export 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
 * LPRECT lprect           - The new bounds
 *
 * RETURNS: OLE_OK
 *
 * CUSTOMIZATION: Re-implement
 *                How an object is sized is application-specific. (Server Demo
 *                uses MoveWindow.)
 *
 */
OLESTATUS FAR PASCAL __export ObjSetBounds (LPOLEOBJECT lpoleobj, LPRECT 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 (hwndMain, (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
 * LPLOGPALETTE 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 FAR PASCAL __export ObjSetColorScheme
   (LPOLEOBJECT lpoleobject, LPLOGPALETTE 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 FAR PASCAL __export 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),
                    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 FAR PASCAL __export 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 FAR PASCAL __export ObjShow (LPOLEOBJECT lpoleobject, BOOL fTakeFocus)
{
    LPOBJ lpobj;
    HWND hwndOldFocus;

    hwndOldFocus = GetFocus();
    lpobj = (LPOBJ) lpoleobject;

    ShowWindow(hwndMain, SW_SHOWNORMAL);

    BringWindowToTop (lpobj->hwnd);
    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);
   }
}
