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

Windows编程

开发平台:

Visual C++

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