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

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