/*
ddeoo.cpp
DDE Ole Object

copyright (c) 1992  Microsoft Corporation

Module Name:

    ddeoo.cpp

Abstract:

    This module contains the methods for DdeObject::OleObject

Author:

    Jason Fuller    (jasonful)  24-July-1992

*/

#include "ddeproxy.h"
#include <limits.h>

ASSERTDATA

//
// OleObject methods
//

STDUNKIMPL_FORDERIVED(DdeObject, OleObjectImpl)



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::SetClientSite
    (IOleClientSite FAR* pClientSite)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::SetClientSite(%x,pClientSite=%x)\n",
		  this,
		  pClientSite));

    ChkD (m_pDdeObject);

    if (m_pDdeObject->m_pOleClientSite)
        m_pDdeObject->m_pOleClientSite->Release();

    // we've decided to keep the pointer that's been passed to us. So we
    // must AddRef()
    if (m_pDdeObject->m_pOleClientSite = pClientSite)
        pClientSite->AddRef();

    // this pointer need not be sent to the server, because we will always
    // send our &m_MyDataSite as the client site
    return NOERROR;
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetClientSite
    (IOleClientSite FAR* FAR* ppClientSite)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetClientSite(%x)\n",
		  this));

    ChkD (m_pDdeObject);
    // we've been asked to give the pointer so we should AddRef()
    if (*ppClientSite = m_pDdeObject->m_pOleClientSite)
        m_pDdeObject->m_pOleClientSite->AddRef();
    return NOERROR;
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::EnumVerbs
    (IEnumOLEVERB FAR* FAR* ppenumOleVerb)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::EnumVerbs(%x)\n",
		  this));

    ChkD (m_pDdeObject);
    return ReportResult(0, OLE_S_USEREG, 0, 0);
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::Update
( void )
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::Update(%x)\n",
		  this));

    ChkD (m_pDdeObject);
    RetErr (m_pDdeObject->Update(TRUE) );
    RetErr (m_pDdeObject->Save (m_pDdeObject->m_pstg));
    return NOERROR;
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::IsUpToDate
( void )
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::IsUpToDate(%x)\n",
		  this));

    ChkD (m_pDdeObject);
    // There is no way to know if a 1.0 server has edited its embedded
    // object, so we assume it has, to be on the safe side.
    return ResultFromScode (m_pDdeObject->m_ulObjType==OT_EMBEDDED
                            ? S_FALSE : S_OK);
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetUserClassID
    (CLSID FAR* pClsid)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetUserClassID(%x)\n",
		  this));

    *pClsid = m_pDdeObject->m_clsid;
    return NOERROR;
}

STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetUserType
    (DWORD dwFormOfType,
    LPOLESTR * pszUserType)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetUserType(%x)\n",
		  this));

    return ReportResult (0, OLE_S_USEREG, 0, 0);
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::SetExtent
( DWORD dwAspect, LPSIZEL lpsizel)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::SetExtent(%x)\n",
		  this));

    HANDLE      hDdePoke;
    LPRECT16      lprc;

    ChkD (m_pDdeObject);
    Puts ("OleObject::SetExtent\n");
    if (!(dwAspect                              // at least one bit
          && !(dwAspect & (dwAspect-1))         // exactly one bit
          && (dwAspect & DVASPECT_CONTENT)))    // a bit we support
    {
        return ResultFromScode (DV_E_DVASPECT);
    }

#ifdef OLD
    m_pDdeObject->m_cxContentExtent = lpsizel->cx;
    m_pDdeObject->m_cyContentExtent = lpsizel->cy;
#endif

    if (!m_pDdeObject->m_pDocChannel)
    {
            return OLE_E_NOTRUNNING;
    }

    lprc = (LPRECT16) wAllocDdePokeBlock (sizeof(RECT16), g_cfBinary, &hDdePoke);
    lprc->left = lprc->right = (SHORT) min(INT_MAX,lpsizel->cx);
    lprc->top  = lprc->bottom= (SHORT) min(INT_MAX,lpsizel->cy);
    aStdDocDimensions = GlobalAddAtom (OLESTR("StdDocDimensions"));
    intrAssert(wIsValidAtom(aStdDocDimensions));
    GlobalUnlock (hDdePoke);
    return m_pDdeObject->Poke(aStdDocDimensions, hDdePoke);
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetExtent
( DWORD dwAspect, LPSIZEL lpsizel)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetExtent(%x)\n",
		  this));

    ChkD (m_pDdeObject);


#ifdef OLD
    VDATEPTROUT (lpsizel, SIZEL);
    if (!(dwAspect                              // at least one bit
          && !(dwAspect & (dwAspect-1))         // exactly one bit
          && !(dwAspect & (DVASPECT_CONTENT | DVASPECT_ICON)))) // a bit we support
    {
        return ResultFromScode (DV_E_DVASPECT);
    }

    if (dwAspect & DVASPECT_CONTENT)
    {
        lpsizel->cx = m_pDdeObject->m_cxContentExtent;
        lpsizel->cy = m_pDdeObject->m_cyContentExtent;
    }

    return NOERROR;
#endif
    return ResultFromScode(E_NOTIMPL);

}

//+---------------------------------------------------------------------------
//
//  Method:     CDdeObject::DoVerb
//
//  Synopsis:   Send the server a message asking it to do a verb.
//
//  Effects:	OLE1.0 servers only know how to do a couple of
//		verbs. Specifically, it will respond to PRIMARY,
//		HIDE, OPEN, and SHOW. All others return error
//
//
//  Arguments:  [iVerb] -- Verb number
//		[lpmsg] -- Window message (ignored)
//		[pActiveSite] -- ActiveSite (ignored)
//		[lindex] -- Index (ignored)
//		[hwndParent] -- (ignored)
//		[lprcPosRect] -- (ignored)
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Created
//
//  Notes:
//
//	ANSI ALERT!
//
//	The server is going to accept a command string from us. This string
//	needs to be done in ANSI, since we are going to pass it to old
//	servers. Therefore, the following code generates an ANSI string
//	The following are the supported verb strings
//
//	[StdShowItem("aItem",FALSE)]
// 	[StdDoVerbItem("aItem",verb,FALSE,FALSE)]
//
//----------------------------------------------------------------------------
STDMETHODIMP NC(CDdeObject,COleObjectImpl)::DoVerb
    (LONG iVerb, LPMSG lpmsg, LPOLECLIENTSITE pActiveSite,
     LONG lindex, HWND hwndParent, const RECT FAR* lprcPosRect)
{
    WORD    len;
    ULONG     size;
    LPSTR   lpdata = NULL;
    LPSTR   lpdataStart = NULL;
    HANDLE  hdata = NULL;
    BOOL    bShow;
    HRESULT hresult;

    ChkD (m_pDdeObject);

    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::DoVerb(%x,iVerb=%x,lindex=%x)\n",
		  this,iVerb,lindex));

    if (iVerb < OLEIVERB_HIDE)
    {
	intrDebugOut((DEB_ITRACE,
		      "CDdeObject::DoVerb(%x)Returning invalid verb\n",
		      this));
	return OLEOBJ_E_INVALIDVERB;
    }


    if (iVerb == OLEIVERB_HIDE)
    {
	intrDebugOut((DEB_ITRACE,"::DoVerb(%x) OLEIVERB_HIDE\n",this));

        if (m_pDdeObject->m_fVisible || OT_LINK==m_pDdeObject->m_ulObjType)
	{
	    intrDebugOut((DEB_ITRACE,
			  "::DoVerb(%x) CANNOT_DOVERB_NOW\n",this));
            return OLEOBJ_S_CANNOT_DOVERB_NOW;
	}

	intrDebugOut((DEB_ITRACE,"::DoVerb(%x) returns NOERROR\n",this));
	return NOERROR;
    }

    //
    // Calculate the number of bytes needed to pass the
    // execute command to the server.
    //
    if (bShow = (iVerb == OLEIVERB_SHOW
                 || iVerb == OLEIVERB_OPEN
                 || m_pDdeObject->m_bOldSvr))
    {
	//
        // [StdShowItem("aItem",FALSE)]
	//

        len = 23 + wAtomLenA (m_pDdeObject->m_aItem) + 1;

    }
    else
    {
        // [StdDoVerbItem("aItem",verb,FALSE,FALSE)]
        len = 32 + 10 /* for verb */ + wAtomLenA (m_pDdeObject->m_aItem) + 1;

    }

    if (!(hdata = GlobalAlloc (GMEM_DDESHARE, size = len)))
    {
	intrDebugOut((DEB_ITRACE,
		      "::DoVerb(%x) cannot alloc %x bytes\n",
		      this,size));
        return ReportResult(0, E_OUTOFMEMORY, 0, 0);
    }


    if (!(lpdata = (LPSTR)GlobalLock (hdata)))
    {
	intrDebugOut((DEB_ITRACE,
		      "::DoVerb(%x) cannot lock\n",
		      this));
        GlobalFree (hdata);
        return ReportResult(0, E_OUTOFMEMORY, 0, 0);
    }

    lpdataStart = lpdata;


    strcpy (lpdata, bShow ? "[StdShowItem(\"" : "[StdDoVerbItem(\"");
    len = strlen (lpdata);
    lpdata += len;

    // For links
    if (m_pDdeObject->m_aItem)
        lpdata += GlobalGetAtomNameA (m_pDdeObject->m_aItem, lpdata, size - len);

    if (!bShow) {
        wsprintfA (lpdata,"\",%lu,TRUE,FALSE)]", iVerb);
    } else {
        strcpy (lpdata, "\")]");
        // apps like excel and wingraph do not support activate at item level.
    }

    intrDebugOut((DEB_ITRACE,"::DoVerb(%x)lpdata(%s)\n",this,lpdataStart));

    Assert (strlen(lpdata) < size);

    GlobalUnlock (hdata);

    hresult = m_pDdeObject->Execute (m_pDdeObject->m_pDocChannel, hdata);

    if (NOERROR==hresult)
    {
        // Assume doing a verb makes the server visible.
        // This is not strictly true.
        m_pDdeObject->DeclareVisibility (TRUE);
    }
    else
    {
	intrDebugOut((DEB_ITRACE,
		      "::DoVerb(%x)Execute returned %x\n",
		      this,
		      hresult));
    }
    return hresult;
}





//+---------------------------------------------------------------------------
//
//  Method:     CDdeObject::SetHostNames
//
//  Synopsis:   Sets the host names
//
//  Effects:
//
//  Arguments:  [szContainerApp] -- Name of container app
//		[szContainerObj] -- Name of contained object
//
//  Requires:
//
//  Returns:
//
//  Signals:
//
//  Modifies:
//
//  Derivation:
//
//  Algorithm:
//
//  History:    5-12-94   kevinro   Created
//
//  Notes:
//	ANSI ALERT!
//
//	The server is going to accept a command string from us. This string
//	needs to be done in ANSI, since we are going to pass it to old
//	servers. Therefore, the following code generates an ANSI string
//
//----------------------------------------------------------------------------
STDMETHODIMP NC(CDdeObject,COleObjectImpl)::SetHostNames
(LPCOLESTR szContainerApp, LPCOLESTR szContainerObj)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::SetHostNames(%x,App=%ws,Obj=%ws)\n",
		  this,szContainerApp,szContainerObj));

    WORD        cbName;
    WORD        wSize;
    LPSTR        lpBuf;
    HANDLE      hDdePoke;

    ChkD (m_pDdeObject);

    VDATEPTRIN (szContainerApp, char);

    if (!m_pDdeObject->m_pDocChannel)
        return NOERROR;
    if (szContainerObj==NULL)
        szContainerObj=OLESTR("");

    if (szContainerApp[0]=='\0')
        szContainerApp = OLESTR("Container Application");

    //
    // The OLE 1.0 server is going to want ANSI strings.
    // convert the two that we have
    //

    char pszContainerApp[MAX_STR];
    char pszContainerObj[MAX_STR];

    if (WideCharToMultiByte(CP_ACP,
			    0,
			    szContainerApp,
			    -1,
			    pszContainerApp,
			    MAX_STR,
			    NULL,
			    NULL) == FALSE)
    {
	intrDebugOut((DEB_ERROR,
		      "::SetHostNames(%x) can't convert szContainerApp(%ws) err=%x\n",
		      this,szContainerApp,GetLastError()));

	//
	// Couldn't convert string
	//
	return(E_UNEXPECTED);
    }
    if (WideCharToMultiByte(CP_ACP,
			    0,
			    szContainerObj,
			    -1,
			    pszContainerObj,
			    MAX_STR,
			    NULL,
			    NULL) == FALSE)
    {
	intrDebugOut((DEB_ERROR,
		      "::SetHostNames(%x) can't convert szContainerObj(%ws) err=%x\n",
		      this,szContainerObj,GetLastError));

	//
	// Couldn't convert string
	//
	return(E_UNEXPECTED);
    }


    WORD cbObj;

    wSize = (cbName = strlen(pszContainerApp)+1)
            + (cbObj = strlen(pszContainerObj)+1)
            + 2 * sizeof(WORD);  // for the two offsets

    lpBuf = wAllocDdePokeBlock ((DWORD)wSize, g_cfBinary, &hDdePoke);
    ((WORD FAR*)lpBuf)[0] = 0;
    ((WORD FAR*)lpBuf)[1] = cbName;
    lpBuf += 2*sizeof(WORD);
    memcpy (lpBuf,pszContainerApp,cbName);
    memcpy (lpBuf+cbName, pszContainerObj,cbObj);
    GlobalUnlock (hDdePoke);
    aStdHostNames = GlobalAddAtom (OLESTR("StdHostNames"));
    intrAssert(wIsValidAtom(aStdHostNames));
    m_pDdeObject->Poke(aStdHostNames, hDdePoke);
    return NOERROR;
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::Close
    (DWORD dwSaveOption)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::Close(%x,dwSaveOption=%x)\n",
		  this,dwSaveOption));


    ChkDR (m_pDdeObject);

    HRESULT hresult;
    if (m_pDdeObject->m_fDidSendOnClose)
        return ResultFromScode (RPC_E_CANTCALLOUT_INASYNCCALL);

    if (((OLECLOSE_SAVEIFDIRTY  == dwSaveOption) ||
         (OLECLOSE_PROMPTSAVE==dwSaveOption)) &&
         (m_pDdeObject->m_clsid != CLSID_Package))
    {
        // Packager gives truncated native data (header info with no
        // actual embedded file) if you DDE_REQUEST it. Bug 3103
        Update(); // IOleObject::Update
        m_pDdeObject->OleCallBack (ON_SAVE,NULL);
    }
    RetZ (m_pDdeObject->m_pDocChannel);
    hresult=m_pDdeObject->Execute (m_pDdeObject->m_pDocChannel,
                       wNewHandle ((LPSTR)&achStdCloseDocument,sizeof(achStdCloseDocument)),
                       TRUE);
    if (NOERROR==hresult)
        m_pDdeObject->m_fDidStdCloseDoc = TRUE;

    // Client sends StdCloseDocument. Srvr sends ACK. Srvr may or may not
    // send Terminate.  We may interpret the TERMINATE the server sends
    // as the reply to ours even if he posted first. But since some servers
    // post first and others wait for the client, this is what we need to do.

    BOOL fVisible = m_pDdeObject->m_fWasEverVisible; // TermConv clears this flag
    m_pDdeObject->TermConv (m_pDdeObject->m_pDocChannel);
    if (!fVisible)
        m_pDdeObject->MaybeUnlaunchApp();

    return hresult;
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::SetMoniker
    (DWORD dwWhichMoniker, LPMONIKER pmk)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::SetMoniker(%x,dwWhichMoniker=%x)\n",
		  this,dwWhichMoniker));

    ChkD (m_pDdeObject);
    Puts ("OleObject::SetMoniker\r\n");
    // we ignore this always
    return NOERROR;
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetMoniker
    (DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER FAR* ppmk)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetMoniker(%x,dwWhichMoniker=%x)\n",
		  this,dwWhichMoniker));

    ChkD (m_pDdeObject);
    if (m_pDdeObject->m_pOleClientSite)
        return m_pDdeObject->m_pOleClientSite->GetMoniker(dwAssign,
                dwWhichMoniker, ppmk);
    else {
        // no client site
        *ppmk = NULL;
        return ReportResult(0, E_UNSPEC, 0, 0);
    }
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::InitFromData
    (LPDATAOBJECT pDataObject, BOOL fCreation, DWORD dwReserved)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::InitFromData(%x)\n",
		  this));

    Puts ("OleObject::InitFromData\r\n");
    return ReportResult(0, E_NOTIMPL, 0, 0);
}


STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetClipboardData
    (DWORD dwReserved, LPDATAOBJECT FAR* ppDataObject)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetClipboardData(%x)\n",
		  this));

    Puts ("OleObject::GetClipboardData\r\n");
    return ReportResult(0, E_NOTIMPL, 0, 0);
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::Advise
    (IAdviseSink FAR* pAdvSink,
     DWORD FAR*       pdwConnection)
{
    HRESULT hres;
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::Advise(%x)\n",
		  this));

    ChkD (m_pDdeObject);
    Puts ("OleObject::Advise\n");
    // Esstablish a DDE advise connection.
    if (m_pDdeObject->m_ulObjType == OT_EMBEDDED
        && !m_pDdeObject->m_fDidAdvNative)
    {
        // Embedded case.
        // Always advise on Save and Close.
        if (hres = m_pDdeObject->AdviseOn (g_cfNative, ON_SAVE))
            return hres;
        if (hres = m_pDdeObject->AdviseOn (g_cfNative, ON_CLOSE))
            return hres;
        if (m_pDdeObject->m_clsid == CLSID_MSDraw)
        {
            // MSDraw has (another) bug.  If you do not do an Advise on
            // presentation, then File.Update does not work, and you
            // cannot close the app unless you answer "no" to the update
            // dialog.  This would happen when you "Display As Icon"
            // because ordinarily there is no need to advise on presentation.
            // The following "unnecessary" advise fixes this problem.
            if (hres = m_pDdeObject->AdviseOn (CF_METAFILEPICT, ON_SAVE))
                return hres;
            if (hres = m_pDdeObject->AdviseOn (CF_METAFILEPICT, ON_CLOSE))
                return hres;
        }
    }
    else {
        /* Linked case */
        if (hres = m_pDdeObject->AdviseOn (g_cfBinary, ON_RENAME))
            return hres;
    }
    RetZS (m_pDdeObject->m_pOleAdvHolder, E_OUTOFMEMORY);
    return m_pDdeObject->m_pOleAdvHolder->Advise (pAdvSink, pdwConnection);
}




STDMETHODIMP NC(CDdeObject,COleObjectImpl)::Unadvise
    (DWORD dwConnection)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::Unadvise(%x,dwConnection=%x)\n",
		  this,dwConnection));

    HRESULT hres;
    ChkD (m_pDdeObject);

    // Terminate the DDE advise connection
    if (m_pDdeObject->m_ulObjType == OT_EMBEDDED)
    {
        // Embedded case.
        if (hres = m_pDdeObject->UnAdviseOn (g_cfNative, ON_SAVE))
            return hres;
        if (hres = m_pDdeObject->UnAdviseOn (g_cfNative, ON_CLOSE))
            return hres;
    }
    else
    {
        /* Linked case */
        if (hres = m_pDdeObject->UnAdviseOn (g_cfBinary, ON_RENAME))
            return hres;
    }
    RetZS (m_pDdeObject->m_pOleAdvHolder, E_OUTOFMEMORY);
    return m_pDdeObject->m_pOleAdvHolder->Unadvise (dwConnection);
}




STDMETHODIMP NC(CDdeObject,COleObjectImpl)::EnumAdvise
    (THIS_ LPENUMSTATDATA FAR* ppenumAdvise)
{
    ChkD (m_pDdeObject);
    RetZS (m_pDdeObject->m_pOleAdvHolder, E_OUTOFMEMORY);
    return m_pDdeObject->m_pOleAdvHolder->EnumAdvise(ppenumAdvise);
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::GetMiscStatus
    (DWORD dwAspect,
    DWORD FAR* pdwStatus)
{
    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::GetMiscStatus(%x)\n",
		  this));

    VDATEPTRIN (pdwStatus, DWORD);
    *pdwStatus = 0L;
    return ResultFromScode (OLE_S_USEREG);
}



STDMETHODIMP NC(CDdeObject,COleObjectImpl)::SetColorScheme
    (LPLOGPALETTE lpLogpal)
{
    HANDLE          hDdePoke;
    LPLOGPALETTE    lptmpLogpal;

    intrDebugOut((DEB_ITRACE,
		  "CDdeObject::SetColorScheme(%x)\n",
		  this));

    ChkD (m_pDdeObject);

    if (!m_pDdeObject->m_pDocChannel)
        return NOERROR;

    aStdColorScheme = GlobalAddAtom (OLESTR("StdColorScheme"));
    intrAssert(wIsValidAtom(aStdColorScheme));

    DWORD dwSize = (lpLogpal->palNumEntries - 1) * sizeof(PALETTEENTRY)
                        + sizeof(LOGPALETTE);
    lptmpLogpal = (LPLOGPALETTE) wAllocDdePokeBlock (dwSize, g_cfBinary, &hDdePoke);
    memcpy(lptmpLogpal, lpLogpal, dwSize);
    GlobalUnlock(hDdePoke);

    return m_pDdeObject->Poke(aStdColorScheme, hDdePoke);
}



#ifdef _DEBUG
STDMETHODIMP_(void) NC(CDdeObject,CDebug)::Dump( IDebugStream FAR * pdbstm)
{
}

STDMETHODIMP_(BOOL) NC(CDdeObject,CDebug)::IsValid( BOOL fSuspicious )
{
    if( m_pDdeObject->m_refs > 0 && m_pDdeObject->m_chk == chkDdeObj )
        return TRUE;
    else
        return FALSE;
}
#endif
