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 / chap24 / patron / page.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  44.2 KB  |  1,887 lines

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