TENANT.CPP
/* 
 * TENANT.CPP 
 * Patron Chapter 13 
 * 
 * Implementation of the CTentant class which holds information 
 * for a single object on a page.  It maintains position, references 
 * to data, and a storage. 
 * 
 * Copyright (c)1993-1997 Microsoft Corporation, All Rights Reserved 
 * 
 * Kraig Brockschmidt, Software Design Engineer 
 * Microsoft Systems Developer Relations 
 * 
 * Internet  :  kraigb@microsoft.com 
 * Compuserve:  >INTERNET:kraigb@microsoft.com 
 */ 
 
 
#include "patron.h" 
 
 
/* 
 * CTenant::CTenant 
 * CTenant::~CTenant 
 * 
 * Constructor Parameters: 
 *  dwID            DWORD identifier for this page. 
 *  hWnd            HWND of the pages window. 
 *  pPG             PCPages to the parent structure. 
 */ 
 
CTenant::CTenant(DWORD dwID, HWND hWnd, PCPages pPG) 
    { 
    m_hWnd=hWnd; 
    m_dwID=dwID; 
 
    m_fInitialized=0; 
    m_pIStorage=NULL; 
    m_cOpens=0; 
 
    m_pObj=NULL; 
    m_pPG =pPG; 
    m_clsID=CLSID_NULL; 
    m_fSetExtent=FALSE; 
 
    m_cRef=0; 
    m_pIOleObject=NULL; 
    m_pIViewObject2=NULL; 
 
    m_pIOleClientSite=NULL; 
    m_pIAdviseSink=NULL; 
 
    m_pmkFile=NULL; 
    m_fLinkAvail=TRUE;          //Checked on FLoad 
 
    //CHAPTER13MOD 
    m_pmk=NULL; 
    //End CHAPTER13MOD 
    return; 
    } 
 
 
CTenant::~CTenant(void) 
    { 
    //CHAPTER13MOD 
    if (NULL!=m_pmk) 
        m_pmk->Release(); 
    //End CHAPTER13MOD 
 
    if (NULL!=m_pmkFile) 
        m_pmkFile->Release(); 
 
    //Object pointers cleaned up in Close. 
 
    //We delete our own interfaces since we control them 
    if (NULL!=m_pIAdviseSink) 
        delete m_pIAdviseSink; 
 
    if (NULL!=m_pIOleClientSite) 
        delete m_pIOleClientSite; 
 
    return; 
    } 
 
 
 
 
/* 
 * CTenant::QueryInterface 
 * CTenant::AddRef 
 * CTenant::Release 
 * 
 * Purpose: 
 *  IUnknown members for CTenant object. 
 */ 
 
STDMETHODIMP CTenant::QueryInterface(REFIID riid, PPVOID ppv) 
    { 
    *ppv=NULL; 
 
    if (IID_IUnknown==riid) 
        *ppv=this; 
 
    if (IID_IOleClientSite==riid) 
        *ppv=m_pIOleClientSite; 
 
    if (IID_IAdviseSink2==riid || IID_IAdviseSink==riid) 
        *ppv=m_pIAdviseSink; 
 
    if (NULL!=*ppv) 
        { 
        ((LPUNKNOWN)*ppv)->AddRef(); 
        return NOERROR; 
        } 
 
    return E_NOINTERFACE; 
    } 
 
 
STDMETHODIMP_(ULONG) CTenant::AddRef(void) 
    { 
    return ++m_cRef; 
    } 
 
STDMETHODIMP_(ULONG) CTenant::Release(void) 
    { 
    ULONG           cRefT; 
 
    cRefT=--m_cRef; 
 
    if (0L==m_cRef) 
        delete this; 
 
    return cRefT; 
    } 
 
 
 
 
 
 
/* 
 * CTenant::GetID 
 * 
 * Return Value: 
 *  DWORD           dwID field in this tenant.  This function is 
 *                  only here to avoid hiding inline implementations 
 *                  in tenant.h. 
 */ 
 
DWORD CTenant::GetID(void) 
    { 
    return m_dwID; 
    } 
 
 
 
/* 
 * CTenant::GetStorageName 
 * 
 * Parameters: 
 *  pszName         LPTSTR to a buffer in which to store the storage 
 *                  name for this tenant. 
 * 
 * Return Value: 
 *  UINT            Number of characters stored. 
 */ 
 
UINT CTenant::GetStorageName(LPTSTR pszName) 
    { 
    return wsprintf(pszName, TEXT("Tenant %lu"), m_dwID); 
    } 
 
 
 
 
 
/* 
 * CTenant::UCreate 
 * 
 * Purpose: 
 *  Creates a new tenant of the given CLSID, which can be either a 
 *  static bitmap or metafile or any compound document object. 
 * 
 * Parameters: 
 *  tType           TENANTTYPE to create, either a static metafile, 
 *                  bitmap, or some kind of compound document object 
 *                  This determines which OleCreate* call we use. 
 *  pvType          LPVOID providing the relevant pointer from which 
 *                  to create the tenant, depending on iType. 
 *  pFE             LPFORMATETC specifying the type of renderings 
 *                  to use. 
 *  pptl            LPPOINTL in which we store offset coordinates. 
 *  pszl            LPSIZEL where this object should store its 
 *                  lometric extents. 
 *  pIStorage       LPSTORAGE of the page we live in.  We have to 
 *                  create another storage in this for the tenant. 
 *  ppo             PPATRONOBJECT containing placement data. 
 *  dwData          DWORD with extra data, sensitive to iType. 
 * 
 * Return Value: 
 *  UINT            A UCREATE_* value depending on what we 
 *                  actually do. 
 */ 
 
UINT CTenant::UCreate(TENANTTYPE tType, LPVOID pvType 
    , LPFORMATETC pFE, LPPOINTL pptl, LPSIZEL pszl 
    , LPSTORAGE pIStorage, PPATRONOBJECT ppo, DWORD dwData) 
    { 
    HRESULT             hr; 
    LPUNKNOWN           pObj; 
    UINT                uRet=UCREATE_GRAPHICONLY; 
 
    if (NULL==pvType || NULL==pIStorage) 
        return UCREATE_FAILED; 
 
    //Fail if this is called for an already living tenant. 
    if (m_fInitialized) 
        return UCREATE_FAILED; 
 
    m_fInitialized=TRUE; 
 
    //Create a new storage for this tenant. 
    if (!FOpen(pIStorage)) 
        return UCREATE_FAILED; 
 
    /* 
     * Get the placement info if it's here.  We either have a non- 
     * NULL PPATRONOBJECT in ppo or we have to use default 
     * placement and retrieve the size from the object itself. 
     */ 
    pszl->cx=0; 
    pszl->cy=0; 
 
    if (NULL!=ppo) 
        { 
        *pFE=ppo->fe; 
        *pptl=ppo->ptl; 
        *pszl=ppo->szl;     //Could be 0,0 , so we ask object 
 
        uRet=UCREATE_PLACEDOBJECT; 
        } 
 
    hr=E_FAIL; 
 
    //Now create an object based specifically for the type. 
    switch (tType) 
        { 
        case TENANTTYPE_NULL: 
            break; 
 
        case TENANTTYPE_STATIC: 
            /* 
             * We could use OleCreateStaticFromData here which does 
             * pretty much what we're doing below.  However, it does 
             * not allow us to control whether we paste a bitmap or 
             * a metafile--it uses metafile first, bitmap second. 
             * For this reason we'll use code developed in Chapter 
             * 6's FreeLoader to affect the paste. 
             */ 
            hr=CreateStatic((LPDATAOBJECT)pvType, pFE, &pObj); 
            break; 
 
        case TENANTTYPE_EMBEDDEDOBJECT: 
            hr=OleCreate(*((LPCLSID)pvType), IID_IUnknown 
                , OLERENDER_DRAW, NULL, NULL, m_pIStorage 
                , (PPVOID)&pObj); 
            break; 
 
        case TENANTTYPE_EMBEDDEDFILE: 
#if defined(WIN32) && !defined(UNICODE) 
            OLECHAR pwcsFile[MAX_PATH]; 
            mbstowcs(pwcsFile, (LPTSTR)pvType, MAX_PATH); 
            hr=OleCreateFromFile(CLSID_NULL, pwcsFile 
                , IID_IUnknown, OLERENDER_DRAW, NULL, NULL 
                , m_pIStorage, (PPVOID)&pObj); 
#else 
            hr=OleCreateFromFile(CLSID_NULL, (LPTSTR)pvType 
                , IID_IUnknown, OLERENDER_DRAW, NULL, NULL 
                , m_pIStorage, (PPVOID)&pObj); 
#endif 
            break; 
 
        case TENANTTYPE_EMBEDDEDOBJECTFROMDATA: 
            hr=OleCreateFromData((LPDATAOBJECT)pvType, IID_IUnknown 
                , OLERENDER_DRAW, NULL, NULL, m_pIStorage 
                , (PPVOID)&pObj); 
            break; 
 
        case TENANTTYPE_LINKEDFILE: 
#if defined(WIN32) && !defined(UNICODE) 
            OLECHAR pwcsType[256]; 
            mbstowcs(pwcsType, (LPTSTR)pvType, 256); 
            hr=OleCreateLinkToFile(pwcsType, IID_IUnknown 
                , OLERENDER_DRAW, NULL, NULL, m_pIStorage 
                , (PPVOID)&pObj); 
#else 
            hr=OleCreateLinkToFile((LPTSTR)pvType, IID_IUnknown 
                , OLERENDER_DRAW, NULL, NULL, m_pIStorage 
                , (PPVOID)&pObj); 
#endif 
            break; 
 
        case TENANTTYPE_LINKEDOBJECTFROMDATA: 
            hr=OleCreateLinkFromData((LPDATAOBJECT)pvType 
                , IID_IUnknown, OLERENDER_DRAW, NULL, NULL 
                , m_pIStorage, (PPVOID)&pObj); 
            break; 
 
        default: 
            break; 
        } 
 
    //If creation didn't work, get rid of the element FOpen created. 
    if (FAILED(hr)) 
        { 
        Destroy(pIStorage); 
        return UCREATE_FAILED; 
        } 
 
    //We don't get the size if PatronObject data was seen already. 
    if (!FObjectInitialize(pObj, pFE, dwData)) 
        { 
        Destroy(pIStorage); 
        return UCREATE_FAILED; 
        } 
 
    if (0==pszl->cx && 0==pszl->cy) 
        { 
        SIZEL   szl; 
 
        //Try to get the real size of the object, default to 2"*2" 
        SETSIZEL((*pszl), 2*LOMETRIC_PER_INCH, 2*LOMETRIC_PER_INCH); 
        hr=E_FAIL; 
 
        //Try IViewObject2 first, then IOleObject as a backup. 
        if (NULL!=m_pIViewObject2) 
            { 
            hr=m_pIViewObject2->GetExtent(m_fe.dwAspect, -1, NULL 
                , &szl); 
            } 
        else 
            { 
            if (NULL!=m_pIOleObject) 
                hr=m_pIOleObject->GetExtent(m_fe.dwAspect, &szl); 
            } 
 
        if (SUCCEEDED(hr)) 
            { 
            //Convert HIMETRIC to our LOMETRIC mapping 
            SETSIZEL((*pszl), szl.cx/10, szl.cy/10); 
            } 
        } 
 
    return uRet; 
    } 
 
 
 
 
 
 
/* 
 * CTenant::FLoad 
 * 
 * Purpose: 
 *  Recreates the object living in this tenant in place of calling 
 *  FCreate.  This is used in loading as opposed to new creation. 
 * 
 * Parameters: 
 *  pIStorage       LPSTORAGE of the page we live in. 
 *  pti             PTENTANTINFO containing persistent information. 
 *                  The ID value in this structure is ignored. 
 * 
 * Return Value: 
 *  BOOL            TRUE if successful, FALSE otherwise. 
 */ 
 
BOOL CTenant::FLoad(LPSTORAGE pIStorage, PTENANTINFO pti) 
    { 
    HRESULT         hr; 
    LPUNKNOWN       pObj; 
 
    if (NULL==pIStorage || NULL==pti) 
        return FALSE; 
 
    //Fail if this is called for an already living tenant. 
    if (m_fInitialized) 
        return FALSE; 
 
    m_fInitialized=TRUE; 
 
    //Open the storage for this tenant. 
    if (!FOpen(pIStorage)) 
        return FALSE; 
 
    /* 
     * NOTE:  If you do not pass an IOleClientSite to OleLoad 
     * it will not automatically reconnect a linked object to 
     * the running source via IOleLink::BindIfRunning.  Since 
     * we do not pass m_pIOleClientSite here, we'll call 
     * BindIfRunning ourselves in FObjectInitialize. 
     */ 
    hr=OleLoad(m_pIStorage, IID_IUnknown, NULL, (PPVOID)&pObj); 
 
    if (FAILED(hr)) 
        { 
        Destroy(pIStorage); 
        return FALSE; 
        } 
 
    m_fSetExtent=pti->fSetExtent; 
    FObjectInitialize(pObj, &pti->fe, NULL); 
 
    RectSet(&pti->rcl, FALSE, FALSE); 
    return TRUE; 
    } 
 
 
 
 
/* 
 * CTenant::GetInfo 
 * 
 * Purpose: 
 *  Retrieved a TENANTINFO structure for this tenant. 
 * 
 * Parameters: 
 *  pti             PTENANTINFO structure to fill 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::GetInfo(PTENANTINFO pti) 
    { 
    if (NULL!=pti) 
        { 
        pti->dwID=m_dwID; 
        pti->rcl=m_rcl; 
        pti->fe=m_fe; 
        pti->fSetExtent=m_fSetExtent; 
        } 
 
    return; 
    } 
 
 
 
 
/* 
 * CTenant::FObjectInitialize 
 * (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 CTenant::FObjectInitialize(LPUNKNOWN pObj, LPFORMATETC pFE 
    , DWORD dwData) 
    { 
    HRESULT         hr; 
    LPPERSIST       pIPersist=NULL; 
    DWORD           dw; 
    PCDocument      pDoc; 
    TCHAR           szFile[CCHPATHMAX]; 
    LPOLELINK       pIOleLink=NULL; 
 
    if (NULL==pObj || NULL==pFE) 
        return FALSE; 
 
    m_pObj=pObj; 
    m_fe=*pFE; 
    m_fe.ptd=NULL; 
    m_dwState=TENANTSTATE_DEFAULT; 
 
    /* 
     * Determine the type:  Static or Embedded.  If Static, 
     * this will have CLSID_Picture_Metafile or CLSID_Picture_Dib. 
     * Otherwise it's embedded.  Later we'll add a case for links. 
     */ 
    m_tType=TENANTTYPE_EMBEDDEDOBJECT; 
 
    if (SUCCEEDED(pObj->QueryInterface(IID_IPersist 
        , (PPVOID)&pIPersist))) 
        { 
        CLSID   clsid=CLSID_NULL; 
 
        hr=pIPersist->GetClassID(&clsid); 
 
        //If we don't have a CLSID, default to static 
        if (FAILED(hr) || CLSID_Picture_Metafile==clsid 
            || CLSID_Picture_Dib==clsid) 
            m_tType=TENANTTYPE_STATIC; 
 
        pIPersist->Release(); 
        } 
 
    //Check if this is a linked object. 
    if (SUCCEEDED(pObj->QueryInterface(IID_IOleLink 
        , (PPVOID)&pIOleLink))) 
        { 
        LPMONIKER   pmk; 
 
        hr=pIOleLink->GetSourceMoniker(&pmk); 
 
        if (FAILED(hr) || NULL==pmk) 
            m_tType=TENANTTYPE_STATIC; 
        else 
            { 
            m_tType=TENANTTYPE_LINKEDOBJECT; 
            pmk->Release(); 
 
            //Connect to the object if the source is running. 
            pIOleLink->BindIfRunning(); 
            } 
 
        pIOleLink->Release(); 
        } 
 
    m_pIViewObject2=NULL; 
    hr=pObj->QueryInterface(IID_IViewObject2 
        , (PPVOID)&m_pIViewObject2); 
 
    if (FAILED(hr)) 
        return FALSE; 
 
    m_pIViewObject2->SetAdvise(m_fe.dwAspect, 0, m_pIAdviseSink); 
 
    //We need an IOleObject most of the time, so get one here. 
    m_pIOleObject=NULL; 
    hr=pObj->QueryInterface(IID_IOleObject 
         , (PPVOID)&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; 
 
    /* 
     * Get the MiscStatus bits and check for OLEMISC_ONLYICONIC. 
     * If set, force dwAspect in m_fe to DVASPECT_ICON so we 
     * remember to draw it properly and do extents right. 
     */ 
    m_pIOleObject->GetMiscStatus(m_fe.dwAspect, &m_grfMisc); 
 
    if (OLEMISC_ONLYICONIC & m_grfMisc) 
        m_fe.dwAspect=DVASPECT_ICON; 
 
    /* 
     * We could pass m_pIOleClientSite in an OleCreate* call, but 
     * since this function could be called after OleLoad, we still 
     * need to do this here, so it's always done here... 
     */ 
    m_pIOleObject->SetClientSite(m_pIOleClientSite); 
    m_pIOleObject->Advise(m_pIAdviseSink, &dw); 
 
    OleSetContainedObject(m_pIOleObject, TRUE); 
 
    /* 
     * For IOleObject::SetHostNames we need the application name 
     * and the document name (which is passed in the object 
     * parameter).  The design of Patron doesn't give us nice 
     * structured access to the name of the document we're in, so 
     * I grab the parent of the Pages window (the document) and 
     * send it DOCM_PDOCUMENT which returns us the pointer. 
     * Roundabout, but it works. 
     */ 
 
    pDoc=(PCDocument)SendMessage(GetParent(m_hWnd), DOCM_PDOCUMENT 
        , 0, 0L); 
 
    if (NULL!=pDoc) 
        pDoc->FilenameGet(szFile, CCHPATHMAX); 
    else 
        szFile[0]=0; 
 
    //CHAPTER13MOD 
    NotifyOfRename(szFile, NULL, NULL); 
    //End CHAPTER13MOD 
 
    /* 
     * This might have been Display as Icon (or the 
     * OLEMISC_ONLYICONIC flag was set) in which case 
     * m_fe.dwAspect=DVASPECT_ICON.  If this is coming from 
     * a Display As Icon choice, then then dwData is a handle 
     * to a metafile with the iconic aspect.  We take this and shove 
     * it into the cache for this aspect, releasing the content 
     * aspect.  OLESTD has a nice function that does this: 
     * OleStdSwitchDisplayAspect, which also handles a later case 
     * (Chapter 14) when we might want to switch BACK to content. 
     * That, however, requires the Change Type dialog... 
     * 
     * If the object is marked as OLEMISC_ONLYICONIC, then it 
     * is responsible for rendering the iconic image--not us. 
     */ 
 
    if ((DVASPECT_ICON & m_fe.dwAspect) && NULL!=dwData) 
        { 
        //Temps to give to OleStdSwitchDisplayAspect 
        DWORD       dw=DVASPECT_CONTENT; 
        BOOL        fUpdate; 
 
        OleStdSwitchDisplayAspect(m_pIOleObject, &dw, DVASPECT_ICON 
            , (HGLOBAL)(UINT)dwData, TRUE, FALSE, NULL, &fUpdate); 
        } 
 
    return TRUE; 
    } 
 
 
 
/* 
 * CTenant::FOpen 
 * 
 * Purpose: 
 *  Retrieves the IStorage associated with this tenant.  The 
 *  IStorage is owned by the tenant and thus the tenant always 
 *  holds a reference count. 
 * 
 *  If the storage is already open for this tenant, then this 
 *  function will AddRef it; therefore the caller must always 
 *  match an FOpen with a Close. 
 * 
 * Parameters: 
 *  pIStorage       LPSTORAGE above this tenant (which has its 
 *                  own storage). 
 * 
 * Return Value: 
 *  BOOL            TRUE if opening succeeds, FALSE otherwise. 
 */ 
 
BOOL CTenant::FOpen(LPSTORAGE pIStorage) 
    { 
    HRESULT     hr=NOERROR; 
    DWORD       dwMode=STGM_TRANSACTED | STGM_READWRITE 
                    | STGM_SHARE_EXCLUSIVE; 
    TCHAR       szTemp[32]; 
 
    if (NULL==m_pIStorage) 
        { 
        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. 
         */ 
 
        GetStorageName(szTemp); 
#if defined(WIN32) && !defined(UNICODE) 
        OLECHAR pwcsTemp[32]; 
        mbstowcs(pwcsTemp, szTemp, 32); 
#else 
        OLECHAR * pwcsTemp = szTemp; 
#endif 
        hr=pIStorage->OpenStorage(pwcsTemp, NULL, dwMode, NULL, 0 
            , &m_pIStorage); 
 
        if (FAILED(hr)) 
            { 
            hr=pIStorage->CreateStorage(pwcsTemp, dwMode, 0, 0 
                , &m_pIStorage); 
            } 
        } 
    else 
        m_pIStorage->AddRef(); 
 
    if (FAILED(hr)) 
        return FALSE; 
 
    m_cOpens++; 
 
    m_pIOleClientSite=new CImpIOleClientSite(this, this); 
    m_pIAdviseSink=new CImpIAdviseSink(this, this); 
 
    if (NULL==m_pIOleClientSite || NULL==m_pIAdviseSink) 
        return FALSE; 
 
    return TRUE; 
    } 
 
 
 
 
/* 
 * CTenant::Close 
 * 
 * Purpose: 
 *  Possibly commits the storage, then releases it reversing the 
 *  reference count from FOpen.  If the reference on the storage 
 *  goes to zero, the storage is forgotten.  However, the object we 
 *  contain is still held and as long as it's active the storage 
 *  remains alive. 
 * 
 * Parameters: 
 *  fCommit         BOOL indicating if we're to commit. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::Close(BOOL fCommit) 
    { 
    if (fCommit) 
        Update(); 
 
    if (NULL!=m_pIStorage) 
        { 
        m_pIStorage->Release(); 
 
        /* 
         * We can't use a zero reference count to know when to NULL 
         * this since other things might have AddRef'd the storage. 
         */ 
        if (0==--m_cOpens) 
            { 
            m_pIStorage=NULL; 
 
            //Close the object saving if necessary 
            if (NULL!=m_pIOleObject) 
                { 
                m_pIOleObject->Close(OLECLOSE_SAVEIFDIRTY); 
                m_pIOleObject->Release(); 
                m_pIOleObject=NULL; 
                } 
 
            //Release all other held pointers 
            if (NULL!=m_pIViewObject2) 
                { 
                m_pIViewObject2->SetAdvise(m_fe.dwAspect, 0, NULL); 
                m_pIViewObject2->Release(); 
                m_pIViewObject2=NULL; 
                } 
 
            if (NULL!=m_pObj) 
                { 
                //We know we only hold one ref from UCreate or FLoad 
                m_pObj->Release(); 
                m_pObj=NULL; 
                } 
            } 
        } 
 
    return; 
    } 
 
 
 
 
/* 
 * CTenant::Update 
 * 
 * Purpose: 
 *  Forces a common on the page if it's open. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  BOOL            TRUE if the object is open, FALSE otherwise. 
 */ 
 
BOOL CTenant::Update(void) 
    { 
    LPPERSISTSTORAGE    pIPS; 
 
    if (NULL!=m_pIStorage) 
        { 
        /* 
         * We need to OleSave again because we might have changed 
         * the size or position of this tenant.  We also need to 
         * save the rectangle on the page, since that's not known 
         * to OLE. 
         */ 
        m_pObj->QueryInterface(IID_IPersistStorage, (PPVOID)&pIPS); 
 
        //This fails for static objects...so we improvise if so 
        if (FAILED(OleSave(pIPS, m_pIStorage, TRUE))) 
            { 
            //This is essentially what OleSave does. 
            WriteClassStg(m_pIStorage, m_clsID); 
            pIPS->Save(m_pIStorage, TRUE); 
            } 
 
        pIPS->SaveCompleted(NULL); 
        pIPS->Release(); 
 
        m_pIStorage->Commit(STGC_ONLYIFCURRENT); 
        } 
 
    return FALSE; 
    } 
 
 
 
 
 
/* 
 * CTenant::Destroy 
 * 
 * Purpose: 
 *  Removes this page from the given storage.  The caller should 
 *  eventually delete this CTenant object to free the object herein. 
 *  Nothing is committed when being destroyed. 
 * 
 * Parameters: 
 *  pIStorage       LPSTORAGE contianing this page on which to call 
 *                  DestroyElement 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::Destroy(LPSTORAGE pIStorage) 
    { 
    TCHAR       szTemp[32]; 
 
    if (NULL!=pIStorage) 
        { 
        if (NULL!=m_pIOleObject) 
            m_pIOleObject->Close(OLECLOSE_NOSAVE); 
 
        if (NULL!=m_pIStorage) 
            { 
            //Remove all reference/open counts on this storage. 
            while (0!=m_cOpens) 
                { 
                m_pIStorage->Release(); 
                m_cOpens--; 
                } 
            } 
 
        GetStorageName(szTemp); 
#if defined(WIN32) && !defined(UNICODE) 
        OLECHAR pwcsTemp[32]; 
        mbstowcs(pwcsTemp, szTemp, 32); 
        pIStorage->DestroyElement(pwcsTemp); 
#else 
        pIStorage->DestroyElement(szTemp); 
#endif 
        m_pIStorage=NULL; 
        } 
 
    //m_pObj is released in destructor. 
    return; 
    } 
 
 
 
 
/* 
 * CTenant::Select 
 * 
 * Purpose: 
 *  Selects or deselects the tenant. 
 * 
 * Parameters: 
 *  fSelect         BOOL indicating the new state of the tenant. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::Select(BOOL fSelect) 
    { 
    BOOL        fWasSelected; 
    DWORD       dwState; 
    RECT        rc; 
    HDC         hDC; 
 
    fWasSelected=(BOOL)(TENANTSTATE_SELECTED & m_dwState); 
 
    //Nothing to do when there's no change. 
    if (fWasSelected==fSelect) 
        return; 
 
    dwState=m_dwState & ~TENANTSTATE_SELECTED; 
    m_dwState=dwState | ((fSelect) ? TENANTSTATE_SELECTED : 0); 
 
    /* 
     * Draw sizing handles to show the selection state.  We convert 
     * things to MM_TEXT since that's what this function expects. 
     */ 
 
    RECTFROMRECTL(rc, m_rcl); 
    RectConvertMappings(&rc, NULL, TRUE); 
    OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
 
    hDC=GetDC(m_hWnd); 
 
    OleUIDrawHandles(&rc, hDC, OLEUI_HANDLES_INSIDE 
        | OLEUI_HANDLES_NOBORDER| OLEUI_HANDLES_USEINVERSE 
        , CXYHANDLE, !fWasSelected); 
 
    ReleaseDC(m_hWnd, hDC); 
    return; 
    } 
 
 
 
 
/* 
 * CTenant::ShowAsOpen 
 * 
 * Purpose: 
 *  Draws or removes the hatch pattern over an object. 
 * 
 * Parameters: 
 *  fOpen           BOOL indicating the open state of this tenant. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::ShowAsOpen(BOOL fOpen) 
    { 
    BOOL        fWasOpen; 
    DWORD       dwState; 
    RECT        rc; 
    HDC         hDC; 
 
    fWasOpen=(BOOL)(TENANTSTATE_OPEN & m_dwState); 
 
    dwState=m_dwState & ~TENANTSTATE_OPEN; 
    m_dwState=dwState | ((fOpen) ? TENANTSTATE_OPEN : 0); 
 
    //If this was not open, then just hatch, otherwise repaint. 
    if (!fWasOpen && fOpen) 
        { 
        RECTFROMRECTL(rc, m_rcl); 
        RectConvertMappings(&rc, NULL, TRUE); 
        OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
 
        hDC=GetDC(m_hWnd); 
        OleUIDrawShading(&rc, hDC, OLEUI_SHADE_FULLRECT, 0); 
        ReleaseDC(m_hWnd, hDC); 
        } 
 
    if (fWasOpen && !fOpen) 
        Repaint(); 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::ShowYourself 
 * 
 * Purpose: 
 *  Function that really just implements IOleClientSite::ShowObject. 
 *  Here we first check if the tenant is fully visible, and if so, 
 *  then nothing needs to happen.  Otherwise, if the upper left 
 *  corner of the tenant is in the upper left visible quadrant of 
 *  the window, we'll also consider ourselves done.  Otherwise 
 *  we'll put the upper left corner of the object at the upper left 
 *  corner of the window. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::ShowYourself(void) 
    { 
    RECTL       rcl; 
    RECT        rc; 
    POINT       pt1, pt2; 
 
    //Scrolling deals in device units; get our rectangle in those. 
    RectGet(&rcl, TRUE); 
 
    //Get the window rectangle offset for the current scroll pos. 
    GetClientRect(m_hWnd, &rc); 
    OffsetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos); 
 
    //Check if the object is already visible. (macro in bookguid.h) 
    SETPOINT(pt1, (int)rcl.left,  (int)rcl.top); 
    SETPOINT(pt2, (int)rcl.right, (int)rcl.bottom); 
 
    if (PtInRect(&rc, pt1) && PtInRect(&rc, pt2)) 
        return; 
 
    //Check if the upper left is within the upper left quadrant 
    if (((int)rcl.left > rc.left 
        && (int)rcl.left < ((rc.right+rc.left)/2)) 
        && ((int)rcl.top > rc.top 
        && (int)rcl.top < ((rc.bottom+rc.top)/2))) 
        return; 
 
    //These are macros in INC\BOOK1632.H 
    SendScrollPosition(m_hWnd, WM_HSCROLL, rcl.left-8); 
    SendScrollPosition(m_hWnd, WM_VSCROLL, rcl.top-8); 
    return; 
    } 
 
 
 
/* 
 * CTenant::AddVerbMenu 
 * 
 * Purpose: 
 *  Creates the variable verb menu item for the object in this 
 *  tenant. 
 * 
 * Parmeters: 
 *  hMenu           HMENU on which to add items. 
 *  iPos            UINT position on that menu to add items. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::AddVerbMenu(HMENU hMenu, UINT iPos) 
    { 
    HMENU       hMenuTemp; 
    LPOLEOBJECT pObj=m_pIOleObject; 
 
    //If we're static, say we have no object. 
    if (TENANTTYPE_STATIC==m_tType) 
        pObj=NULL; 
 
    OleUIAddVerbMenu(pObj, NULL, hMenu, iPos, IDM_VERBMIN 
        , IDM_VERBMAX, FALSE, 0, &hMenuTemp); 
 
    return; 
    } 
 
 
 
 
 
 
/* 
 * CTenant::CopyEmbeddedObject 
 * 
 * Purpose: 
 *  Copies an embedded object to the given data object (via SetData, 
 *  assuming this is a data transfer object for clipboard/drag-drop) 
 *  if that's what we're holding. 
 * 
 * Parameters: 
 *  pIDataObject    LPDATAOBJECT in which to store the copy. 
 *  pFE             LPFORMATETC into which to copy CF_EMBEDDEDOBJECT 
 *                  if we put that in the data object. 
 *  pptl            LPPOINTL to the pick point (NULL outside of 
 *                  drag-drop); 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::CopyEmbeddedObject(LPDATAOBJECT pIDataObject 
    , LPFORMATETC pFE, LPPOINTL pptl) 
    { 
    LPPERSISTSTORAGE    pIPS; 
    STGMEDIUM           stm; 
    FORMATETC           fe; 
    HRESULT             hr; 
    UINT                cf; 
    POINTL              ptl; 
    SIZEL               szl; 
 
    //Can only copy embeddings. 
    if (TENANTTYPE_EMBEDDEDOBJECT!=m_tType || NULL==m_pIOleObject) 
        return; 
 
    if (NULL==pptl) 
        { 
        SETPOINTL(ptl, 0, 0); 
        pptl=&ptl; 
        } 
 
    /* 
     * Create CF_EMBEDDEDOBJECT.  This is simply an IStorage with a 
     * copy of the embedded object in it.  The not-so-simple part is 
     * getting an IStorage to stuff it in.  For this operation we'll 
     * use a temporary compound file. 
     */ 
 
    stm.pUnkForRelease=NULL; 
    stm.tymed=TYMED_ISTORAGE; 
    hr=StgCreateDocfile(NULL, STGM_TRANSACTED | STGM_READWRITE 
        | STGM_CREATE| STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE 
        , 0, &stm.pstg); 
 
    if (FAILED(hr)) 
        return; 
 
    m_pObj->QueryInterface(IID_IPersistStorage, (PPVOID)&pIPS); 
    if (NOERROR==pIPS->IsDirty()) 
        { 
        OleSave(pIPS, stm.pstg, FALSE); 
        pIPS->SaveCompleted(NULL); 
        } 
    else 
        m_pIStorage->CopyTo(0, NULL, NULL, stm.pstg); 
 
    pIPS->Release(); 
 
    //stm.pstg now has a copy, so stuff it away. 
    cf=RegisterClipboardFormat(CF_EMBEDDEDOBJECT); 
    SETDefFormatEtc(fe, cf, TYMED_ISTORAGE); 
 
    if (SUCCEEDED(pIDataObject->SetData(&fe, &stm, TRUE))) 
        *pFE=fe; 
    else 
        ReleaseStgMedium(&stm); 
 
    stm.tymed=TYMED_HGLOBAL; 
 
    /* 
     * You want to make sure that if this object is iconic, that you 
     * create the object descriptor with DVASPECT_ICON instead of 
     * the more typical DVASPECT_CONTENT.  Also remember that 
     * the pick point is in HIMETRIC. 
     */ 
    XformSizeInPixelsToHimetric(NULL, (LPSIZEL)pptl, (LPSIZEL)&ptl); 
 
    SETSIZEL(szl, (10*(m_rcl.right-m_rcl.left)) 
        , (10 * (m_rcl.bottom-m_rcl.top))); 
 
    stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject 
        (m_pIOleObject, NULL, m_fe.dwAspect, ptl, &szl); 
 
    cf=RegisterClipboardFormat(CF_OBJECTDESCRIPTOR); 
    SETDefFormatEtc(fe, cf, TYMED_HGLOBAL); 
 
    if (FAILED(pIDataObject->SetData(&fe, &stm, TRUE))) 
        ReleaseStgMedium(&stm); 
 
    return; 
    } 
 
 
 
 
//CHAPTER13MOD 
/* 
 * CTenant::CopyLinkedObject 
 * 
 * Purpose: 
 *  Copies an linked object to the given data object (via SetData, 
 *  assuming this is a data transfer object for clipboard/drag-drop) 
 *  if that's what we're holding. 
 * 
 * Parameters: 
 *  pIDataObject    LPDATAOBJECT in which to store the copy. 
 *  pFE             LPFORMATETC into which to copy CF_LINKSOURCE 
 *                  if we put that in the data object. 
 *  pptl            LPPOINTL to the pick point (NULL outside of 
 *                  drag-drop); 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::CopyLinkedObject(LPDATAOBJECT pIDataObject 
    , LPFORMATETC pFE, LPPOINTL pptl) 
    { 
    STGMEDIUM           stm; 
    FORMATETC           fe; 
    HRESULT             hr; 
    UINT                cf; 
    POINTL              ptl; 
    LPMONIKER           pmk; 
    CLSID               clsID; 
    LPTSTR              psz=NULL; 
    SIZEL               szl; 
 
    //Can only copy links to embeddings from here. 
    if (TENANTTYPE_EMBEDDEDOBJECT!=m_tType || NULL==m_pIOleObject) 
        return; 
 
    //If we don't have a full moniker, no linking allowed 
    if (NULL==m_pmk) 
        return; 
 
    //If the object doesn't support this, return. 
    if (OLEMISC_CANTLINKINSIDE & m_grfMisc) 
        return; 
 
    if (NULL==pptl) 
        { 
        SETPOINTL(ptl, 0, 0); 
        pptl=&ptl; 
        } 
 
    /* 
     * We need to get CF_LINKSOURCE, but the server may not be 
     * running, in which case we just grab the moniker and CLSID 
     * for this object and call OleStdGetLinkSrcData. 
     */ 
 
    m_pIOleObject->GetUserClassID(&clsID); 
    hr=m_pIOleObject->GetMoniker(0, OLEWHICHMK_OBJFULL, &pmk); 
 
    if (FAILED(hr)) 
        return; 
 
    //Set these tymeds or brain-dead OleStdGetLinkSourceData fails. 
    stm.pUnkForRelease=NULL; 
    stm.tymed=TYMED_NULL; 
    cf=RegisterClipboardFormat(CF_LINKSOURCE); 
    SETDefFormatEtc(fe, cf, TYMED_ISTREAM); 
    hr=OleStdGetLinkSourceData(pmk, &clsID, &fe, &stm); 
 
    if (FAILED(hr)) 
        { 
        pmk->Release(); 
        return; 
        } 
 
    //Send it to the data object for transfer 
    if (SUCCEEDED(pIDataObject->SetData(&fe, &stm, TRUE))) 
        *pFE=fe; 
    else 
        ReleaseStgMedium(&stm); 
 
    XformSizeInPixelsToHimetric(NULL, (LPSIZEL)pptl, (LPSIZEL)&ptl); 
 
    SETSIZEL(szl, (10*(m_rcl.right-m_rcl.left)) 
        , (10 * (m_rcl.bottom-m_rcl.top))); 
 
    stm.hGlobal=OleStdGetObjectDescriptorDataFromOleObject 
        (m_pIOleObject, NULL, m_fe.dwAspect, ptl, &szl); 
 
    //Better set these properly or errors occur. 
    stm.tymed=TYMED_HGLOBAL; 
    stm.pUnkForRelease=NULL; 
 
    cf=RegisterClipboardFormat(CF_LINKSRCDESCRIPTOR); 
    SETDefFormatEtc(fe, cf, TYMED_HGLOBAL); 
 
    if (FAILED(pIDataObject->SetData(&fe, &stm, TRUE))) 
        ReleaseStgMedium(&stm); 
 
    return; 
    } 
//End CHAPTER13MOD 
 
 
 
 
 
/* 
 * CTenant::ShowObjectType 
 * 
 * Purpose: 
 *  Tells the object to switch on or off an indication of whether 
 *  it is linked or embedded. 
 * 
 * Parameters: 
 *  fShow           BOOL indicating to show the type (TRUE) or 
 *                  not (FALSE) 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::ShowObjectType(BOOL fShow) 
    { 
    BOOL        fWasShow; 
    DWORD       dwState; 
    RECT        rc; 
    HDC         hDC; 
 
    fWasShow=(BOOL)(TENANTSTATE_SHOWTYPE & m_dwState); 
 
    dwState=m_dwState & ~TENANTSTATE_SHOWTYPE; 
    m_dwState=dwState | ((fShow) ? TENANTSTATE_SHOWTYPE : 0); 
 
    /* 
     * If this wasn't previously shown, just add the line, 
     * otherwise repaint. 
     */ 
    if (!fWasShow && fShow) 
        { 
        RECTFROMRECTL(rc, m_rcl); 
        RectConvertMappings(&rc, NULL, TRUE); 
        OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
 
        hDC=GetDC(m_hWnd); 
        OleUIShowObject(&rc, hDC 
            , (TENANTTYPE_LINKEDOBJECT==m_tType)); 
        ReleaseDC(m_hWnd, hDC); 
        } 
 
    if (fWasShow && !fShow) 
        Repaint(); 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::NotifyOfRename 
 * 
 * Purpose: 
 *  Instructs the tenant that the document was saved under a 
 *  different name.  In order to keep the right compound document 
 *  user interface, this tenant needs to tell its object through 
 *  IOleObject::SetHostNames. 
 * 
 * Parameters: 
 *  pszFile         LPTSTR of filename. 
 *  pmkFile         LPMONIKER of the new filename.  If this and 
 *                  pmkPage are NULL then nothing happens with 
 *                  monikers. 
 *  pmkPage         LPMONIKER of the page we're in. 
 * 
 * Return Value: 
 *  None 
 */ 
 
//CHAPTER13MOD 
void CTenant::NotifyOfRename(LPTSTR pszFile, LPMONIKER pmkFile 
    , LPMONIKER pmkPage) 
//End CHAPTER13MOD 
    { 
    TCHAR       szObj[40]; 
    TCHAR       szApp[40]; 
 
    if (NULL==m_pIOleObject) 
        return; 
 
    if (TEXT('\0')==*pszFile) 
        { 
        LoadString(m_pPG->m_hInst, IDS_UNTITLED, szObj 
            , sizeof(szObj)); 
        } 
    else 
        { 
        GetFileTitle(pszFile, szObj, sizeof(szObj)); 
 
       #ifndef WIN32 
        //Force filenames to uppercase in DOS versions. 
        AnsiUpper(szObj); 
       #endif 
        } 
 
    LoadString(m_pPG->m_hInst, IDS_CAPTION, szApp, sizeof(szApp)); 
#if defined(WIN32) && !defined(UNICODE) 
    OLECHAR pwcsObj[40]; 
    OLECHAR pwcsApp[40]; 
    mbstowcs(pwcsObj, szObj, 40); 
    mbstowcs(pwcsApp, szApp, 40); 
    m_pIOleObject->SetHostNames(pwcsApp, pwcsObj); 
#else 
    m_pIOleObject->SetHostNames(szApp, szObj); 
#endif 
    //CHAPTER13MOD 
    if (NULL!=pmkFile) 
        { 
        if (NULL!=m_pmkFile) 
            m_pmkFile->Release(); 
 
        m_pmkFile=pmkFile; 
        m_pmkFile->AddRef(); 
 
        m_pIOleObject->SetMoniker(OLEWHICHMK_CONTAINER, pmkFile); 
        } 
 
    if (NULL!=pmkFile && NULL!=pmkPage) 
        { 
        LPMONIKER   pmkTenant=NULL; 
        LPMONIKER   pmkRel=NULL; 
        HRESULT     hr; 
 
        //Create the moniker for this tenant. 
        GetStorageName(szObj); 
#if defined(WIN32) && !defined(UNICODE) 
        OLECHAR pwcsObj[40]; 
        mbstowcs(pwcsObj, szObj, 40); 
        hr=CreateItemMoniker(OLESTR("!"), pwcsObj, &pmkTenant); 
#else 
        hr=CreateItemMoniker(OLESTR("!"), szObj, &pmkTenant); 
#endif 
        if (SUCCEEDED(hr)) 
            { 
            //Create the relative moniker, i.e. no pathname. 
            hr=CreateGenericComposite(pmkPage, pmkTenant, &pmkRel); 
            pmkTenant->Release(); 
 
            if (SUCCEEDED(hr)) 
                m_pIOleObject->SetMoniker(OLEWHICHMK_OBJREL, pmkRel); 
 
            //Hold on to the relative moniker 
            if (NULL!=m_pmk) 
                m_pmk->Release(); 
 
            m_pmk=pmkRel; 
            } 
        } 
    //End CHAPTER13MOD 
 
    return; 
    } 
 
 
 
//CHAPTER13MOD 
/* 
 * CTenant::StorageGet 
 * 
 * Purpose: 
 *  Returns the IStorage pointer maintained by this tenant, 
 *  AddRef'd of course. 
 * 
 * Parameters: 
 *  ppStg           LPSTORAGE * in which to return the pointer. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::StorageGet(LPSTORAGE *ppStg) 
    { 
    if (NULL==ppStg) 
        return; 
 
    *ppStg=m_pIStorage; 
 
    if (NULL!=*ppStg) 
        (*ppStg)->AddRef(); 
 
    return; 
    } 
//End CHAPTER13MOD 
 
 
 
 
 
/* 
 * CTenant::Activate 
 * 
 * Purpose: 
 *  Activates a verb on the object living in the tenant.  Does 
 *  nothing for static objects. 
 * 
 * Parameters: 
 *  iVerb           LONG of the verb to execute. 
 * 
 * Return Value: 
 *  BOOL            TRUE if the object changed due to this verb 
 *                  execution. 
 */ 
 
BOOL CTenant::Activate(LONG iVerb) 
    { 
    RECT        rc, rcH; 
    CHourglass *pHour; 
    SIZEL       szl; 
 
    //Can't activate statics. 
    if (TENANTTYPE_STATIC==m_tType || NULL==m_pIOleObject) 
        { 
        MessageBeep(0); 
        return FALSE; 
        } 
 
    RECTFROMRECTL(rc, m_rcl); 
    RectConvertMappings(&rc, NULL, TRUE); 
    XformRectInPixelsToHimetric(NULL, &rc, &rcH); 
 
    pHour=new CHourglass; 
 
    //Get the server running first, then do a SetExtent, then show it 
    OleRun(m_pIOleObject); 
 
    if (m_fSetExtent) 
        { 
        SETSIZEL(szl, rcH.right-rcH.left, rcH.bottom-rcH.top); 
        m_pIOleObject->SetExtent(m_fe.dwAspect, &szl); 
        m_fSetExtent=FALSE; 
        } 
 
    m_pIOleObject->DoVerb(iVerb, NULL, m_pIOleClientSite, 0 
        , m_hWnd, &rcH); 
 
    delete pHour; 
 
    //If object changes, IAdviseSink::OnViewChange will see it. 
    return FALSE; 
    } 
 
 
 
 
 
 
/* 
 * CTenant::Draw 
 * 
 * Purpose: 
 *  Draws the tenant in its rectangle on the given hDC.  We assume 
 *  the DC is already set up for the mapping mode in which our 
 *  rectangle is expressed, since the Page we're in tells us both 
 *  the rect and the hDC. 
 * 
 * Parameters: 
 *  hDC             HDC in which to draw.  Could be a metafile, 
 *                  memory DC, screen, or printer. 
 *  ptd             DVTARGETDEVICE * describing the device. 
 *  hIC             HDC holding an information context (printing). 
 *  xOff, yOff      int offsets for the page in lometric 
 *  fNoColor        BOOL indicating if we should do B & W 
 *  fPrinter        BOOL indicating if we should render for a 
 *                  printer. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::Draw(HDC hDC, DVTARGETDEVICE *ptd, HDC hIC 
    , int xOff, int yOff, BOOL fNoColor, BOOL fPrinter) 
    { 
    HRESULT         hr; 
    RECT            rc; 
    RECTL           rcl; 
    UINT            uMM; 
 
    RECTFROMRECTL(rc, m_rcl); 
    OffsetRect(&rc, -xOff, -yOff); 
    RECTLFROMRECT(rcl, rc); 
 
    //Repaint erases the rectangle to insure full object cleanup 
    if (!fNoColor && !fPrinter) 
        { 
        COLORREF    cr; 
        cr=SetBkColor(hDC, GetSysColor(COLOR_WINDOW)); 
        ExtTextOut(hDC, rc.left, rc.top, ETO_OPAQUE, &rc, NULL 
            , 0, NULL); 
        SetBkColor(hDC, cr); 
        } 
 
    //We have to use Draw since we have a target device and IC. 
    hr=m_pIViewObject2->Draw(m_fe.dwAspect, -1, NULL, ptd, hIC, hDC 
        , &rcl, NULL, NULL, 0); 
 
 
    /* 
     * If Draw failed, then perhaps it couldn't work for the device, 
     * so try good old OleDraw as a last resort.  The code will 
     * generally be OLE_E_BLANK. 
     */ 
    if (FAILED(hr)) 
        OleDraw(m_pObj, m_fe.dwAspect, hDC, &rc); 
 
    if (!fPrinter) 
        { 
        /* 
         * Draw sizing handles to show the selection state.  We 
         * convert things to MM_TEXT since that's what this 
         * function expects. 
         */ 
        RectConvertMappings(&rc, NULL, TRUE); 
        uMM=SetMapMode(hDC, MM_TEXT); 
 
        if (TENANTSTATE_SELECTED & m_dwState) 
            { 
            OleUIDrawHandles(&rc, hDC, OLEUI_HANDLES_INSIDE 
                | OLEUI_HANDLES_NOBORDER | OLEUI_HANDLES_USEINVERSE 
                , CXYHANDLE, TRUE); 
            } 
 
        if (TENANTSTATE_OPEN & m_dwState) 
            OleUIDrawShading(&rc, hDC, OLEUI_SHADE_FULLRECT, 0); 
 
        //Distinguish linked and embedded objects. 
        if (TENANTSTATE_SHOWTYPE & m_dwState) 
            { 
            OleUIShowObject(&rc, hDC 
                , (TENANTTYPE_LINKEDOBJECT==m_tType)); 
            } 
 
        uMM=SetMapMode(hDC, uMM); 
        } 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::Repaint 
 * CTenant::Invalidate 
 * 
 * Purpose: 
 *  Repaints the tenant where it lies or invalidates its area 
 *  for later repainting. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::Repaint(void) 
    { 
    RECT        rc; 
    HDC         hDC; 
 
    /* 
     * We might be asked to repaint from 
     * IOleClientSite::OnShowWindow after we've switched pages if 
     * our server was runnnig. This check on m_cOpens prevents that. 
     */ 
    if (0==m_cOpens) 
        return; 
 
    hDC=GetDC(m_hWnd); 
    SetRect(&rc, m_pPG->m_xPos, m_pPG->m_yPos, 0, 0); 
    RectConvertMappings(&rc, NULL, FALSE); 
 
    SetMapMode(hDC, MM_LOMETRIC); 
    Draw(hDC, NULL, NULL, rc.left, rc.top, FALSE, FALSE); 
 
    ReleaseDC(m_hWnd, hDC); 
    return; 
    } 
 
 
void CTenant::Invalidate(void) 
    { 
    RECTL       rcl; 
    RECT        rc; 
 
    RectGet(&rcl, TRUE); 
    RECTFROMRECTL(rc, rcl); 
 
    OffsetRect(&rc, -(int)m_pPG->m_xPos, -(int)m_pPG->m_yPos); 
    InvalidateRect(m_hWnd, &rc, FALSE); 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::TypeGet 
 * 
 * Purpose: 
 *  Returns the type of this tenant 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  TENANTTYPE      Type of the tenant. 
 */ 
 
TENANTTYPE CTenant::TypeGet(void) 
    { 
    return m_tType; 
    } 
 
 
 
 
/* 
 * CTenant::FIsSelected 
 * 
 * Purpose: 
 *  Returns the selection state of this tenant. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  BOOL            TRUE if selected, FALSE otherwise. 
 */ 
 
BOOL CTenant::FIsSelected(void) 
    { 
    return (BOOL)(m_dwState & TENANTSTATE_SELECTED); 
    } 
 
 
 
/* 
 * CTenant::FConvertToStatic 
 * 
 * Purpose: 
 *  Changes the object that lives in this tenant to a static one. 
 * 
 * Parameters: 
 *  None 
 * 
 * Return Value: 
 *  BOOL            TRUE if successful, FALSE otherwise. 
 */ 
 
BOOL CTenant::FConvertToStatic(void) 
    { 
    /* 
     * If you SetSourceMoniker in IOleLink to NULL, then the link is 
     * gone as far as OLE is concerned.  You only need to make sure 
     * the user doesn't have access to other functionality for this 
     * object, which we insure by changing our internal type.  We 
     * set this on loading if GetSourceMoniker returns NULL. 
     */ 
    m_tType=TENANTTYPE_STATIC; 
    return TRUE; 
    } 
 
 
 
 
 
 
/* 
 * CTenant::ObjectGet 
 * 
 * Purpose: 
 *  Retrieves the LPUNKNOWN of the object in use by this tenant 
 * 
 * Parameters: 
 *  ppUnk           LPUNKNOWN * in which to return the object 
 *                  pointer. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::ObjectGet(LPUNKNOWN *ppUnk) 
    { 
    if (NULL!=ppUnk) 
        { 
        *ppUnk=m_pObj; 
        m_pObj->AddRef(); 
        } 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::FormatEtcGet 
 * 
 * Purpose: 
 *  Retrieves the FORMATETC in use by this tenant 
 * 
 * Parameters: 
 *  pFE             LPFORMATETC in which to store the information. 
 *  fPresentation   BOOL indicating if we want the real format or 
 *                  that of the presentation. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::FormatEtcGet(LPFORMATETC pFE, BOOL fPresentation) 
    { 
    if (NULL!=pFE) 
        { 
        *pFE=m_fe; 
 
        //If there is no format, use metafile (for embedded objects) 
        if (fPresentation || 0==pFE->cfFormat) 
            { 
            //Don't mess with dwAspect; might be icon or content. 
            pFE->cfFormat=CF_METAFILEPICT; 
            pFE->tymed=TYMED_MFPICT; 
            } 
        } 
 
    return; 
    } 
 
 
 
 
 
/* 
 * CTenant::SizeGet 
 * CTenant::SizeSet 
 * CTenant::RectGet 
 * CTenant::RectSet 
 * 
 * Purpose: 
 *  Returns or sets the size/position of the object contained here. 
 * 
 * Parameters: 
 *  pszl/prcl       LPSIZEL (Size) or LPRECTL (Rect) with the 
 *                  extents of interest.  In Get situations, 
 *                  this will receive the extents; in Set it 
 *                  contains the extents. 
 *  fDevice         BOOL indicating that pszl/prcl is expressed 
 *                  in device units.  Otherwise it's LOMETRIC. 
 *  fInformObj      (Set Only) BOOL indicating if we need to inform 
 *                  the object all. 
 * 
 * Return Value: 
 *  None 
 */ 
 
void CTenant::SizeGet(LPSIZEL pszl, BOOL fDevice) 
    { 
    if (!fDevice) 
        { 
        pszl->cx=m_rcl.right-m_rcl.left; 
        pszl->cy=m_rcl.bottom-m_rcl.top; 
        } 
    else 
        { 
        RECT        rc; 
 
        SetRect(&rc, (int)(m_rcl.right-m_rcl.left) 
            , (int)(m_rcl.bottom-m_rcl.top), 0, 0); 
 
        RectConvertMappings(&rc, NULL, TRUE); 
 
        pszl->cx=(long)rc.left; 
        pszl->cy=(long)rc.top; 
        } 
 
    return; 
    } 
 
 
void CTenant::SizeSet(LPSIZEL pszl, BOOL fDevice, BOOL fInformObj) 
    { 
    SIZEL           szl; 
 
    if (!fDevice) 
        { 
        szl=*pszl; 
        m_rcl.right =pszl->cx+m_rcl.left; 
        m_rcl.bottom=pszl->cy+m_rcl.top; 
        } 
    else 
        { 
        RECT        rc; 
 
        SetRect(&rc, (int)pszl->cx, (int)pszl->cy, 0, 0); 
        RectConvertMappings(&rc, NULL, FALSE); 
 
        m_rcl.right =(long)rc.left+m_rcl.left; 
        m_rcl.bottom=(long)rc.top+m_rcl.top; 
 
        SETSIZEL(szl, (long)rc.left, (long)rc.top); 
        } 
 
 
    //Tell OLE that this object was resized. 
    if (NULL!=m_pIOleObject && fInformObj) 
        { 
        HRESULT     hr; 
        BOOL        fRun=FALSE; 
 
        //Convert our LOMETRIC into HIMETRIC by *=10 
        szl.cx*=10; 
        szl.cy*=-10;    //Our size is stored negative. 
 
        /* 
         * If the MiscStatus bit of OLEMISC_RECOMPOSEONRESIZE 
         * is set, then we need to run the object before calling 
         * SetExtent to make sure it has a real chance to 
         * re-render the object.  We have to update and close 
         * the object as well after this happens. 
         */ 
 
        if (OLEMISC_RECOMPOSEONRESIZE & m_grfMisc) 
            { 
            if (!OleIsRunning(m_pIOleObject)) 
                { 
                OleRun(m_pIOleObject); 
                fRun=TRUE; 
                } 
            } 
 
        hr=m_pIOleObject->SetExtent(m_fe.dwAspect, &szl); 
 
        /* 
         * If the object is not running and it does not have 
         * RECOMPOSEONRESIZE, then SetExtent fails.  Make 
         * sure that we call SetExtent again (by just calling 
         * SizeSet here again) when we next run the object. 
         */ 
        if (SUCCEEDED(hr)) 
            { 
            m_fSetExtent=FALSE; 
 
            if (fRun) 
                { 
                m_pIOleObject->Update(); 
                m_pIOleObject->Close(OLECLOSE_SAVEIFDIRTY); 
                } 
            } 
        else 
            { 
            if (OLE_E_NOTRUNNING==hr) 
                m_fSetExtent=TRUE; 
            } 
        } 
 
    return; 
    } 
 
 
void CTenant::RectGet(LPRECTL prcl, BOOL fDevice) 
    { 
    if (!fDevice) 
        *prcl=m_rcl; 
    else 
        { 
        RECT        rc; 
 
        RECTFROMRECTL(rc, m_rcl); 
        RectConvertMappings(&rc, NULL, TRUE); 
        RECTLFROMRECT(*prcl, rc); 
        } 
 
    return; 
    } 
 
 
void CTenant::RectSet(LPRECTL prcl, BOOL fDevice, BOOL fInformObj) 
    { 
    SIZEL   szl; 
    LONG    cx, cy; 
 
    cx=m_rcl.right-m_rcl.left; 
    cy=m_rcl.bottom-m_rcl.top; 
 
    if (!fDevice) 
        m_rcl=*prcl; 
    else 
        { 
        RECT        rc; 
 
        RECTFROMRECTL(rc, *prcl); 
        RectConvertMappings(&rc, NULL, FALSE); 
        RECTLFROMRECT(m_rcl, rc); 
        } 
 
    /* 
     * Tell ourselves that the size changed, if it did.  SizeSet 
     * will call IOleObject::SetExtent for us. 
     */ 
    if ((m_rcl.right-m_rcl.left)!=cx || (m_rcl.bottom-m_rcl.top)!=cy) 
        { 
        SETSIZEL(szl, m_rcl.right-m_rcl.left, m_rcl.bottom-m_rcl.top); 
        SizeSet(&szl, FALSE, fInformObj); 
        } 
 
    return; 
    } 
 
 
 
 
 
 
 
/* 
 * CTenant::CreateStatic 
 * (Protected) 
 * 
 * Purpose: 
 *  Creates a new static bitmap or metafile object for this tenant 
 *  using a freeloading method allowing us to specify exactly which 
 *  type of data we want to paste since OleCreateStaticFromData 
 *  doesn't. 
 * 
 * Parameters: 
 *  pIDataObject    LPDATAOBJECT from which to paste. 
 *  pFE             LPFORMATETC describing the format to paste. 
 *  ppObj           LPUNKNOWN * into which we store the 
 *                  object pointer. 
 * 
 * Return Value: 
 *  HRESULT         NOERROR on success, error code otherwise. 
 */ 
 
HRESULT CTenant::CreateStatic(LPDATAOBJECT pIDataObject 
    , LPFORMATETC pFE, LPUNKNOWN *ppObj) 
    { 
    HRESULT             hr; 
    STGMEDIUM           stm; 
    LPUNKNOWN           pIUnknown; 
    LPOLECACHE          pIOleCache; 
    LPPERSISTSTORAGE    pIPersistStorage; 
    CLSID               clsID; 
 
    *ppObj=NULL; 
 
    //Try to get the data desired as specified in pFE->cfFormat 
    hr=pIDataObject->GetData(pFE, &stm); 
 
    if (FAILED(hr)) 
        return hr; 
 
    //Create the object to handle this data. 
    if (CF_METAFILEPICT==pFE->cfFormat) 
        clsID=CLSID_Picture_Metafile; 
    else 
        clsID=CLSID_Picture_Dib; 
 
    hr=CreateDataCache(NULL, clsID, IID_IUnknown 
        , (PPVOID)&pIUnknown); 
 
    if (FAILED(hr)) 
        { 
        ReleaseStgMedium(&stm); 
        return hr; 
        } 
 
    m_clsID=clsID; 
 
    //Stuff the data into the object 
    pIUnknown->QueryInterface(IID_IPersistStorage 
        , (PPVOID)&pIPersistStorage); 
    pIPersistStorage->InitNew(m_pIStorage); 
 
    //Now that we have the cache object, shove the data into it. 
    pIUnknown->QueryInterface(IID_IOleCache, (PPVOID)&pIOleCache); 
    pIOleCache->Cache(pFE, ADVF_PRIMEFIRST, NULL); 
 
    hr=pIOleCache->SetData(pFE, &stm, TRUE); 
    pIOleCache->Release(); 
 
    //Insure there is a persistent copy on the disk 
    WriteClassStg(m_pIStorage, m_clsID); 
    pIPersistStorage->Save(m_pIStorage, TRUE); 
    pIPersistStorage->SaveCompleted(NULL); 
    pIPersistStorage->Release(); 
 
    //The cache owns this now. 
    ReleaseStgMedium(&stm); 
 
    if (FAILED(hr)) 
        pIUnknown->Release(); 
    else 
        *ppObj=pIUnknown; 
 
    return hr; 
    }