/*
 * SITE.CPP
 * Document Object Site Object
 *
 * Copyright (c)1995-1996 Microsoft Corporation, All Rights Reserved
 */


#include "framer.h"


/*
 * CSite::CSite
 * CSite::~CSite
 *
 * Constructor Parameters:
 *  dwID            DWORD identifer for this site.
 *  hWnd            HWND of the window associated with the site
 *  pFR             PCFrame to the parent structure.
 */

CSite::CSite(DWORD dwID, HWND hWnd, PCFrame pFR)
    {
    m_cRef=0;
    m_dwID=dwID;
    m_hWnd=hWnd;
    m_pFR=pFR;

    m_fInitialized=0;
    m_pIStorage=NULL;

    m_pObj=NULL;
    
    m_pIOleObject=NULL;
    m_pIOleIPObject=NULL;
    m_pIOleDocView=NULL;

    m_pImpIOleClientSite=NULL;
    m_pImpIAdviseSink=NULL;
    m_pImpIOleIPSite=NULL;
    m_pImpIOleDocumentSite=NULL;

    m_fDocObj=FALSE;
    return;
    }


CSite::~CSite(void)
    {
    //Object pointers cleaned up in Close.

    //We delete our own interfaces since we control them
    DeleteInterfaceImp(m_pImpIOleDocumentSite);
    DeleteInterfaceImp(m_pImpIOleIPSite);
    DeleteInterfaceImp(m_pImpIAdviseSink);
    DeleteInterfaceImp(m_pImpIOleClientSite);
    return;
    }




/*
 * CSite::QueryInterface
 * CSite::AddRef
 * CSite::Release
 *
 * Purpose:
 *  IUnknown members for CSite object.
 */

STDMETHODIMP CSite::QueryInterface(REFIID riid, void **ppv)
    {
    *ppv=NULL;

    if (IID_IUnknown==riid)
        *ppv=this;

    if (IID_IOleClientSite==riid)
        *ppv=m_pImpIOleClientSite;

    if (IID_IAdviseSink==riid)
        *ppv=m_pImpIAdviseSink;

    if (IID_IOleWindow==riid || IID_IOleInPlaceSite==riid)
        *ppv=m_pImpIOleIPSite;

    if (IID_IOleDocumentSite==riid)
        *ppv=m_pImpIOleDocumentSite;

    if (NULL!=*ppv)
        {
        ((LPUNKNOWN)*ppv)->AddRef();
        return NOERROR;
        }

    return ResultFromScode(E_NOINTERFACE);
    }


STDMETHODIMP_(ULONG) CSite::AddRef(void)
    {
    return ++m_cRef;
    }

STDMETHODIMP_(ULONG) CSite::Release(void)
    {
    if (0!=--m_cRef)
        return m_cRef;

    delete this;
    return 0;
    }






/*
 * CSite::Create
 *
 * Purpose:
 *  Asks the site to instantiate an object given a filename.
 *  This goes through OleCreateFromFile and will either create
 *  an embedded object or a package (embedded) object.  When
 *  activated, this will either launch the app and activate
 *  as a DocObject, launch the app and open the file, or fail
 *  because no app can open the file.
 *
 * Parameters:
 *  pszFile         LPTSTR of the file from which to create the object.
 *  pIStorage       IStorage * of the parent storage in which we're
 *                  to create an IStorage for the new object.
 *  dwID            DWORD identifier for this site.
 *
 * Return Value:
 *  BOOL            Result of the creation.
 */

BOOL CSite::Create(LPTSTR pszFile, IStorage *pIStorage)
    {
    HRESULT             hr=E_FAIL;
    LPUNKNOWN           pObj;

    //Create a new storage for this object (sets m_pIStorage)
    if (!CreateStorage(pIStorage))
        return FALSE;

    //Now create an object from the file
   #ifdef UNICODE    
    hr=OleCreateFromFile(CLSID_NULL, pszFile, IID_IUnknown
        , OLERENDER_NONE, NULL, NULL, m_pIStorage, (void **)&pObj);
   #else
    OLECHAR     szFile[512];    //Assumption on string length

    MultiByteToWideChar(CP_ACP, 0, pszFile, -1, szFile, 512);    
	hr=OleCreateFromFile(CLSID_NULL, szFile, IID_IUnknown
        , OLERENDER_NONE, NULL, NULL, m_pIStorage, (void **)&pObj);
   #endif

    //If creation didn't work, get rid of the element Open created.
    if (FAILED(hr))
        {
        Destroy(pIStorage);
        return FALSE;
        }

    //We don't get the size if PatronObject data was seen already.
    if (!ObjectInitialize(pObj))
        {
        Destroy(pIStorage);
        return FALSE;
        }

    m_fInitialized=TRUE;
    return TRUE;
    }






/*
 * CSite::ObjectInitialize
 * (Protected)
 *
 * Purpose:
 *  Performs operations necessary after creating an object or
 *  reloading one from storage.
 *
 * Parameters:
 *  pObj            LPUNKNOWN of the object in this tenant.
 *  pFE             LPFORMATETC describing the graphic here.
 *  dwData          DWORD extra data.  If pFE->dwAspect==
 *                  DVASPECT_ICON then this is the iconic metafile.
 *
 * Return Value:
 *  BOOL            TRUE if the function succeeded, FALSE otherwise.
 */

BOOL CSite::ObjectInitialize(LPUNKNOWN pObj)
    {
    HRESULT         hr;
    DWORD           dw;

    if (NULL==pObj)
        return FALSE;

    m_pObj=pObj;

    //We need an IOleObject most of the time, so get one here.
    m_pIOleObject=NULL;
    hr=pObj->QueryInterface(IID_IOleObject, (void **)&m_pIOleObject);         

    /*
     * Follow up object creation with advises and so forth.  If
     * we cannot get IOleObject here, then we know we can't do
     * any IOleObject actions from here on--object is static.
     */
    if (FAILED(hr))
        return TRUE;

    //SetClientSite is critical for DocObjects
    m_pIOleObject->SetClientSite(m_pImpIOleClientSite);
    m_pIOleObject->Advise(m_pImpIAdviseSink, &dw);

	//This is to give PowerPoint a chance to initialize itself earlier
	OleRun(m_pIOleObject);
    return TRUE;
    }




/*
 * CSite::CreateStorage
 *
 * Purpose:
 *  Creates an sub-storage within a given parent storage,
 *  setting m_pIStorage.
 *
 * Parameters:
 *  pIStorage       IStorage * of the parent storage
 *
 * Return Value:
 *  BOOL            TRUE if creation succeeds, FALSE otherwise.
 */

BOOL CSite::CreateStorage(IStorage *pIStorage)
    {
    HRESULT     hr;
    DWORD       dwMode=STGM_TRANSACTED | STGM_READWRITE
                    | STGM_SHARE_EXCLUSIVE;
    TCHAR       szName[32];

    if (NULL==pIStorage)
        return FALSE;

    /*
     * Attempt to open the storage under this ID.  If there is
     * none, then create it.  In either case we end up with an
     * IStorage that we either save in pPage or release.
     */
    wsprintf(szName, TEXT("Site %lu"), m_dwID);

   #ifdef UNICODE
    hr=pIStorage->CreateStorage(szName, dwMode, 0, 0, &m_pIStorage);
   #else
    OLECHAR  szwName[32];
	MultiByteToWideChar(CP_ACP, 0, szName, -1, szwName, 32);    
    hr=pIStorage->CreateStorage(szwName, dwMode, 0, 0, &m_pIStorage);
   #endif

    if (FAILED(hr))
        return FALSE;

	//Create stream for the object; name is irrelevant
	dwMode=STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
   #ifdef UNICODE
    hr=m_pIStorage->CreateStream(TEXT("\003DocObjInfo"), dwMode, 0, 0
        , &m_pIStream);
   #else    
	MultiByteToWideChar(CP_ACP, 0, "\003DocObjInfo", -1, szwName, 32);    
    hr=m_pIStorage->CreateStream(szwName, dwMode, 0, 0, &m_pIStream);
   #endif
   
    //If we failed to create a stream in the file, do it in memory
	if (FAILED(hr))
		{
		if (FAILED(CreateStreamOnHGlobal(NULL, TRUE, &m_pIStream)))
		    return FALSE;
		}

    //Create interface implementations
    m_pImpIOleClientSite=new CImpIOleClientSite(this, this);
    m_pImpIAdviseSink=new CImpIAdviseSink(this, this);
    m_pImpIOleIPSite=new CImpIOleInPlaceSite(this, this);
    m_pImpIOleDocumentSite=new CImpIOleDocumentSite(this, this);

    if (NULL==m_pImpIOleClientSite || NULL==m_pImpIAdviseSink
        || NULL==m_pImpIOleIPSite || NULL==m_pImpIOleDocumentSite)
        return FALSE;

    return TRUE;
    }




/*
 * CSite::Close
 *
 * Purpose:
 *  Possibly commits the storage, then releases it, afterwards
 *  frees alls the object pointers.
 *
 * Parameters:
 *  fCommit         BOOL indicating if we're to commit.
 *
 * Return Value:
 *  None
 */

void CSite::Close(BOOL fCommit)
    {
    //OnInPlaceDeactivate releases this pointer.
    if (NULL!=m_pIOleIPObject)
        m_pIOleIPObject->InPlaceDeactivate();

    ReleaseInterface(m_pIOleDocView);

    if (NULL!=m_pIOleObject)
        {
        m_pIOleObject->Close(fCommit
            ? OLECLOSE_SAVEIFDIRTY : OLECLOSE_NOSAVE);
        ReleaseInterface(m_pIOleObject);
        }

    ReleaseInterface(m_pObj);
	ReleaseInterface(m_pIStream);
    ReleaseInterface(m_pIStorage);

    return;
    }




/*
 * CSite::Update
 *
 * Purpose:
 *  Forces a commit on the object's storage
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  None
 */

void CSite::Update(void)
    {
    LPPERSISTSTORAGE    pIPS;

    if (NULL!=m_pIStorage)
        return;

    m_pObj->QueryInterface(IID_IPersistStorage, (void **)&pIPS);
    OleSave(pIPS, m_pIStorage, TRUE);
    pIPS->SaveCompleted(NULL);
    pIPS->Release();

    m_pIStorage->Commit(STGC_DEFAULT);
    return;
    }





/*
 * CSite::Destroy
 *
 * Purpose:
 *  Removes this storage from the parent storage.  The caller should
 *  eventually delete this CSite object to free the object herein.
 *  Nothing is committed when being destroyed.
 *
 * Parameters:
 *  pIStorage       IStorage * of the parent
 *
 * Return Value:
 *  None
 */

void CSite::Destroy(IStorage *pIStorage)
    {
    TCHAR     szName[32];

    if (NULL==pIStorage)
        return;

    if (NULL!=m_pObj)
        Close(FALSE);
    
	wsprintf(szName, TEXT("Site %lu"), m_dwID);
   #ifdef UNICODE
    pIStorage->DestroyElement(szName);
   #else
    OLECHAR  szwName[32];
	MultiByteToWideChar(CP_ACP, 0, szName, -1, szwName, 512);    
	pIStorage->DestroyElement(szwName);    
   #endif
    return;
    }





/*
 * CSite::Activate
 *
 * Purpose:
 *  Activates a verb on the object living in the site.
 *
 * Parameters:
 *  iVerb           LONG of the verb to execute.
 *
 * Return Value:
 *  None
 */

void CSite::Activate(LONG iVerb)
    {
    CHourglass *pHour;
    RECT        rc;
            
    pHour=new CHourglass;

    GetClientRect(m_hWnd, &rc);
    m_pIOleObject->DoVerb(iVerb, NULL, m_pImpIOleClientSite, 0
        , m_hWnd, &rc);

    delete pHour;
    return;
    }




/*
 * CSite::UpdateObjectRects
 *
 * Purpose:
 *  Informs the site that the client area window was resized and
 *  that the site needs to also tell the DocObject of the resize.
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  None
 */

void CSite::UpdateObjectRects(void)
    {
    RECT    rc;

    if (NULL==m_pIOleDocView)
        return;

    GetClientRect(m_hWnd, &rc);
    m_pIOleDocView->SetRect(&rc);
    return;
    }
