home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / inole2 / chap21 / patron / page.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  38.1 KB  |  1,616 lines

  1. /*
  2.  * PAGE.CPP
  3.  * Patron Chapter 21
  4.  *
  5.  * Implementation of parts of the CPage class; those member
  6.  * functions dealing with mouse events are in PAGEMOUS.CPP.
  7.  *
  8.  * Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  9.  *
  10.  * Kraig Brockschmidt, Microsoft
  11.  * Internet  :  kraigb@microsoft.com
  12.  * Compuserve:  >INTERNET:kraigb@microsoft.com
  13.  */
  14.  
  15.  
  16. #include "patron.h"
  17.  
  18.  
  19. /*
  20.  * CPage::CPage
  21.  * CPage::~CPage
  22.  *
  23.  * Constructor Parameters:
  24.  *  dwID            DWORD identifier for this page.
  25.  *  hWnd            HWND of the pages window (for repaints, etc).
  26.  *  pPG             PCPages to the Pages window.
  27.  */
  28.  
  29. CPage::CPage(DWORD dwID, HWND hWnd, PCPages pPG)
  30.     {
  31.     m_dwID     =dwID;
  32.     m_pIStorage=NULL;
  33.  
  34.     m_cOpens=0;
  35.     m_hWnd=hWnd;
  36.     m_pPG=pPG;
  37.  
  38.     m_dwIDNext      =0;
  39.     m_cTenants      =0;
  40.     m_hWndTenantList=NULL;
  41.     m_iTenantCur    =NOVALUE;   //Tenants are zero indexed.
  42.     m_pTenantCur    =NULL;
  43.  
  44.     m_uHTCode=HTNOWHERE;
  45.     m_uSizingFlags=0;
  46.     m_fTracking=FALSE;
  47.     m_hDC=NULL;
  48.  
  49.     m_fDragPending=FALSE;
  50.     m_fSizePending=FALSE;
  51.     m_fTimer=FALSE;
  52.  
  53.     //Get WIN.INI distance and delay values, with OLE defaults.
  54.     m_cxyDist=GetProfileInt(TEXT("windows"), TEXT("DragMinDist")
  55.         , DD_DEFDRAGMINDIST);
  56.     m_cDelay=GetProfileInt(TEXT("windows"), TEXT("DragDelay")
  57.         , DD_DEFDRAGDELAY);
  58.  
  59.     m_fReopen=FALSE;
  60.  
  61.     //CHAPTER21MOD
  62.     m_pmkFile=m_pPG->m_pmkFile;
  63.  
  64.     if (NULL!=m_pmkFile)
  65.         m_pmkFile->AddRef();
  66.  
  67.     m_cRef=0L;
  68.     m_dwRegROTWild=0L;
  69.     m_pImpIOleItemContainer=NULL;
  70.     //End CHAPTER21MOD
  71.  
  72.     return;
  73.     }
  74.  
  75.  
  76. CPage::~CPage(void)
  77.     {
  78.     //CHAPTER21MOD
  79.     INOLE_RevokeAsRunning(&m_dwRegROTWild);
  80.     //End CHAPTER21MOD
  81.  
  82.     if (m_fTimer)
  83.         KillTimer(m_hWnd, IDTIMER_DEBOUNCE);
  84.  
  85.     m_hWnd=NULL;
  86.     Close(FALSE);
  87.     return;
  88.     }
  89.  
  90.  
  91.  
  92. //CHAPTER21MOD
  93. /*
  94.  * CPage::QueryInterface
  95.  * CPage::AddRef
  96.  * CPage::Release
  97.  *
  98.  * Purpose:
  99.  *  IUnknown members for CPage object.
  100.  */
  101.  
  102. STDMETHODIMP CPage::QueryInterface(REFIID riid, PPVOID ppv)
  103.     {
  104.     *ppv=NULL;
  105.  
  106.     if (IID_IUnknown==riid)
  107.         *ppv=this;
  108.  
  109.     if (IID_IOleItemContainer==riid || IID_IOleContainer==riid
  110.         || IID_IParseDisplayName==riid)
  111.         *ppv=m_pImpIOleItemContainer;
  112.  
  113.     if (NULL!=*ppv)
  114.         {
  115.         ((LPUNKNOWN)*ppv)->AddRef();
  116.         return NOERROR;
  117.         }
  118.  
  119.     return ResultFromScode(E_NOINTERFACE);
  120.     }
  121.  
  122.  
  123. STDMETHODIMP_(ULONG) CPage::AddRef(void)
  124.     {
  125.     return ++m_cRef;
  126.     }
  127.  
  128. STDMETHODIMP_(ULONG) CPage::Release(void)
  129.     {
  130.     if (0!=--m_cRef)
  131.         return m_cRef;
  132.  
  133.     delete this;
  134.     return 0;
  135.     }
  136.  
  137. //End CHAPTER21MOD
  138.  
  139.  
  140.  
  141.  
  142. /*
  143.  * CPage::GetID
  144.  *
  145.  * Return Value:
  146.  *  DWORD           dwID field in this page.
  147.  */
  148.  
  149. DWORD CPage::GetID(void)
  150.     {
  151.     return m_dwID;
  152.     }
  153.  
  154.  
  155.  
  156.  
  157.  
  158. /*
  159.  * CPage::Open
  160.  *
  161.  * Purpose:
  162.  *  Retrieves the IStorage associated with this page.  The IStorage
  163.  *  is owned by the page and thus the page always holds a reference
  164.  *  count.  The caller should call Close or delete this page to
  165.  *  match this open.
  166.  *
  167.  *  This function may be called multiple times resulting in
  168.  *  additional reference counts on the storage each of which must be
  169.  *  matched with a call to Close.  The last Close can be done
  170.  *  through delete.
  171.  *
  172.  * Parameters:
  173.  *  pIStorage       LPSTORAGE in which this page lives.
  174.  *
  175.  * Return Value:
  176.  *  BOOL            TRUE if opening succeeds, FALSE otherwise.
  177.  */
  178.  
  179. BOOL CPage::Open(LPSTORAGE pIStorage)
  180.     {
  181.     HRESULT                 hr=NOERROR;
  182.     LPSTREAM                pIStream;
  183.     DWORD                   dwMode;
  184.     OLECHAR                 szTemp[32];
  185.     TCHAR                   szCap[32];
  186.     BOOL                    fNew;
  187.     BOOL                    fCreated=FALSE;
  188.     TENANTLIST              tl;
  189.     PTENANTINFO             pti;
  190.     ULONG                   cb;
  191.     LPMALLOC                pIMalloc;
  192.     UINT                    i;
  193.     PCTenant                pTenant;
  194.     UINT                    cLinks;
  195.     LPOLELINK               pIOleLink;
  196.     LPUNKNOWN               pIUnknown;
  197.     UINT                    uRet;
  198.     OLEUIEDITLINKS          el;
  199.     PCIOleUILinkContainer   pIUILinks;
  200.     HWND                    hWndDoc;
  201.  
  202.     fNew=(NULL==m_pIStorage);
  203.  
  204.     if (!fNew)
  205.         {
  206.         m_cOpens++;
  207.         m_pIStorage->AddRef();
  208.         return TRUE;
  209.         }
  210.  
  211.     if (NULL==pIStorage)
  212.         return FALSE;
  213.  
  214.     /*
  215.      * Attempt to open the storage under this ID.  If none,
  216.      * create one.  In either case, the IStorage is either
  217.      * saved in pPage or released.
  218.      */
  219.  
  220.     GetStorageName(szTemp);
  221.     dwMode=STGM_TRANSACTED | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
  222.  
  223.     hr=pIStorage->OpenStorage(szTemp, NULL, dwMode, NULL, 0
  224.         , &m_pIStorage);
  225.  
  226.     if (FAILED(hr))
  227.         {
  228.         hr=pIStorage->CreateStorage(szTemp, dwMode, 0, 0
  229.             , &m_pIStorage);
  230.         fCreated=TRUE;
  231.         }
  232.  
  233.     if (FAILED(hr))
  234.         return FALSE;
  235.  
  236.     m_cOpens++;
  237.  
  238.     if (NULL==m_hWndTenantList)
  239.         {
  240.         /*
  241.          * The first time we open this page, create the hidden
  242.          * listbox we'll use to track tenants.  We give it the
  243.          * owner-draw style so we can just store pointers in it.
  244.          */
  245.         m_hWndTenantList=CreateWindow(TEXT("listbox")
  246.             , TEXT("Tenant List"), WS_POPUP | LBS_OWNERDRAWFIXED
  247.             , 0, 0, 100, 100, HWND_DESKTOP, NULL
  248.             , m_pPG->m_hInst, NULL);
  249.  
  250.         if (NULL==m_hWndTenantList)
  251.             return FALSE;
  252.         }
  253.  
  254.     //CHAPTER21MOD
  255.     m_pImpIOleItemContainer=new CImpIOleItemContainer(this, this
  256.         , FALSE);
  257.  
  258.     if (NULL==m_pImpIOleItemContainer)
  259.         return FALSE;
  260.     //End CHAPTER21MOD
  261.  
  262.     //If this is brand-new, we're done.
  263.     if (fCreated)
  264.         return TRUE;
  265.  
  266.  
  267.     /*
  268.      * Now open the stream we saved in Close and load all the
  269.      * tenants listed in there.  If there are none, then we don't
  270.      * have to load squat.
  271.      */
  272.  
  273.     hr=m_pIStorage->OpenStream(SZSTREAMTENANTLIST, NULL, STGM_DIRECT
  274.         | STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pIStream);
  275.  
  276.     if (FAILED(hr))
  277.         return FALSE;
  278.  
  279.     if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
  280.         {
  281.         pIStream->Read(&tl, sizeof(tl), NULL);
  282.         m_cTenants=tl.cTenants;
  283.         m_dwIDNext=tl.dwIDNext;
  284.         m_iTenantCur=0;
  285.  
  286.         cb=tl.cTenants*sizeof(TENANTINFO);
  287.  
  288.         if (0!=cb)
  289.             {
  290.             pti=(PTENANTINFO)pIMalloc->Alloc(cb);
  291.  
  292.             if (NULL!=pti)
  293.                 {
  294.                 pIStream->Read(pti, cb, NULL);
  295.  
  296.                 for (i=0; i < m_cTenants; i++)
  297.                     {
  298.                     if (TenantAdd(NOVALUE, (pti+i)->dwID, &pTenant))
  299.                         {
  300.                         pTenant->Load(m_pIStorage, (pti+i));
  301.  
  302.                         //Make sure it knows about the show state.
  303.                         pTenant->ShowObjectType(m_pPG->m_fShowTypes);
  304.                         }
  305.                     }
  306.  
  307.                 pIMalloc->Free(pti);
  308.                 }
  309.             }
  310.  
  311.         pIMalloc->Release();
  312.         }
  313.  
  314.     pIStream->Release();
  315.  
  316.     //Get and select the first tenant
  317.     if (TenantGet(0, &m_pTenantCur, FALSE))
  318.         m_pTenantCur->Select(TRUE);
  319.  
  320.  
  321.     //If we just saved and closed, don't bother with updating links
  322.     if (m_fReopen)
  323.         {
  324.         m_fReopen=FALSE;
  325.         return TRUE;
  326.         }
  327.  
  328.     /*
  329.      * Update all the links in this page, showing the progress
  330.      * indicator as it's happening.  We use the same
  331.      * IOlUILinkContainer implementation as we do for the links
  332.      * dialog, passing it to OleUIUpdateLinks which does everything
  333.      * for us.
  334.      *
  335.      * We might also optimize this to not do anything if there are
  336.      * no automatic links, but it's not a big concern.
  337.      */
  338.  
  339.     //First, count the number of automatic links.
  340.     cLinks=0;
  341.  
  342.     for (i=0; i < m_cTenants; i++)
  343.         {
  344.         if (TenantGet(i, &pTenant, FALSE))
  345.             {
  346.             DWORD       dw;
  347.  
  348.             pTenant->ObjectGet(&pIUnknown);
  349.             hr=pIUnknown->QueryInterface(IID_IOleLink
  350.                 , (PPVOID)&pIOleLink);
  351.             pIUnknown->Release();
  352.  
  353.             if (FAILED(hr))
  354.                 continue;
  355.  
  356.             pIOleLink->GetUpdateOptions(&dw);
  357.             pIOleLink->Release();
  358.  
  359.             if (OLEUPDATE_ALWAYS==dw)
  360.                 cLinks++;
  361.             }
  362.         }
  363.  
  364.     //If we have any automatic links, invoke the update dialog.
  365.     if (0==cLinks)
  366.         return TRUE;
  367.  
  368.     //Create an IOleUILinkContainer instantiation.
  369.     if (!m_pPG->GetUILinkContainer(&pIUILinks))
  370.         return TRUE;    //Guess we can't update, oh well.
  371.  
  372.     hWndDoc=GetParent(m_hWnd);
  373.     LoadString(m_pPG->m_hInst, IDS_CAPTION, szCap, sizeof(szCap));
  374.  
  375.     if (!OleUIUpdateLinks(pIUILinks, hWndDoc, szCap, cLinks))
  376.         {
  377.         /*
  378.          * If updating failed, ask to show the links dialog.  NOTE:
  379.          * OleUIPromptUser has a variable wsprintf argument list
  380.          * after the hWnd parameter!  Use appropriate typecasting!
  381.          */
  382.         uRet=OleUIPromptUser(IDD_CANNOTUPDATELINK, hWndDoc, szCap);
  383.  
  384.        #ifdef IDC_PU_LINKS
  385.         if (IDC_PU_LINKS==uRet)
  386.        #else
  387.         if (ID_PU_LINKS==uRet)  //Win3.1
  388.        #endif
  389.             {
  390.             //Throw up the links dialog.
  391.             memset(&el, 0, sizeof(el));
  392.             el.cbStruct=sizeof(el);
  393.             el.hWndOwner=hWndDoc;
  394.             el.lpOleUILinkContainer=pIUILinks;
  395.             OleUIEditLinks(&el);
  396.             }
  397.         }
  398.  
  399.     m_pPG->m_fDirty=pIUILinks->m_fDirty;
  400.     pIUILinks->Release();
  401.     return TRUE;
  402.     }
  403.  
  404.  
  405.  
  406.  
  407.  
  408. /*
  409.  * CPage::Close
  410.  *
  411.  * Purpose:
  412.  *  Possibly commits the storage, then releases it reversing the
  413.  *  reference count from Open.
  414.  *
  415.  * Parameters:
  416.  *  fCommit         BOOL indicating if we're to commit.
  417.  *
  418.  * Return Value:
  419.  *  None
  420.  */
  421.  
  422. void CPage::Close(BOOL fCommit)
  423.     {
  424.     if (NULL==m_pIStorage)
  425.         return;
  426.  
  427.     if (fCommit)
  428.         Update();
  429.  
  430.     m_pIStorage->Release();
  431.  
  432.     //If this was the last close, make all tenants loaded->passive
  433.     if (0==--m_cOpens)
  434.         {
  435.         UINT        i;
  436.         PCTenant    pTenant;
  437.  
  438.         m_pIStorage=NULL;
  439.  
  440.         for (i=0; i < m_cTenants; i++)
  441.             {
  442.             if (TenantGet(i, &pTenant, FALSE))
  443.                 {
  444.                 if (NULL!=m_hWnd)
  445.                     {
  446.                     //Open may select again, so this repaints.
  447.                     pTenant->Select(FALSE);
  448.                     }
  449.  
  450.                 pTenant->Close(FALSE);
  451.                 pTenant->Release();
  452.                 }
  453.             }
  454.  
  455.         DestroyWindow(m_hWndTenantList);
  456.         m_hWndTenantList=NULL;
  457.         m_fReopen=TRUE;
  458.  
  459.         //CHAPTER21MOD
  460.         if (NULL!=m_pmkFile)
  461.             m_pmkFile->Release();
  462.  
  463.         m_pmkFile=NULL;
  464.  
  465.         DeleteInterfaceImp(m_pImpIOleItemContainer);
  466.         //End CHAPTER21MOD
  467.         }
  468.  
  469.     return;
  470.     }
  471.  
  472.  
  473.  
  474.  
  475. /*
  476.  * CPage::Update
  477.  *
  478.  * Purpose:
  479.  *  Forces a common on the page if it's open.
  480.  *
  481.  * Parameters:
  482.  *  None
  483.  *
  484.  * Return Value:
  485.  *  BOOL            TRUE if there are any open objects on this page,
  486.  *                  that is, we should remain open.
  487.  */
  488.  
  489. BOOL CPage::Update(void)
  490.     {
  491.     BOOL            fOpen=FALSE;
  492.     UINT            i;
  493.     PCTenant        pTenant;
  494.     HRESULT         hr;
  495.     LPSTREAM        pIStream;
  496.     TENANTLIST      tl;
  497.     PTENANTINFO     pti;
  498.     ULONG           cb;
  499.     LPMALLOC        pIMalloc;
  500.  
  501.     //Walk the list of objects and update them all as well.
  502.     for (i=0; i < m_cTenants; i++)
  503.         {
  504.         if (TenantGet(i, &pTenant, FALSE))
  505.             fOpen |= pTenant->Update();
  506.         }
  507.  
  508.     //Now write our own stream containing the tenant list.
  509.     hr=m_pIStorage->CreateStream(SZSTREAMTENANTLIST, STGM_CREATE
  510.         | STGM_WRITE| STGM_DIRECT | STGM_SHARE_EXCLUSIVE, 0, 0
  511.         , &pIStream);
  512.  
  513.     if (FAILED(hr))
  514.         return fOpen;
  515.  
  516.     if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
  517.         {
  518.         tl.cTenants=m_cTenants;
  519.         tl.dwIDNext=m_dwIDNext;
  520.  
  521.         pIStream->Write(&tl, sizeof(TENANTLIST), &cb);
  522.  
  523.         cb=m_cTenants*sizeof(TENANTINFO);
  524.         pti=(PTENANTINFO)pIMalloc->Alloc(cb);
  525.  
  526.         if (NULL!=pti)
  527.             {
  528.             for (i=0; i < m_cTenants; i++)
  529.                 {
  530.                 TenantGet(i, &pTenant, FALSE);
  531.                 pTenant->GetInfo((pti+i));
  532.                 }
  533.  
  534.             pIStream->Write(pti, cb, &cb);
  535.             pIMalloc->Free(pti);
  536.             }
  537.  
  538.         pIMalloc->Release();
  539.         }
  540.  
  541.     pIStream->Release();
  542.  
  543.     //Now commit the whole mess and we're done
  544.     if (NULL!=m_pIStorage)
  545.         m_pIStorage->Commit(STGC_DEFAULT);
  546.  
  547.     return fOpen;
  548.     }
  549.  
  550.  
  551.  
  552.  
  553.  
  554. /*
  555.  * CPage::Destroy
  556.  *
  557.  * Purpose:
  558.  *  Removes this page from the given storage.  The caller should
  559.  *  eventually delete this Page object to free the storage.
  560.  *
  561.  * Parameters:
  562.  *  pIStorage       LPSTORAGE contianing this page on which to call
  563.  *                  DestroyElement
  564.  *
  565.  * Return Value:
  566.  *  None
  567.  */
  568.  
  569. void CPage::Destroy(LPSTORAGE pIStorage)
  570.     {
  571.     if (NULL!=pIStorage)
  572.         {
  573.         OLECHAR szTemp[32];
  574.  
  575.         Close(FALSE);
  576.         GetStorageName(szTemp);
  577.         pIStorage->DestroyElement(szTemp);
  578.         }
  579.  
  580.     return;
  581.     }
  582.  
  583.  
  584.  
  585.  
  586. /*
  587.  * CPage::GetStorageName
  588.  *
  589.  * Parameters:
  590.  *  pszName         LPOLESTR to a buffer in which to store the
  591.  *                  storage name for this page.
  592.  *
  593.  * Return Value:
  594.  *  UINT            Number of characters stored.
  595.  */
  596.  
  597. UINT CPage::GetStorageName(LPOLESTR pszName)
  598.     {
  599.    #ifdef WIN32ANSI
  600.     char        szTemp[32];
  601.     UINT        cch;
  602.  
  603.     cch=wsprintf(szTemp, "Page %lu", m_dwID);
  604.     MultiByteToWideChar(CP_ACP, 0, szTemp, -1, pszName, 32);
  605.     return cch;
  606.    #else
  607.     return wsprintf(pszName, TEXT("Page %lu"), m_dwID);
  608.    #endif
  609.     }
  610.  
  611.  
  612.  
  613.  
  614. /*
  615.  * CPage::Draw
  616.  *
  617.  * Purpose:
  618.  *  Draws the objects on this page to the given hDC.
  619.  *
  620.  * Parameters:
  621.  *  hDC             HDC on which to draw.
  622.  *  xOff, yOff      int offsets for the page.
  623.  *  fNoColor        BOOL indicating black & white screen rendering.
  624.  *  fPrinter        BOOL indicating hDC is on the printer.
  625.  *
  626.  * Return Value:
  627.  *  None
  628.  */
  629.  
  630. void CPage::Draw(HDC hDC, int xOff, int yOff, BOOL fNoColor
  631.     , BOOL fPrinter)
  632.     {
  633.     int                 i;
  634.     PCTenant            pTenant;
  635.     HDC                 hIC=NULL;
  636.     PCOMBINEDEVICE      pcd=NULL;
  637.     DVTARGETDEVICE     *ptd=NULL;
  638.  
  639.     /*
  640.      * If printing, tell the tenant to forget the borders. Otherwise
  641.      * we leave xOff and yOff the same to account for scrolling.
  642.      */
  643.     if (fPrinter)
  644.         {
  645.         xOff=LOMETRIC_BORDER+m_pPG->m_xMarginLeft;
  646.         yOff=-LOMETRIC_BORDER-m_pPG->m_yMarginTop;
  647.  
  648.         /*
  649.          * Get device information.  If this fails, ptd is
  650.          * NULL which is acceptable.
  651.          */
  652.         if (m_pPG->DevReadConfig(&pcd, &hIC))
  653.             ptd=&(pcd->td);
  654.         }
  655.  
  656.     for (i=(int)m_cTenants-1; i >=0; i--)
  657.         {
  658.         if (TenantGet(i, &pTenant, FALSE))
  659.             {
  660.             RECT        rc, rcWin;
  661.             RECTL       rcl;
  662.  
  663.             //Paint this tenant only if visible.
  664.             pTenant->RectGet(&rcl, TRUE);
  665.             RECTFROMRECTL(rc, rcl);
  666.             OffsetRect(&rc, -(int)m_pPG->m_xPos
  667.                 , -(int)m_pPG->m_yPos);
  668.             GetClientRect(m_hWnd, &rcWin);
  669.  
  670.             if (IntersectRect(&rc, &rc, &rcWin))
  671.                 {
  672.                 pTenant->Draw(hDC, ptd, hIC, xOff, yOff
  673.                     , fNoColor, fPrinter);
  674.                 }
  675.             }
  676.         }
  677.  
  678.     //Free whatever CPages::DevReadConfig returned.
  679.     if (NULL!=pcd)
  680.         {
  681.         LPMALLOC    pIMalloc;
  682.  
  683.         if (SUCCEEDED(CoGetMalloc(MEMCTX_TASK, &pIMalloc)))
  684.             {
  685.             pIMalloc->Free(pcd);
  686.             pIMalloc->Release();
  687.             }
  688.         }
  689.  
  690.     if (NULL!=hIC)
  691.         DeleteDC(hIC);
  692.  
  693.     return;
  694.     }
  695.  
  696.  
  697.  
  698.  
  699.  
  700.  
  701. /*
  702.  * CPage::TenantCreate
  703.  *
  704.  * Purpose:
  705.  *  Creates a new tenant of a specific type.
  706.  *
  707.  * Parameters:
  708.  *  tType           TENANTTYPE to create.
  709.  *  pv              LPVOID providing information for the new
  710.  *                  object creation.
  711.  *  pFE             LPFORMATETC describing how we want this
  712.  *                  rendered.
  713.  *  ppo             PPATRONOBJECT with placement data.
  714.  *  dwData          DWORD extra data to pass to the tenant.
  715.  *
  716.  * Return Value:
  717.  *  None
  718.  */
  719.  
  720. BOOL CPage::TenantCreate(TENANTTYPE tType, LPVOID pv
  721.     , LPFORMATETC pFE, PPATRONOBJECT ppo, DWORD dwData)
  722.     {
  723.     PCTenant    pTenant;
  724.     UINT        uRet;
  725.     int         x, y;
  726.     int         h, v;
  727.     POINTL      ptl;
  728.     SIZEL       szl;
  729.     RECTL       rcl;
  730.     RECT        rc;
  731.  
  732.     //New tenants go at top of the pile; zero index to TenantAdd.
  733.     if (!TenantAdd(0, m_dwIDNext, &pTenant))
  734.         return FALSE;
  735.  
  736.     uRet=pTenant->Create(tType, pv, pFE, &ptl, &szl, m_pIStorage
  737.         , ppo, dwData);
  738.  
  739.     if (CREATE_FAILED==uRet)
  740.         {
  741.         //Reverse Create AND TenantAdd
  742.         SendMessage(m_hWndTenantList, LB_DELETESTRING, 0, 0L);
  743.         pTenant->Destroy(m_pIStorage);
  744.  
  745.         pTenant->Release();
  746.         return FALSE;
  747.         }
  748.  
  749.     m_dwIDNext++;
  750.     m_cTenants++;
  751.  
  752.     if (NULL!=m_pTenantCur)
  753.         m_pTenantCur->Select(FALSE);
  754.  
  755.     m_iTenantCur=0;             //First one in the list now.
  756.     m_pTenantCur=pTenant;
  757.  
  758.     //Tell the tenant where it lives, default is (0,0) in print area
  759.     x=LOMETRIC_BORDER+m_pPG->m_xMarginLeft;
  760.     y=-LOMETRIC_BORDER-m_pPG->m_yMarginTop;
  761.  
  762.     h=x;
  763.     v=y;
  764.  
  765.     if (CREATE_PLACEDOBJECT==uRet)
  766.         {
  767.         SetRect(&rc, 3*CXYHANDLE, 3*CXYHANDLE, 0, 0);
  768.         RectConvertMappings(&rc, NULL, FALSE);
  769.  
  770.         //Make sure place point is on page, otherwise go to (0,0)
  771.         if (((int)ptl.x > x)
  772.             && ((int)ptl.x < x+(int)m_pPG->m_cx-rc.left))
  773.             x=(int)ptl.x;
  774.  
  775.         //m_pPG->m_cy is absolute
  776.         if (((int)ptl.y < y)
  777.             && ((int)ptl.y > y-(int)m_pPG->m_cy-rc.top))
  778.             y=(int)ptl.y;
  779.         }
  780.  
  781.     //Bounds check size of the object and fit to page as necessary.
  782.     if (x+(int)szl.cx > (int)(h+m_pPG->m_cx))
  783.         szl.cx=h+m_pPG->m_cx-x;
  784.  
  785.     //Remember that szl we get from Create is absolute
  786.     if (y-(int)szl.cy < (int)(v-m_pPG->m_cy))
  787.         szl.cy=-(int)(v-m_pPG->m_cy-y);
  788.  
  789.     SETRECTL(rcl, x, y, x+szl.cx, y-szl.cy);
  790.     m_pTenantCur->RectSet(&rcl, FALSE, TRUE);
  791.  
  792.     //Force a repaint on this new guy
  793.     m_pTenantCur->Invalidate();
  794.     UpdateWindow(m_hWnd);
  795.  
  796.     m_pTenantCur->Select(TRUE);
  797.  
  798.     //Make sure this new tenant knows about showing it's type.
  799.     m_pTenantCur->ShowObjectType(m_pPG->m_fShowTypes);
  800.  
  801.     //CHAPTER21MOD
  802.     //New tenants must know the available monikers
  803.     if (NULL!=m_pmkFile)
  804.         {
  805.         LPBC        pbc;
  806.         LPMONIKER   pmkPage;
  807.         TCHAR       szTemp[32];
  808.         LPTSTR      pszFile;
  809.  
  810.        #ifdef WIN32ANSI
  811.         OLECHAR     szW[32];
  812.  
  813.         GetStorageName(szW);
  814.         WideCharToMultiByte(CP_ACP, 0, szW, -1, szTemp, 32
  815.            , NULL, NULL);
  816.        #else
  817.         GetStorageName(szTemp);
  818.        #endif
  819.         CreateItemMoniker(TEXT("!"), szTemp, &pmkPage);
  820.  
  821.         //We can get the filename from our file moniker
  822.         CreateBindCtx(0, &pbc);
  823.        #ifdef WIN32ANSI
  824.         LPOLESTR    pszTemp;
  825.         m_pmkFile->GetDisplayName(pbc, NULL, &pszTemp);
  826.         pszFile=(LPTSTR)CoTaskMemAlloc(512);
  827.         WideCharToMultiByte(CP_ACP, 0, pszTemp, -1, pszFile
  828.            , 512, NULL, NULL);
  829.         CoTaskMemFree((void *)pszTemp);
  830.        #else
  831.         m_pmkFile->GetDisplayName(pbc, NULL, &pszFile);
  832.        #endif
  833.         pbc->Release();
  834.  
  835.         pTenant->NotifyOfRename(pszFile, m_pmkFile, pmkPage);
  836.         pmkPage->Release();
  837.         CoTaskMemFree((void *)pszFile);
  838.         }
  839.     //End CHAPTER21MOD
  840.  
  841.     //Activate new objects immediately and force a save on them
  842.     if (TENANTTYPE_EMBEDDEDOBJECT==tType)
  843.         {
  844.         m_pTenantCur->Activate(OLEIVERB_SHOW);
  845.         m_pTenantCur->Update();
  846.         }
  847.  
  848.     return TRUE;
  849.     }
  850.  
  851.  
  852.  
  853.  
  854.  
  855.  
  856. /*
  857.  * CPage::TenantDestroy
  858.  *
  859.  * Purpose:
  860.  *  Destroys the currently selected tenant on this page.
  861.  *
  862.  * Parameters:
  863.  *  None
  864.  *
  865.  * Return Value:
  866.  *  None
  867.  */
  868.  
  869. BOOL CPage::TenantDestroy(void)
  870.     {
  871.     if (NULL==m_pTenantCur)
  872.         {
  873.         MessageBeep(0);
  874.         return FALSE;
  875.         }
  876.  
  877.     SendMessage(m_hWndTenantList, LB_DELETESTRING
  878.         , m_iTenantCur, 0L);
  879.  
  880.     m_pTenantCur->Invalidate();
  881.     m_pTenantCur->Destroy(m_pIStorage);
  882.     m_pTenantCur->Release();
  883.     m_pTenantCur=NULL;
  884.  
  885.     //Update counts, etc., and select the next tenant in the list.
  886.     if (m_iTenantCur==m_cTenants-1)
  887.         m_iTenantCur--;
  888.  
  889.     if (0==--m_cTenants)
  890.         m_pTenantCur=NULL;
  891.     else
  892.         {
  893.         TenantGet(m_iTenantCur, &m_pTenantCur, TRUE);
  894.         m_pTenantCur->Select(TRUE);
  895.         }
  896.  
  897.     UpdateWindow(m_hWnd);
  898.     return TRUE;
  899.     }
  900.  
  901.  
  902.  
  903.  
  904.  
  905. /*
  906.  * CPage::TenantClip
  907.  *
  908.  * Purpose:
  909.  *  Copies or cuts the currently selected tenant to the clipoard.
  910.  *
  911.  * Parameters:
  912.  *  fCut            BOOL TRUE to cut the object, FALSE to copy.
  913.  *
  914.  * Return Value:
  915.  *  BOOL            TRUE if successful, FALSE otherwise.
  916.  */
  917.  
  918. BOOL CPage::TenantClip(BOOL fCut)
  919.     {
  920.     LPDATAOBJECT    pIDataObject;
  921.     BOOL            fRet=FALSE;
  922.  
  923.     if (NULL==m_pTenantCur)
  924.         return FALSE;
  925.  
  926.     /*
  927.      * To perform a data transfer operation, we need to create a
  928.      * data object with the selected object's data inside. To do
  929.      * this we CoCreateInstance on CLSID_DataTransferObject
  930.      * (Also implemented in this chapter), retrieve data from the
  931.      * object we have, stuff that data into the transfer object,
  932.      * then stick that object on the clipboard.
  933.      *
  934.      * Since we'll want an identical object at other times, like for
  935.      * drag-drop, we use a private function to actually create it.
  936.      */
  937.  
  938.     pIDataObject=TransferObjectCreate(NULL);
  939.  
  940.     if (NULL!=pIDataObject)
  941.         {
  942.         if (SUCCEEDED(OleSetClipboard(pIDataObject)))
  943.             {
  944.             if (fCut)
  945.                 TenantDestroy();
  946.  
  947.             fRet=TRUE;
  948.             }
  949.  
  950.         pIDataObject->Release();
  951.         }
  952.  
  953.     return fRet;
  954.     }
  955.  
  956.  
  957.  
  958.  
  959.  
  960. /*
  961.  * CPage::FQueryObjectSelected
  962.  *
  963.  * Purpose:
  964.  *  Returns whether or not there is an object selected on this
  965.  *  page for Cut, Copy, Delete functions.
  966.  *
  967.  * Parameters:
  968.  *  hMenu           HMENU of the Edit menu.
  969.  *
  970.  * Return Value:
  971.  *  BOOL            TRUE if we have an object, FALSE otherwise.
  972.  */
  973.  
  974. BOOL CPage::FQueryObjectSelected(HMENU hMenu)
  975.     {
  976.     HMENU       hMenuTemp;
  977.  
  978.     /*
  979.      * This will only be called on WM_INITMENUPOPUP, we'll also
  980.      * use this function to create the Verb menu for this object.
  981.      */
  982.     if (NULL!=m_pTenantCur)
  983.         {
  984.         m_pTenantCur->AddVerbMenu(hMenu, MENUPOS_OBJECT);
  985.         return TRUE;
  986.         }
  987.  
  988.     OleUIAddVerbMenu(NULL, NULL, hMenu, MENUPOS_OBJECT
  989.         , IDM_VERBMIN, IDM_VERBMAX, FALSE, 0, &hMenuTemp);
  990.  
  991.     return FALSE;
  992.     }
  993.  
  994.  
  995.  
  996.  
  997. /*
  998.  * CPage::ActivateObject
  999.  *
  1000.  * Purpose:
  1001.  *  Executes a verb on the currently selected object.
  1002.  *
  1003.  * Parameters:
  1004.  *  iVerb           LONG of the selected verb.
  1005.  *
  1006.  * Return Value:
  1007.  *  None
  1008.  */
  1009.  
  1010. void CPage::ActivateObject(LONG iVerb)
  1011.     {
  1012.     if (NULL==m_pTenantCur)
  1013.         return;
  1014.  
  1015.     m_pTenantCur->Activate(iVerb);
  1016.     return;
  1017.     }
  1018.  
  1019.  
  1020.  
  1021.  
  1022. /*
  1023.  * CPage::ShowObjectTypes
  1024.  *
  1025.  * Purpose:
  1026.  *  Loops through all the tenants and tells each one to turn on or
  1027.  *  off the Show Objects features.
  1028.  *
  1029.  * Parameters:
  1030.  *  fShow           BOOL indicating to show the type or not.
  1031.  *
  1032.  * Return Value:
  1033.  *  None
  1034.  */
  1035.  
  1036. void CPage::ShowObjectTypes(BOOL fShow)
  1037.     {
  1038.     PCTenant    pTenant;
  1039.     UINT        i;
  1040.  
  1041.     for (i=0; i < m_cTenants; i++)
  1042.         {
  1043.         if (TenantGet(i, &pTenant, FALSE))
  1044.             pTenant->ShowObjectType(fShow);
  1045.         }
  1046.  
  1047.     return;
  1048.     }
  1049.  
  1050.  
  1051.  
  1052.  
  1053. /*
  1054.  * CPage::NotifyTenantsOfRename
  1055.  *
  1056.  * Purpose:
  1057.  *  Loops through all the tenants and informs each of the new
  1058.  *  document name.
  1059.  *
  1060.  * Parameters:
  1061.  *  pszFile         LPTSTR of the new filename.
  1062.  *  pmk             LPMONKIER for the new filename.
  1063.  *
  1064.  * Return Value:
  1065.  *  None
  1066.  */
  1067.  
  1068. void CPage::NotifyTenantsOfRename(LPTSTR pszFile, LPMONIKER pmk)
  1069.     {
  1070.     //CHAPTER21MOD
  1071.     PCTenant    pTenant;
  1072.     UINT        i;
  1073.     LPMONIKER   pmkPage;
  1074.     LPMONIKER   pmkAll;
  1075.     TCHAR       szTemp[32];
  1076.  
  1077.     //Save the file moniker
  1078.     ReleaseInterface(m_pmkFile);
  1079.     m_pmkFile=pmk;
  1080.     m_pmkFile->AddRef();
  1081.  
  1082.     //Create a page moniker to send to the tenants.
  1083.    #ifdef WIN32ANSI
  1084.     OLECHAR     szW[32];
  1085.  
  1086.     GetStorageName(szW);
  1087.     WideCharToMultiByte(CP_ACP, 0, szW, -1, szTemp, 32
  1088.        , NULL, NULL);
  1089.    #else
  1090.     GetStorageName(szTemp);
  1091.    #endif
  1092.     CreateItemMoniker(TEXT("!"), szTemp, &pmkPage);
  1093.  
  1094.     for (i=0; i < m_cTenants; i++)
  1095.         {
  1096.         if (TenantGet(i, &pTenant, FALSE))
  1097.             pTenant->NotifyOfRename(pszFile, pmk, pmkPage);
  1098.         }
  1099.  
  1100.     /*
  1101.      * Register a File!Page!"\" wildcard moniker as well.
  1102.      * Note that the page is already marked as running
  1103.      * with the document's wildcard moniker.
  1104.      */
  1105.     CreateItemMoniker(TEXT("!"), TEXT("\\"), &pmkAll);
  1106.  
  1107.     if (NULL!=pmkAll)
  1108.         {
  1109.         LPMONIKER   pmkWild=NULL;
  1110.         LPMONIKER   pmkTemp=NULL;
  1111.  
  1112.         INOLE_RevokeAsRunning(&m_dwRegROTWild);
  1113.         pmk->ComposeWith(pmkPage, FALSE, &pmkTemp);
  1114.  
  1115.         if (NULL!=pmkTemp)
  1116.             {
  1117.             pmkTemp->ComposeWith(pmkAll, FALSE, &pmkWild);
  1118.             pmkTemp->Release();
  1119.             }
  1120.  
  1121.         if (NULL!=pmkWild)
  1122.             {
  1123.             INOLE_RegisterAsRunning(this, pmkWild
  1124.                 , ROTFLAGS_REGISTRATIONKEEPSALIVE, &m_dwRegROTWild);
  1125.             pmkWild->Release();
  1126.             }
  1127.  
  1128.         pmkAll->Release();
  1129.         }
  1130.  
  1131.     //If anything held onto this, they AddRef'd
  1132.     pmkPage->Release();
  1133.     //End CHAPTER21MOD
  1134.     return;
  1135.     }
  1136.  
  1137.  
  1138. /*
  1139.  * CPage::ConvertObject
  1140.  *
  1141.  * Purpose:
  1142.  *  Invokes and handles the results of the Convert dialog
  1143.  *
  1144.  * Parameters:
  1145.  *  hWndFrame       HWND to use as the parent of the dialog.
  1146.  *  fNoServer       BOOL indicating if this was called because
  1147.  *                  ActivateObject failed.
  1148.  *
  1149.  * Return Value:
  1150.  *  None
  1151.  */
  1152.  
  1153. BOOL CPage::ConvertObject(HWND hWndFrame, BOOL fNoServer)
  1154.     {
  1155.     HRESULT         hr;
  1156.     OLEUICONVERT    ct;
  1157.     TENANTTYPE      tType;
  1158.     TENANTINFO      ti;
  1159.     UINT            uRet;
  1160.     HCURSOR         hCur;
  1161.     BOOL            fActivate=fNoServer;
  1162.     SIZEL           szl;
  1163.  
  1164.     if (NULL==m_pTenantCur)
  1165.         return FALSE;
  1166.  
  1167.     tType=m_pTenantCur->TypeGet();
  1168.  
  1169.     if (TENANTTYPE_STATIC==tType)
  1170.         {
  1171.         MessageBeep(0);
  1172.         return FALSE;
  1173.         }
  1174.  
  1175.     //Get object information we may want.
  1176.     m_pTenantCur->GetInfo(&ti);
  1177.  
  1178.     //Fill the structure.
  1179.     memset(&ct, 0, sizeof(ct));
  1180.     ct.cbStruct=sizeof(OLEUICONVERT);
  1181.     ct.hWndOwner=hWndFrame;
  1182.     ct.fIsLinkedObject=(TENANTTYPE_LINKEDOBJECT==tType);
  1183.     ct.dvAspect=ti.fe.dwAspect;
  1184.  
  1185.     m_pTenantCur->ObjectClassFormatAndIcon(&ct.clsid, &ct.wFormat
  1186.         , &ct.lpszUserType, &ct.hMetaPict, &ct.lpszDefLabel);
  1187.  
  1188.     uRet=OleUIConvert(&ct);
  1189.  
  1190.     if (OLEUI_OK==uRet)
  1191.         {
  1192.         //Potentially a long operation.
  1193.         hCur=SetCursor(LoadCursor(NULL, IDC_WAIT));
  1194.  
  1195.         //Prevent multiple repaints.
  1196.         m_pTenantCur->EnableRepaint(FALSE);
  1197.  
  1198.         //First, let's bother with the iconic aspect switch.
  1199.         if ((DVASPECT_ICON==ct.dvAspect && ct.fObjectsIconChanged)
  1200.             || ct.dvAspect!=ti.fe.dwAspect)
  1201.             {
  1202.             HGLOBAL     hMem=NULL;
  1203.  
  1204.             //Only pass non-NULL handle for icon aspects.
  1205.             if (DVASPECT_ICON==ct.dvAspect)
  1206.                 hMem=ct.hMetaPict;
  1207.  
  1208.             m_pPG->m_fDirty=m_pTenantCur->SwitchOrUpdateAspect(hMem
  1209.                 , FALSE);
  1210.             }
  1211.  
  1212.         //Now change types around.
  1213.         if ((CF_SELECTCONVERTTO & ct.dwFlags)
  1214.             && ct.clsid!=ct.clsidNew)
  1215.             {
  1216.             LPSTORAGE   pIStorage;
  1217.  
  1218.             /*
  1219.              * User selected convert, so:
  1220.              *  1.  Unload the object (back to passive state)
  1221.              *  2.  Call INOLE_DoConvert, which calls WriteClassStg,
  1222.              *      WriteFmtUserTypeStg, and SetConvertStg.
  1223.              *  3.  Reload the object and force an update.
  1224.              */
  1225.  
  1226.             //This should be the only close necessary.
  1227.             m_pTenantCur->StorageGet(&pIStorage);
  1228.             m_pTenantCur->Close(TRUE);
  1229.  
  1230.             hr=INOLE_DoConvert(pIStorage, ct.clsidNew);
  1231.  
  1232.             //Need to commit the new type and format
  1233.             pIStorage->Commit(STGC_DEFAULT);
  1234.             pIStorage->Release();
  1235.  
  1236.             if (SUCCEEDED(hr))
  1237.                 {
  1238.                 LPUNKNOWN   pObj;
  1239.                 LPOLEOBJECT pIOleObject;
  1240.  
  1241.                 //Reload and update.
  1242.                 m_pTenantCur->Load(m_pIStorage, &ti);
  1243.  
  1244.                 m_pTenantCur->ObjectGet(&pObj);
  1245.                 pObj->QueryInterface(IID_IOleObject
  1246.                     , (PPVOID)&pIOleObject);
  1247.                 pIOleObject->Update();
  1248.                 pIOleObject->Release();
  1249.                 pObj->Release();
  1250.                 }
  1251.  
  1252.             m_pPG->m_fDirty=TRUE;
  1253.             }
  1254.  
  1255.  
  1256.         if (CF_SELECTACTIVATEAS & ct.dwFlags)
  1257.             {
  1258.             /*
  1259.              * User selected Activate As, so:
  1260.              *  1.  Add the TreatAs entry in the registry
  1261.              *      through CoTreatAsClass
  1262.              *  2.  Unload all objects of the old CLSID that you
  1263.              *      have loaded.
  1264.              *  3.  Reload objects as desired
  1265.              *  4.  Activate the current object.
  1266.              */
  1267.  
  1268.             hr=CoTreatAsClass(ct.clsid, ct.clsidNew);
  1269.  
  1270.             if (SUCCEEDED(hr))
  1271.                 {
  1272.                 PCTenant    pTenant;
  1273.                 UINT        i;
  1274.  
  1275.                 for (i=0; i < m_cTenants; i++)
  1276.                     {
  1277.                     if (TenantGet(i, &pTenant, FALSE))
  1278.                         {
  1279.                         pTenant->GetInfo(&ti);
  1280.                         pTenant->Close(FALSE);
  1281.                         pTenant->Load(m_pIStorage, &ti);
  1282.                         }
  1283.                     }
  1284.  
  1285.                 fActivate=TRUE;
  1286.                 }
  1287.             }
  1288.  
  1289.         //These two steps insure the object knows of the size.
  1290.         m_pTenantCur->SizeGet(&szl, FALSE);
  1291.         m_pTenantCur->SizeSet(&szl, FALSE, TRUE);
  1292.  
  1293.         m_pTenantCur->EnableRepaint(TRUE);
  1294.         m_pTenantCur->Repaint();
  1295.  
  1296.         if (fActivate)
  1297.             m_pTenantCur->Activate(OLEIVERB_SHOW);
  1298.  
  1299.         SetCursor(hCur);
  1300.         }
  1301.  
  1302.     CoTaskMemFree((void*)ct.lpszUserType);
  1303.     INOLE_MetafilePictIconFree(ct.hMetaPict);
  1304.  
  1305.     return TRUE;
  1306.     }
  1307.  
  1308.  
  1309.  
  1310.  
  1311.  
  1312.  
  1313.  
  1314. /*
  1315.  * CPage::FQueryLinksInPage
  1316.  *
  1317.  * Purpose:
  1318.  *  Pass through to current page to see if there are any linked
  1319.  *  objects.
  1320.  *
  1321.  * Parameters:
  1322.  *  None
  1323.  *
  1324.  * Return Value:
  1325.  *  None
  1326.  */
  1327.  
  1328. BOOL CPage::FQueryLinksInPage()
  1329.     {
  1330.     PCTenant    pTenant;
  1331.     UINT        i;
  1332.     BOOL        fRet=FALSE;
  1333.  
  1334.     for (i=0; i < m_cTenants; i++)
  1335.         {
  1336.         if (TenantGet(i, &pTenant, FALSE))
  1337.             fRet |= (pTenant->TypeGet()==TENANTTYPE_LINKEDOBJECT);
  1338.         }
  1339.  
  1340.     return fRet;
  1341.     }
  1342.  
  1343.  
  1344.  
  1345.  
  1346.  
  1347. /*
  1348.  * CPage::TenantGet
  1349.  * (Protected)
  1350.  *
  1351.  * Purpose:
  1352.  *  Returns a tenant of a given index returning a BOOL so it's
  1353.  *  simple to use this function inside if statements.
  1354.  *
  1355.  * Parameters:
  1356.  *  iTenant         UINT tenant to retrieve 0 based.
  1357.  *  ppTenant        PCPage * in which to return the tenant
  1358.  *                  pointer
  1359.  *  fOpen           BOOL indicating if we should open this
  1360.  *                  tenant as well.
  1361.  *
  1362.  * Return Value:
  1363.  *  BOOL            TRUE if successful, FALSE otherwise.
  1364.  */
  1365.  
  1366. BOOL CPage::TenantGet(UINT iTenant, PCTenant *ppTenant
  1367.     , BOOL fOpen)
  1368.     {
  1369.     if (NULL==ppTenant)
  1370.         return FALSE;
  1371.  
  1372.     if (LB_ERR!=SendMessage(m_hWndTenantList, LB_GETTEXT
  1373.         , iTenant, (LONG)ppTenant))
  1374.         {
  1375.         if (fOpen)
  1376.             (*ppTenant)->Open(m_pIStorage);
  1377.  
  1378.         return TRUE;
  1379.         }
  1380.  
  1381.     return FALSE;
  1382.     }
  1383.  
  1384.  
  1385.  
  1386.  
  1387.  
  1388.  
  1389. //CHAPTER21MOD
  1390. /*
  1391.  * CPage::TenantGetFromID
  1392.  * (Protected)
  1393.  *
  1394.  * Purpose:
  1395.  *  Finds a tenant pointer from an ID for use from
  1396.  *  IOleItemContainer::GetObject
  1397.  *
  1398.  * Parameters:
  1399.  *  dwID            DWORD identifier of the tenant to find.
  1400.  *  ppTenant        PCTenant * in which to return the tenant
  1401.  *                  pointer
  1402.  *  fOpen           BOOL indicating if we should open this tenant as
  1403.  *                  well.
  1404.  *
  1405.  * Return Value:
  1406.  *  BOOL            TRUE if successful, FALSE otherwise.
  1407.  */
  1408.  
  1409. BOOL CPage::TenantGetFromID(DWORD dwID, PCTenant *ppTenant
  1410.     , BOOL fOpen)
  1411.     {
  1412.     UINT        i;
  1413.     PCTenant    pTenant;
  1414.  
  1415.     if (NULL==ppTenant)
  1416.         return FALSE;
  1417.  
  1418.     for (i=0; i < m_cTenants; i++)
  1419.         {
  1420.         if (!TenantGet(i, &pTenant, FALSE))
  1421.             continue;
  1422.  
  1423.         if (pTenant->GetID()==dwID)
  1424.             {
  1425.             if (fOpen)
  1426.                 pTenant->Open(m_pIStorage);
  1427.  
  1428.             *ppTenant=pTenant;
  1429.             return TRUE;
  1430.             }
  1431.         }
  1432.  
  1433.     return FALSE;
  1434.     }
  1435.  
  1436. //End CHAPTER21MOD
  1437.  
  1438.  
  1439.  
  1440.  
  1441. /*
  1442.  * CPage::TenantAdd
  1443.  * (Protected)
  1444.  *
  1445.  * Purpose:
  1446.  *  Creates a new tenant initialized to the given values.  The new
  1447.  *  tenants's storage is created if it does not already exist.  If
  1448.  *  fOpenStorage is set the the tenants's storage is opened and left
  1449.  *  opened.
  1450.  *
  1451.  * Parameters:
  1452.  *  iTenant         UINT Location at which to insert tenant; new
  1453.  *                  tenant is inserted after this position.  NOVALUE
  1454.  *                  for the end.
  1455.  *  dwID            DWORD ID for this tenant.
  1456.  *  ppNew           PCTenant * in which to store the new tenant.
  1457.  *
  1458.  * Return Value:
  1459.  *  BOOL            TRUE if the function succeeded, FALSE otherwise.
  1460.  */
  1461.  
  1462. BOOL CPage::TenantAdd(UINT iTenant, DWORD dwID
  1463.     , PCTenant *ppNew)
  1464.     {
  1465.     PCTenant    pTenant;
  1466.     LRESULT     lr;
  1467.  
  1468.     if (NULL!=ppNew)
  1469.         *ppNew=NULL;
  1470.  
  1471.     pTenant=new CTenant(dwID, m_hWnd, m_pPG);
  1472.  
  1473.     if (NULL==pTenant)
  1474.         return FALSE;
  1475.  
  1476.     //The constructor doesn't AddRef, so we need to.
  1477.     pTenant->AddRef();
  1478.  
  1479.     //Now try to add to the listbox.
  1480.     lr=SendMessage(m_hWndTenantList, LB_INSERTSTRING, iTenant
  1481.         , (LONG)pTenant);
  1482.  
  1483.     if (lr < 0)
  1484.         {
  1485.         pTenant->Release();
  1486.         return FALSE;
  1487.         }
  1488.  
  1489.     *ppNew=pTenant;
  1490.     return TRUE;
  1491.     }
  1492.  
  1493.  
  1494.  
  1495.  
  1496.  
  1497. /*
  1498.  * CPage::TransferObjectCreate
  1499.  * (Protected)
  1500.  *
  1501.  * Purpose:
  1502.  *  Creates a DataTransferObject and stuff the current selection's
  1503.  *  data into it.
  1504.  *
  1505.  * Parameters:
  1506.  *  pptl            PPOINTL containing the pick point in device
  1507.  *                  units applicable only to drag-drop; since
  1508.  *                  drag-drop is inherently mouse oriented, we use
  1509.  *                  device units for the point.  Ignored if NULL.
  1510.  *
  1511.  * Return Value:
  1512.  *  LPDATAOBJECT    Pointer to the object created, NULL on failure
  1513.  */
  1514.  
  1515. LPDATAOBJECT CPage::TransferObjectCreate(PPOINTL pptl)
  1516.     {
  1517.     LPDATAOBJECT    pIDataObject;
  1518.     LPDATAOBJECT    pIDataT;
  1519.     PPATRONOBJECT   ppo;
  1520.     RECTL           rcl;
  1521.     LPUNKNOWN       pObj;
  1522.     FORMATETC       fe;
  1523.     STGMEDIUM       stm;
  1524.     HRESULT         hr;
  1525.     //CHAPTER21MOD
  1526.     HGLOBAL         hMem;
  1527.     //End CHAPTER21MOD
  1528.  
  1529.     m_pTenantCur->ObjectGet(&pObj);
  1530.  
  1531.     hr=CoCreateInstance(CLSID_DataTransferObject, NULL
  1532.         , CLSCTX_INPROC_SERVER, IID_IDataObject
  1533.         , (PPVOID)&pIDataObject);
  1534.  
  1535.     if (FAILED(hr))
  1536.         return NULL;
  1537.  
  1538.     //Go get the data we should hold on to.
  1539.     hr=pObj->QueryInterface(IID_IDataObject, (PPVOID)&pIDataT);
  1540.  
  1541.     if (FAILED(hr))
  1542.         {
  1543.         pIDataObject->Release();
  1544.         pObj->Release();
  1545.         return NULL;
  1546.         }
  1547.  
  1548.     //Copy from known obj into transfer obj.  Ordering is important!
  1549.  
  1550.     //Generate placeable object structure
  1551.     stm.tymed=TYMED_HGLOBAL;
  1552.     stm.pUnkForRelease=NULL;
  1553.     stm.hGlobal=GlobalAlloc(GHND, sizeof(PATRONOBJECT));
  1554.  
  1555.     if (NULL==stm.hGlobal)
  1556.         {
  1557.         pIDataObject->Release();
  1558.         pObj->Release();
  1559.         return NULL;
  1560.         }
  1561.  
  1562.     ppo=(PPATRONOBJECT)GlobalLock(stm.hGlobal);
  1563.  
  1564.     m_pTenantCur->SizeGet(&ppo->szl, FALSE);
  1565.     ppo->szl.cy=-ppo->szl.cy; //Negate to make absolute size
  1566.  
  1567.     m_pTenantCur->RectGet(&rcl, FALSE);
  1568.     ppo->ptl.x=rcl.left;
  1569.     ppo->ptl.y=rcl.top;
  1570.  
  1571.     if (NULL==pptl)
  1572.         {
  1573.         ppo->ptlPick.x=0;
  1574.         ppo->ptlPick.y=0;
  1575.         }
  1576.     else
  1577.         ppo->ptlPick=*pptl;
  1578.  
  1579.     m_pTenantCur->FormatEtcGet(&ppo->fe, FALSE);
  1580.  
  1581.     //If this is a linked object, just copy a presentation
  1582.     if (TENANTTYPE_LINKEDOBJECT==m_pTenantCur->TypeGet())
  1583.         m_pTenantCur->FormatEtcGet(&ppo->fe, TRUE);
  1584.  
  1585.     SETDefFormatEtc(fe, m_pPG->m_cf, TYMED_HGLOBAL);
  1586.     pIDataObject->SetData(&fe, &stm, TRUE);
  1587.  
  1588.     /*
  1589.      * Here now we have to include CFSTR_EMBEDDEDOBJECT and
  1590.      * CFSTR_OBJECTDESCRIPTOR when what we have selected is, in
  1591.      * fact, a compound document object.  We'll just ask the tenant
  1592.      * to set these in pIDataObject since it knows what the object.
  1593.      * If we copy embedded object data, make sure the PATRONOBJECT
  1594.      * structure has the right format in it as well.
  1595.      */
  1596.     m_pTenantCur->CopyEmbeddedObject(pIDataObject, &ppo->fe, pptl);
  1597.     //CHAPTER21MOD
  1598.     hMem=stm.hGlobal;
  1599.     //End CHAPTER21MOD
  1600.  
  1601.     //Copy the actual presentation.
  1602.     m_pTenantCur->FormatEtcGet(&fe, TRUE);
  1603.     pIDataT->GetData(&fe, &stm);
  1604.     pIDataObject->SetData(&fe, &stm, TRUE);
  1605.  
  1606.     //CHAPTER21MOD
  1607.     //Copy a link to this tenant if it's embedded
  1608.     m_pTenantCur->CopyLinkedObject(pIDataObject, &ppo->fe, pptl);
  1609.     GlobalUnlock(hMem); //ppo
  1610.     //End CHAPTER21MOD
  1611.  
  1612.     pIDataT->Release();
  1613.     pObj->Release();
  1614.     return pIDataObject;    //Caller now responsible
  1615.     }
  1616.