PAGE.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:44k
源码类别:

Windows编程

开发平台:

Visual C++

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