home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / com / tutsamp / perclien / guidraw.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-30  |  46.0 KB  |  1,496 lines

  1. /*+==========================================================================
  2.   File:      GUIDRAW.CPP
  3.  
  4.   Summary:   Implementation file for the CGuiDraw C++ class. A GuiDraw is
  5.              a C++ object that is anchored to the Windows GUI (Graphical
  6.              User Interface) environment--it retains knowledge of window
  7.              handles and device contexts in the current application. This
  8.              GuiDraw object relies on a drawing object for storage of the
  9.              drawing data. This drawing page object (a CODrawPage object)
  10.              is instantiated as a COM object in a separate In-process
  11.              server, PERDRAW.
  12.  
  13.              For a comprehensive tutorial code tour of GUIDRAW's contents
  14.              and offerings see the tutorial PERCLIEN.HTM file. For more
  15.              specific technical details on the internal workings see the
  16.              comments dispersed throughout the GUIDRAW source code.
  17.  
  18.   Classes:   CGuiDraw.
  19.  
  20.   Origin:    5-20-97: atrent - Editor inheritance from GUIPAPER.CPP in the
  21.              STOCLIEN source.
  22.  
  23. ----------------------------------------------------------------------------
  24.   This file is part of the Microsoft COM Tutorial Code Samples.
  25.  
  26.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  27.  
  28.   This source code is intended only as a supplement to Microsoft
  29.   Development Tools and/or on-line documentation.  See these other
  30.   materials for detailed information regarding Microsoft code samples.
  31.  
  32.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  33.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  34.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  35.   PARTICULAR PURPOSE.
  36. ==========================================================================+*/
  37.  
  38. /*--------------------------------------------------------------------------
  39.   We include WINDOWS.H for all Win32 applications.
  40.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  41.   We include OLECTL.H because it has definitions for connectable objects.
  42.   We include COMMDLG.H because we will be using the Open File,
  43.     Choose Color, and potentially other Common dialogs.
  44.   We include APPUTIL.H because we will be building this application using
  45.     the convenient Virtual Window and Dialog classes and other
  46.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  47.   We include IPAGES.H and PAGEGUID.H for the common drawing-related
  48.     Interface class, GUID, and CLSID specifications.
  49.   We include RESDEF.H because it has the resource definitions specific
  50.     to this application.
  51.   We include GUIDRAW.H because it has the C++ class used for GUI display
  52.     of the drawing page.
  53.   We include DRAWSINK.H because it has the C++ class used for the sink that
  54.     receives event notifications from the CODrawPage object in the server.
  55. ---------------------------------------------------------------------------*/
  56. #include <windows.h>
  57. #include <ole2.h>
  58. #include <olectl.h>
  59. #include <apputil.h>
  60. #include <ipages.h>
  61. #include <pageguid.h>
  62. #include "resdef.h"
  63. #include "guidraw.h"
  64. #include "drawsink.h"
  65.  
  66.  
  67. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  68.   Method:   CGuiDraw::CGuiDraw
  69.  
  70.   Summary:  Constructor.
  71.  
  72.   Args:     void
  73.  
  74.   Returns:  void
  75. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  76. CGuiDraw::CGuiDraw(HINSTANCE hInst, HWND hWndApp, INT iPage)
  77. {
  78.   m_hInst           = hInst;
  79.   m_hWndApp         = hWndApp;
  80.   m_iPage           = iPage;
  81.   m_hWnd            = NULL;
  82.   m_wszDataName[0]  = 0;
  83.   m_pIStorage_Root  = NULL;
  84.   m_pIStorage_Page  = NULL;
  85.   m_pIDrawPage      = NULL;
  86.   m_hDC             = NULL;
  87.   m_hPen            = NULL;
  88.   m_nInkWidth       = INK_THIN;
  89.   m_crInkColor      = RGB(0,0,0);
  90.   m_bInkSaving      = FALSE;
  91.   m_bInking         = FALSE;
  92.   m_bPainting       = FALSE;
  93.   m_OldPos.x        = 0;
  94.   m_OldPos.y        = 0;
  95.   m_pCODrawPageSink = NULL;
  96.   m_dwDrawPageSink  = 0;
  97.  
  98.   // Save the ClassID of DrawPages using an overloaded '=' operator.
  99.   m_CidDrawPage = CLSID_DrawPage;
  100. }
  101.  
  102.  
  103. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  104.   Method:   CGuiDraw::~CGuiDraw
  105.  
  106.   Summary:  Destructor.
  107.  
  108.   Args:     void
  109.  
  110.   Returns:  void
  111. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  112. CGuiDraw::~CGuiDraw(void)
  113. {
  114.   // CGuiDraw is derived from CVirWindow which traps the WM_DESTROY
  115.   // message and causes a delete of CGuiDraw which in turn causes this
  116.   // destructor to run. The WM_DESTROY results when the window is destoyed
  117.   // after a close of the window.
  118.  
  119.   // Just to make sure, turn off Ink Saving.
  120.   m_bInkSaving = FALSE;
  121.  
  122.   // Disconnect the Sink in the client from the connection points in
  123.   // the server.
  124.   DisconnectSink();
  125.  
  126.   // Delete the Pen object.
  127.   if (m_hPen)
  128.     DeleteObject(m_hPen);
  129.  
  130.   // Release the interface held on the DrawPageSink object.
  131.   RELEASE_INTERFACE(m_pCODrawPageSink);
  132.  
  133.   // Destroy the existing CODrawPage by releasing the IDrawPage interface.
  134.   RELEASE_INTERFACE(m_pIDrawPage);
  135.  
  136.   // Release the interface held on the page's storage.
  137.   RELEASE_INTERFACE(m_pIStorage_Page);
  138.  
  139.   // Release the interface held on the root storage.
  140.   RELEASE_INTERFACE(m_pIStorage_Root);
  141.  
  142.   // Tell main app that this text page is gone.
  143.   PostMessage(m_hWndApp, WM_USER_PAGECLOSED, 0, m_iPage);
  144. }
  145.  
  146.  
  147. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  148.   Method:   CGuiDraw::Load
  149.  
  150.   Summary:  Load DrawPage data from the appropriate substorage under
  151.             the root storage. If there is no existing substorage then
  152.             create and initialize a new one.
  153.  
  154.   Args:     void.
  155.  
  156.   Returns:  HRESULT
  157.               Standard result code. NOERROR for success.
  158. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  159. HRESULT CGuiDraw::Load(void)
  160. {
  161.   HRESULT hr = E_FAIL;
  162.   IPersistStorage* pIPersistStorage;
  163.  
  164.   // Can't do anything without a root storage.
  165.   if (NULL != m_pIStorage_Root)
  166.   {
  167.     // Use the root IStorage to open the nested substorage for this
  168.     // particular DrawPage. Load the drawing data from the substorage.
  169.     // This load internally uses the IPersistStorage features in a
  170.     // created CODrawPage object to reconstitute the object from
  171.     // persistent storage. CODrawPage handles its own persistence that
  172.     // is client-controlled via its exposed IPersistStorage interface.
  173.  
  174.     // Open the single storage named by m_wszDataName. It is under
  175.     // the root storage. The client here is in charge of the underlying
  176.     // storage that the client asks CODrawPage to use.
  177.     hr = m_pIStorage_Root->OpenStorage(
  178.            m_wszDataName,
  179.            NULL,
  180.            STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  181.            NULL,
  182.            0,
  183.            &m_pIStorage_Page);
  184.     if (SUCCEEDED(hr))
  185.     {
  186.       // If the storage already exists, read the ClassID for the class of
  187.       // component objects that can deal with DrawPage data.
  188.       hr = ReadClassStg(m_pIStorage_Page, &m_CidDrawPage);
  189.       if (SUCCEEDED(hr))
  190.       {
  191.         // Now use the obtained Class ID to create a CODrawPage
  192.         // object. Initially ask for the IDrawPage interface.
  193.         hr = CoCreateInstance(
  194.                m_CidDrawPage,
  195.                NULL,
  196.                CLSCTX_INPROC_SERVER,
  197.                IID_IDrawPage,
  198.                (PPVOID)&m_pIDrawPage);
  199.         if (SUCCEEDED(hr))
  200.         {
  201.           // We have a new CODrawPage object. Now obtain the
  202.           // IPersistStorage interface on it. At this point in the
  203.           // client we are assuming that CODrawPage objects use only
  204.           // the IPersistStorage interface for their persistence.
  205.           hr = m_pIDrawPage->QueryInterface(
  206.                  IID_IPersistStorage,
  207.                  (PPVOID)&pIPersistStorage);
  208.           if (SUCCEEDED(hr))
  209.           {
  210.             // And as expected by this client, CODrawPage exposes
  211.             // the IPersistStorage interface. Now use this interface
  212.             // to ask the CODrawPage object to load the drawing data.
  213.             hr = pIPersistStorage->Load(m_pIStorage_Page);
  214.  
  215.             // Done with IPersistStorage for now so release it.
  216.             pIPersistStorage->Release();
  217.           }
  218.         }
  219.       }
  220.     }
  221.     else
  222.     {
  223.       // If there was no existing page substorage then create a new one.
  224.       hr = m_pIStorage_Root->CreateStorage(
  225.              m_wszDataName,
  226.              STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  227.              0,
  228.              0,
  229.              &m_pIStorage_Page);
  230.       if (SUCCEEDED(hr))
  231.       {
  232.         // Write the ClassID of the CODrawPage component object class.
  233.         hr = WriteClassStg(m_pIStorage_Page, m_CidDrawPage);
  234.         if (SUCCEEDED(hr))
  235.         {
  236.           // Now use the Class ID to create a CODrawPage
  237.           // object. Initially ask for the IDrawPage interface.
  238.           hr = CoCreateInstance(
  239.                  m_CidDrawPage,
  240.                  NULL,
  241.                  CLSCTX_INPROC_SERVER,
  242.                  IID_IDrawPage,
  243.                  (PPVOID)&m_pIDrawPage);
  244.           if (SUCCEEDED(hr))
  245.           {
  246.             // We have a new CODrawPage object. Now obtain the
  247.             // IPersistStorage interface on it. At this point in the
  248.             // client we are assuming that CODrawPage objects use only
  249.             // the IPersistStorage interface for their persistence.
  250.             hr = m_pIDrawPage->QueryInterface(
  251.                    IID_IPersistStorage,
  252.                    (PPVOID)&pIPersistStorage);
  253.             if (SUCCEEDED(hr))
  254.             {
  255.               // And as expected by this client, CODrawPage supports
  256.               // the IPersistStorage interface. Now use this interface
  257.               // to ask the CODrawPage object to initialize a new empty
  258.               // drawing page and save it into the substorage.
  259.               hr = pIPersistStorage->InitNew(m_pIStorage_Page);
  260.  
  261.               // Done with IPersistStorage for now so release it.
  262.               pIPersistStorage->Release();
  263.             }
  264.           }
  265.         }
  266.  
  267.         if (FAILED(hr))
  268.           m_pIStorage_Page->DestroyElement(m_wszDataName);
  269.       }
  270.     }
  271.   }
  272.  
  273.   return hr;
  274. }
  275.  
  276.  
  277. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  278.   Method:   CGuiDraw::OpenWin
  279.  
  280.   Summary:  Get CGuiDraw started. Make any subordinate objects, like
  281.             CODrawPage and get them started. Create the window. Read the
  282.             page's drawing data from the specified root storage using the
  283.             specified substorage name.
  284.  
  285.   Args:     IStorage* pIStorage_Root,
  286.               Root storage of the compound file containing the drawing
  287.               page.
  288.             WCHAR* pwszPageTitle,
  289.               The user-displayable title for the drawing page. Generally
  290.               used in the window's title bar.
  291.             WCHAR* pwszDataName)
  292.               The internal storage name of the substorage holding the
  293.               drawing page.
  294.  
  295.   Returns:  HRESULT
  296.               Standard result code. NOERROR for success.
  297. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  298. HRESULT CGuiDraw::OpenWin(
  299.           IStorage* pIStorage_Root,
  300.           WCHAR* pwszPageTitle,
  301.           WCHAR* pwszDataName)
  302. {
  303.   HRESULT hr = E_FAIL;
  304.   HWND hWnd = NULL;
  305.   CODrawPageSink* pCobSink = NULL;
  306.   HCURSOR hCurPrev;
  307.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  308.   TCHAR szTitle[32+PAGE_TITLE_SIZE];
  309.  
  310.   if (m_hInst && m_hWndApp && pwszDataName)
  311.   {
  312.     // Change cursor to the hour glass. This open could take awhile.
  313.     hCurPrev = SetCursor(hCurWait);
  314.  
  315.     // Set title string empty to start.
  316.     szTitle[0] = 0;
  317.  
  318.     lstrcpy(szTitle, TEXT(DRAW_WINDOW_NAME_STR));
  319. #if defined(UNICODE)
  320.     if (NULL != pwszPageTitle)
  321.       lstrcat(szTitle, pwszPageTitle);
  322.     lstrcpy(m_wszDataName, pwszDataName);
  323. #else
  324.     {
  325.       CHAR szAnsi[PAGE_TITLE_SIZE];
  326.  
  327.       // Convert PageTitle from Unicode to Ansi.
  328.       if (NULL != pwszPageTitle)
  329.       {
  330.         UcToAnsi(pwszPageTitle, szAnsi, PAGE_TITLE_SIZE);
  331.         lstrcat(szTitle, szAnsi);
  332.       }
  333.       // Copy/save DataName.
  334.       memcpy(m_wszDataName, pwszDataName, PAGE_NAME_SIZE*sizeof(WCHAR));
  335.     }
  336. #endif
  337.     // Create the main GuiDraw Window. Size the window reasonably. The
  338.     // Create method sets both m_hInst and m_hWnd.
  339.     hWnd = Create(
  340.              TEXT(DRAW_WINDOW_CLASS_NAME_STR),
  341.              szTitle,
  342.              WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
  343.                | WS_MAXIMIZEBOX | WS_THICKFRAME,
  344.              CW_USEDEFAULT,
  345.              CW_USEDEFAULT,
  346.              ::GetSystemMetrics(SM_CXSCREEN)*1/3,
  347.              ::GetSystemMetrics(SM_CYSCREEN)*1/3,
  348.              m_hWndApp,
  349.              NULL,
  350.              m_hInst);
  351.     if (NULL != hWnd)
  352.     {
  353.       // Get and save our private display Device Context.
  354.       m_hDC = GetDC(hWnd);
  355.  
  356.       // Save an interface pointer to the root IStorage and AddRef it.
  357.       m_pIStorage_Root = pIStorage_Root;
  358.       m_pIStorage_Root->AddRef();
  359.  
  360.       // Now use CGuiDraw::Load to load the drawing window from the named
  361.       // substorage under the root storage. This either opens and loads
  362.       // the existing substorage or creates a new one.
  363.       hr = Load();
  364.       if (SUCCEEDED(hr))
  365.       {
  366.         // We loaded or created the Drawing Page storage and have a
  367.         // CODrawPage component to manage the loaded drawing data.
  368.  
  369.         // Create the CODrawPageSink object to receive DrawPage events.
  370.         pCobSink = new CODrawPageSink(NULL, this);
  371.         if (NULL != pCobSink)
  372.         {
  373.           // Save a pointer to the CODrawPageSink IUnknown interface.
  374.           // AddRef because of this saved copy. Released in destructor.
  375.           m_pCODrawPageSink = pCobSink;
  376.           m_pCODrawPageSink->AddRef();
  377.  
  378.           // If we created and reconstituted a CODrawPage object from a
  379.           // persistent storage, then connect the sinks in the client
  380.           // to the connection sources in the server.
  381.           hr = ConnectSink();
  382.  
  383.           // Ensure the new window is shown on screen.
  384.           ::ShowWindow(m_hWnd, SW_SHOWNORMAL);
  385.           ::UpdateWindow(m_hWnd);
  386.         }
  387.         else
  388.           hr = E_OUTOFMEMORY;
  389.       }
  390.  
  391.       if (SUCCEEDED(hr))
  392.       {
  393.         // We can now allow ink saving as well.
  394.         m_bInkSaving = TRUE;
  395.       }
  396.       else
  397.       {
  398.         // If can't open, display an error dialog and close this window.
  399.         HrMsgId(m_hInst, m_hWnd, IDS_OPENDRAWPAGE, hr);
  400.         PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  401.       }
  402.     }
  403.  
  404.     // Set Cursor from hourglass back to what it was.
  405.     SetCursor(hCurPrev);
  406.   }
  407.  
  408.   return (hr);
  409. }
  410.  
  411.  
  412. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  413.   Method:   CGuiDraw::TopWin
  414.  
  415.   Summary:  Bring this GuiDraw window to the top over any other windows
  416.             that are on-screen.
  417.  
  418.   Args:     void.
  419.  
  420.   Returns:  HRESULT
  421.               Standard result code. NOERROR for success.
  422. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  423. HRESULT CGuiDraw::TopWin(
  424.           void)
  425. {
  426.   HRESULT hr = E_FAIL;
  427.  
  428.   if (IsWindow(m_hWnd))
  429.   {
  430.     BringWindowToTop(m_hWnd);
  431.     hr = NOERROR;
  432.   }
  433.  
  434.   return hr;
  435. }
  436.  
  437.  
  438. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  439.   Method:   CGuiDraw::ResizeWin
  440.  
  441.   Summary:  Resizes current drawing page rectangle.
  442.  
  443.   Args:     WORD wWidth
  444.               New window width. Max X coord.
  445.             WORD wHeight
  446.               New window height. Max Y coord.
  447.  
  448.   Returns:  HRESULT
  449.               Standard result code. NOERROR for success.
  450. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  451. HRESULT CGuiDraw::ResizeWin(
  452.                      WORD wWidth,
  453.                      WORD wHeight)
  454. {
  455.   HRESULT hr;
  456.  
  457.   // Store the new window size.
  458.   m_WinRect.right = wWidth;
  459.   m_WinRect.bottom = wHeight;
  460.  
  461.   // Tell CODrawPage about new size.
  462.   hr = m_pIDrawPage->Resize(wWidth, wHeight);
  463.  
  464.   return hr;
  465. }
  466.  
  467.  
  468. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  469.   Method:   CGuiDraw::ClearWin
  470.  
  471.   Summary:  Clear display window but don't delete drawn data.
  472.  
  473.   Args:     void.
  474.  
  475.   Returns:  HRESULT
  476.               Standard result code. NOERROR for success.
  477. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  478. HRESULT CGuiDraw::ClearWin(void)
  479. {
  480.   HRESULT hr = E_FAIL;
  481.   RECT WinRect;
  482.  
  483.   if(!m_bInking)
  484.   {
  485.     // Get our window's client area rectangle.
  486.     GetClientRect(m_hWnd, &WinRect);
  487.  
  488.     // Fill that rectangle with pixels of default white paint.
  489.     FillRect(m_hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
  490.  
  491.     hr = NOERROR;
  492.   }
  493.  
  494.   return hr;
  495. }
  496.  
  497.  
  498. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  499.   Method:   CGuiDraw::PaintWin
  500.  
  501.   Summary:  Clears window background and repaints the current drawing in
  502.             the window. Relies on connectable object technology. The
  503.             Redraw method in the CODrawPage object sends event
  504.             notifications to this client in order to achieve a repaint of
  505.             the drawing. CGuiDraw does not have a copy of the drawing
  506.             data--only the CODrawPage component in the server has that
  507.             in a potentially shared central location.
  508.  
  509.   Args:     void
  510.  
  511.   Returns:  HRESULT
  512.               Standard result code. NOERROR for success.
  513. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  514. HRESULT CGuiDraw::PaintWin(void)
  515. {
  516.   HRESULT hr = E_FAIL;
  517.   COLORREF crInkColor;
  518.   SHORT nInkWidth;
  519.  
  520.   if (m_pIDrawPage && !m_bPainting && !m_bInking)
  521.   {
  522.     m_bPainting = TRUE;
  523.     // Save and restore ink color and width since redraw otherwise
  524.     // ends up changing these values in CGuiDraw.
  525.     crInkColor = m_crInkColor;
  526.     nInkWidth = m_nInkWidth;
  527.     hr = m_pIDrawPage->Redraw();
  528.     m_nInkWidth = nInkWidth;
  529.     m_crInkColor = crInkColor;
  530.     m_bPainting = FALSE;
  531.   }
  532.  
  533.   return hr;
  534. }
  535.  
  536.  
  537. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  538.   Method:   CGuiDraw::InkSaving
  539.  
  540.   Summary:  Tell CGuiDraw whether ink data is being saved in the DrawPage
  541.             object.
  542.  
  543.   Args:     BOOL bInkSaving
  544.               TRUE means save in CODrawPage object; FALSE means not.
  545.  
  546.   Returns:  HRESULT
  547.               Standard result code. NOERROR for success.
  548. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  549. HRESULT CGuiDraw::InkSaving(
  550.           BOOL bInkSaving)
  551. {
  552.   m_bInkSaving = bInkSaving;
  553.  
  554.   return NOERROR;
  555. }
  556.  
  557.  
  558. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  559.   Method:   CGuiDraw::InkWidth
  560.  
  561.   Summary:  Changes current ink line width.
  562.  
  563.   Args:     SHORT nInkWidth
  564.               New ink line width in pixels.
  565.  
  566.   Returns:  HRESULT
  567.               Standard result code. NOERROR for success.
  568. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  569. HRESULT CGuiDraw::InkWidth(
  570.           SHORT nInkWidth)
  571. {
  572.   if (nInkWidth)
  573.     m_nInkWidth = nInkWidth;
  574.  
  575.   return NOERROR;
  576. }
  577.  
  578.  
  579. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  580.   Method:   CGuiDraw::InkColor
  581.  
  582.   Summary:  Changes current ink color.
  583.  
  584.   Args:     COLORREF crInkColor
  585.               RGB color ref value (eg, RGB(0,0,0) is black).
  586.  
  587.   Returns:  HRESULT
  588.               Standard result code. NOERROR for success.
  589. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  590. HRESULT CGuiDraw::InkColor(
  591.           COLORREF crInkColor)
  592. {
  593.   m_crInkColor = crInkColor;
  594.  
  595.   return NOERROR;
  596. }
  597.  
  598.  
  599. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  600.   Method:   CGuiDraw::InkStart
  601.  
  602.   Summary:  Starts an ink drawing sequence at the specified coordinates in
  603.             the current color with the current width.
  604.  
  605.   Args:     SHORT nX,
  606.               X coordinate in window rectangle of start point.
  607.             SHORT nY
  608.               Y coordinate in window rectangle of start point.
  609.  
  610.   Returns:  HRESULT
  611.               Standard result code. NOERROR for success.
  612. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  613. HRESULT CGuiDraw::InkStart(
  614.                      SHORT nX,
  615.                      SHORT nY)
  616. {
  617.   HRESULT hr = E_FAIL;
  618.  
  619.   // Start an ink drawing sequence only if one is not in progress.
  620.   if (!m_bInking)
  621.   {
  622.     // Remember start position.
  623.     m_OldPos.x = nX;
  624.     m_OldPos.y = nY;
  625.  
  626.     // Delete old pen.
  627.     if (m_hPen)
  628.       DeleteObject(m_hPen);
  629.  
  630.     // Create and select the new drawing pen.
  631.     m_hPen = CreatePen(PS_SOLID, m_nInkWidth, m_crInkColor);
  632.     SelectObject(m_hDC, m_hPen);
  633.  
  634.     hr = NOERROR;
  635.  
  636.     // Ask the DrawPage object to mark the start of the ink drawing
  637.     // sequence in the current ink color.
  638.     if (m_pIDrawPage && m_bInkSaving)
  639.     {
  640.       // Save this start point in CODrawPage.
  641.       hr = m_pIDrawPage->InkStart(nX, nY, m_nInkWidth, m_crInkColor);
  642.       // Capture the Mouse.
  643.       SetCapture(m_hWnd);
  644.       // Tell main app this page was changed.
  645.       PostMessage(m_hWndApp, WM_USER_PAGECHANGED, 0, m_iPage);
  646.     }
  647.  
  648.     // Set inking flag to TRUE.
  649.     m_bInking = TRUE;
  650.   }
  651.  
  652.   return hr;
  653. }
  654.  
  655.  
  656. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  657.   Method:   CGuiDraw::InkDraw
  658.  
  659.   Summary:  Draws and saves ink data during the currently active ink
  660.             drawing sequence.
  661.  
  662.   Args:     SHORT nX,
  663.               X coordinate in window rectangle of start point.
  664.             SHORT nY
  665.               Y coordinate in window rectangle of start point.
  666.  
  667.   Returns:  HRESULT
  668.               Standard result code. NOERROR for success.
  669. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  670. HRESULT CGuiDraw::InkDraw(
  671.                      SHORT nX,
  672.                      SHORT nY)
  673. {
  674.   if (m_bInking)
  675.   {
  676.     // Start this ink line at previous old position.
  677.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  678.  
  679.     // Assign new old position and draw the new line.
  680.     LineTo(m_hDC, m_OldPos.x = nX, m_OldPos.y = nY);
  681.  
  682.     // Ask the DrawPage object to save this data.
  683.     if (m_bInkSaving)
  684.       m_pIDrawPage->InkDraw(nX, nY);
  685.   }
  686.  
  687.   return NOERROR;
  688. }
  689.  
  690.  
  691. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  692.   Method:   CGuiDraw::InkStop
  693.  
  694.   Summary:  Stops the currently active ink drawing sequence.
  695.  
  696.   Args:     SHORT nX,
  697.               X coordinate in window rectangle of start point.
  698.             SHORT nY
  699.               Y coordinate in window rectangle of start point.
  700.  
  701.   Returns:  HRESULT
  702.               Standard result code. NOERROR for success.
  703. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  704. HRESULT CGuiDraw::InkStop(
  705.                      SHORT nX,
  706.                      SHORT nY)
  707. {
  708.   if (m_bInking)
  709.   {
  710.     // Start this ink line at previous old position.
  711.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  712.  
  713.     // Draw the last line.
  714.     LineTo(m_hDC, nX, nY);
  715.  
  716.     // Turn off inking.
  717.     m_bInking = FALSE;
  718.  
  719.     // Ask the DrawPage object to mark the stop of the ink drawing sequence.
  720.     if (m_bInkSaving)
  721.     {
  722.       m_pIDrawPage->InkStop(nX, nY);
  723.       // Free the mouse.
  724.       ReleaseCapture();
  725.     }
  726.   }
  727.  
  728.   return NOERROR;
  729. }
  730.  
  731.  
  732. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  733.   Method:   CGuiDraw::Renumber
  734.  
  735.   Summary:  Re-assign the current dynamic page number for this page.
  736.  
  737.   Args:     INT iPage
  738.               New page number.
  739.  
  740.   Returns:  HRESULT
  741.               Standard result code. NOERROR for success.
  742. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  743. HRESULT CGuiDraw::Renumber(
  744.           INT iPage)
  745. {
  746.   HRESULT hr = NOERROR;
  747.  
  748.   m_iPage = iPage;
  749.  
  750.   return hr;
  751. }
  752.  
  753.  
  754. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  755.   Method:   CGuiDraw::ReleasePage
  756.  
  757.   Summary:  Release all substorages for the draw page. Sets the CODrawPage
  758.             object to the HandsOff state.
  759.  
  760.   Args:     none.
  761.  
  762.   Returns:  HRESULT
  763.               Standard result code. NOERROR for success.
  764. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  765. HRESULT CGuiDraw::ReleasePage(
  766.           void)
  767. {
  768.   HRESULT hr = E_FAIL;
  769.   IPersistStorage* pIPersistStorage;
  770.  
  771.   if (NULL != m_pIDrawPage)
  772.   {
  773.     hr = m_pIDrawPage->QueryInterface(
  774.            IID_IPersistStorage,
  775.            (PPVOID)&pIPersistStorage);
  776.     if (SUCCEEDED(hr))
  777.     {
  778.       // Tell the CODrawPage object to release all hold it may have
  779.       // on storage.
  780.       hr = pIPersistStorage->HandsOffStorage();
  781.  
  782.       // Done with IPersistStorage for now so release it.
  783.       pIPersistStorage->Release();
  784.     }
  785.   }
  786.  
  787.   // Release the interface on the page's storage held by CGuiDraw.
  788.   RELEASE_INTERFACE(m_pIStorage_Page);
  789.  
  790.   // Release the interface held on the root storage by CGuiDraw.
  791.   RELEASE_INTERFACE(m_pIStorage_Root);
  792.  
  793.   return hr;
  794. }
  795.  
  796.  
  797. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  798.   Method:   CGuiDraw::RestorePage
  799.  
  800.   Summary:  Reopens all substorages for the draw page. Sets the CODrawPage
  801.             object back to the Scribble state by calling SaveCompleted.
  802.  
  803.   Args:     IStorage* pIStorage_Root
  804.               New root storage for the draw page.
  805.  
  806.   Returns:  HRESULT
  807.               Standard result code. NOERROR for success.
  808. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  809. HRESULT CGuiDraw::RestorePage(
  810.           IStorage* pIStorage_Root)
  811. {
  812.   HRESULT hr = E_POINTER;
  813.   IPersistStorage* pIPersistStorage;
  814.  
  815.   if (NULL != pIStorage_Root)
  816.   {
  817.     // Save the new interface pointer to the root IStorage and AddRef it.
  818.     m_pIStorage_Root = pIStorage_Root;
  819.     m_pIStorage_Root->AddRef();
  820.  
  821.     hr = m_pIDrawPage->QueryInterface(
  822.            IID_IPersistStorage,
  823.            (PPVOID)&pIPersistStorage);
  824.     if (SUCCEEDED(hr))
  825.     {
  826.       // ReOpen the single substorage named by m_wszDataName. This is
  827.       // the storage where the draw page is stored. It is under the
  828.       // root storage. The client here is in charge of the underlying
  829.       // storage that the client asks CODrawPage to use.
  830.       hr = m_pIStorage_Root->OpenStorage(
  831.              m_wszDataName,
  832.              NULL,
  833.              STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE,
  834.              NULL,
  835.              0,
  836.              &m_pIStorage_Page);
  837.       if (SUCCEEDED(hr))
  838.       {
  839.         // Tell the CODrawPage object that the client is done with its
  840.         // use of storage. Thus CODrawPage will re-open all necessary
  841.         // substorages and switch to the Scribble state.
  842.         hr = pIPersistStorage->SaveCompleted(m_pIStorage_Page);
  843.  
  844.         // Done with IPersistStorage for now so release it.
  845.         pIPersistStorage->Release();
  846.       }
  847.     }
  848.   }
  849.  
  850.   return hr;
  851. }
  852.  
  853.  
  854. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  855.   Method:   CGuiDraw::Close
  856.  
  857.   Summary:  Close this page window and thus the drawing page's substorages.
  858.  
  859.   Args:     void.
  860.  
  861.   Returns:  HRESULT
  862.               Standard result code. NOERROR for success.
  863. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  864. HRESULT CGuiDraw::Close(void)
  865. {
  866.   HRESULT hr = NOERROR;
  867.  
  868.   // Close the GuiDraw window. This will cause a delete of this CGuiDraw.
  869.   ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  870.  
  871.   return hr;
  872. }
  873.  
  874.  
  875. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  876.   Method:   CGuiDraw::Save
  877.  
  878.   Summary:  Uses CODrawPage's IPersistStorage interface to save the
  879.             drawing data in the drawing page's substorage.
  880.  
  881.   Args:     void.
  882.  
  883.   Returns:  HRESULT
  884.               Standard result code. NOERROR for success.
  885. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  886. HRESULT CGuiDraw::Save(
  887.           void)
  888. {
  889.   HRESULT hr = E_FAIL;
  890.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  891.   HCURSOR hCurPrev;
  892.   IPersistStorage* pIPersistStorage;
  893.  
  894.   if (NULL != m_pIStorage_Page && NULL != m_pIDrawPage)
  895.   {
  896.     // Change cursor to the hour glass.
  897.     hCurPrev = SetCursor(hCurWait);
  898.  
  899.     // Tell the CODrawPage object to save itself
  900.     // (via its IPersistStorage interface).
  901.     hr = m_pIDrawPage->QueryInterface(
  902.            IID_IPersistStorage,
  903.            (PPVOID)&pIPersistStorage);
  904.     if (SUCCEEDED(hr))
  905.     {
  906.       // Perform the save with fSameAsLoad==TRUE.
  907.       hr = pIPersistStorage->Save(m_pIStorage_Page, TRUE);
  908.       if (SUCCEEDED(hr))
  909.       {
  910.         // This is the general save by the client. In the case of
  911.         // this application the client does not need to do any other
  912.         // intermediate save or write operations to the compound file
  913.         // prior to informing the server that client save operations
  914.         // are completed.
  915.         hr = pIPersistStorage->SaveCompleted(NULL);
  916.       }
  917.  
  918.       // Done with IPersistStorage for now so release it.
  919.       pIPersistStorage->Release();
  920.     }
  921.  
  922.     // Set Cursor back to what it was.
  923.     SetCursor(hCurPrev);
  924.   }
  925.  
  926.   return hr;
  927. }
  928.  
  929.  
  930. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  931.   Method:   CGuiDraw::AskSave
  932.  
  933.   Summary:  Checks dirty flag (ie, if current drawing data was modified
  934.             and is out of sync with the DrawPage data stored in the page's
  935.             storage). If dirty, then ask user in simple dialog if he wants
  936.             to save new data. If he says yes, then save the current
  937.             DrawPage data into its storage.
  938.  
  939.   Args:     void.
  940.  
  941.   Returns:  INT
  942.               Value returned from the Win32 MessageBox function.
  943. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  944. INT CGuiDraw::AskSave(void)
  945. {
  946.   int iAns = IDNO;
  947.   HRESULT hr;
  948.   TCHAR szTitle[MAX_STRING_LENGTH];
  949.   TCHAR szAsk[MAX_STRING_LENGTH];
  950.   TCHAR szMsg[MAX_PATH + MAX_STRING_LENGTH];
  951.   IPersistStorage* pIPersistStorage;
  952.  
  953.   hr = m_pIDrawPage->QueryInterface(
  954.            IID_IPersistStorage,
  955.            (PPVOID)&pIPersistStorage);
  956.   if (SUCCEEDED(hr))
  957.   {
  958.     // Ask the DrawPage object if it is dirty.
  959.     if (S_FALSE != pIPersistStorage->IsDirty())
  960.     {
  961.       // The current data is dirty; ask user if he wants to save it.
  962.       if (LoadString(m_hInst, IDS_DRAW_PAGE, szTitle, MAX_STRING_LENGTH)
  963.           && LoadString(m_hInst, IDS_ASK_SAVE, szAsk, MAX_STRING_LENGTH))
  964.       {
  965.         lstrcpy(szMsg, szTitle);
  966.         lstrcat(szMsg, szAsk);
  967.         // Display AskSaveDlg to user. Ask if he wants to save.
  968.         iAns = MessageBox(
  969.                  m_hWnd,
  970.                  szMsg,
  971.                  szTitle,
  972.                  MB_YESNOCANCEL | MB_ICONEXCLAMATION);
  973.         switch (iAns)
  974.         {
  975.           case IDYES:
  976.             // Tell CGuiDraw to save itself to file storage.
  977.             Save();
  978.             break;
  979.           case IDNO:
  980.             // User clicked No. So don't save; abandon changes.
  981.             break;
  982.           case IDCANCEL:
  983.           default:
  984.             break;
  985.         }
  986.       }
  987.     }
  988.  
  989.     // Done with IPersistStorage for now so release it.
  990.     pIPersistStorage->Release();
  991.   }
  992.  
  993.   return iAns;
  994. }
  995.  
  996.  
  997. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  998.   Method:   CGuiDraw::Delete
  999.  
  1000.   Summary:  Delete the stored DrawPage. Shuts down the drawing page and
  1001.             window. Disconnects the Sink and does final release of the
  1002.             CODrawPage object.
  1003.  
  1004.   Args:     void.
  1005.  
  1006.   Returns:  HRESULT
  1007.               Standard result code. NOERROR for success.
  1008. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1009. HRESULT CGuiDraw::Delete(void)
  1010. {
  1011.   HRESULT hr = E_FAIL;
  1012.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  1013.   HCURSOR hCurPrev;
  1014.  
  1015.   if (NULL != m_pIStorage_Root && NULL != m_pIDrawPage)
  1016.   {
  1017.     // Change cursor to the hour glass.
  1018.     hCurPrev = SetCursor(hCurWait);
  1019.  
  1020.     // Clear the draw page.
  1021.     m_pIDrawPage->Clear(FALSE);
  1022.  
  1023.     // Destroy the entire drawpage's storage.
  1024.     hr = m_pIStorage_Root->DestroyElement(m_wszDataName);
  1025.  
  1026.     // Set Cursor back to what it was.
  1027.     SetCursor(hCurPrev);
  1028.   }
  1029.  
  1030.   return hr;
  1031. }
  1032.  
  1033.  
  1034. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1035.   Method:   CGuiDraw::Clear
  1036.  
  1037.   Summary:  Clear content of the drawing page and clear display window.
  1038.  
  1039.   Args:     void.
  1040.  
  1041.   Returns:  HRESULT
  1042.               Standard result code. NOERROR for success.
  1043. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1044. HRESULT CGuiDraw::Clear(void)
  1045. {
  1046.   HRESULT hr = E_FAIL;
  1047.  
  1048.   if (m_pIDrawPage)
  1049.     hr = m_pIDrawPage->Clear(TRUE);
  1050.   if (SUCCEEDED(hr))
  1051.   {
  1052.     ClearWin();
  1053.     // Tell main app if this page was changed.
  1054.     PostMessage(m_hWndApp, WM_USER_PAGECHANGED, 0, m_iPage);
  1055.   }
  1056.  
  1057.   return hr;
  1058. }
  1059.  
  1060.  
  1061. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1062.   Method:   CGuiDraw::PickColor
  1063.  
  1064.   Summary:  Uses the Choose Color common dialog to ask user for new
  1065.             Pen color. Return that new color.
  1066.  
  1067.   Args:     void.
  1068.  
  1069.   Returns:  COLORREF
  1070.               New chosen color.
  1071. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1072. COLORREF CGuiDraw::PickColor(void)
  1073. {
  1074.   COLORREF crNewColor = m_crInkColor;
  1075.   SHORT i;
  1076.  
  1077.   // Init the custom color array with gray colors.
  1078.   for (i=0; i<16; i++)
  1079.     m_acrCustColors[i] = RGB(i*16, i*16, i*16);
  1080.  
  1081.   // Init the Choose Color structure.
  1082.   m_ChooseColor.lStructSize = sizeof(CHOOSECOLOR);
  1083.   m_ChooseColor.hwndOwner = m_hWnd;
  1084.   m_ChooseColor.hInstance = (HWND) m_hInst;
  1085.   m_ChooseColor.rgbResult = m_crInkColor;
  1086.   m_ChooseColor.lpCustColors = (DWORD*) m_acrCustColors;
  1087.   m_ChooseColor.Flags = CC_PREVENTFULLOPEN | CC_RGBINIT;
  1088.   m_ChooseColor.lCustData = 0L;
  1089.   m_ChooseColor.lpfnHook = NULL;
  1090.   m_ChooseColor.lpTemplateName = NULL;
  1091.  
  1092.   if (ChooseColor(&m_ChooseColor))
  1093.     crNewColor = m_ChooseColor.rgbResult;
  1094.  
  1095.   return crNewColor;
  1096. }
  1097.  
  1098.  
  1099. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1100.   Method:   CGuiDraw::GetConnectionPoint
  1101.  
  1102.   Summary:  Internal private method to obtain a connection point interface.
  1103.  
  1104.   Args:     REFIID riid
  1105.               IID of the requested connection point Interface.
  1106.  
  1107.   Returns:  IConnectionPoint*
  1108.               Requested IConnectionPoint interface pointer. NULL if none.
  1109. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1110. IConnectionPoint* CGuiDraw::GetConnectionPoint(
  1111.                     REFIID riid)
  1112. {
  1113.   IConnectionPoint* pIConnectionPoint = NULL;
  1114.   IConnectionPointContainer* pIConnectionPointContainer = NULL;
  1115.   IConnectionPoint* pIConnPt;
  1116.   HRESULT hr;
  1117.  
  1118.   // First query the object for its Connection Point Container. This
  1119.   // essentially asks the object in the server if it is connectable.
  1120.   hr = m_pIDrawPage->QueryInterface(
  1121.          IID_IConnectionPointContainer,
  1122.          (PPVOID)&pIConnectionPointContainer);
  1123.   if SUCCEEDED(hr)
  1124.   {
  1125.     // Find the requested Connection Point. This AddRef's the
  1126.     // returned pointer.
  1127.     hr = pIConnectionPointContainer->FindConnectionPoint(riid, &pIConnPt);
  1128.     if (SUCCEEDED(hr))
  1129.       pIConnectionPoint = pIConnPt;
  1130.  
  1131.     RELEASE_INTERFACE(pIConnectionPointContainer);
  1132.   }
  1133.  
  1134.   return pIConnectionPoint;
  1135. }
  1136.  
  1137.  
  1138. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1139.   Method:   CGuiDraw::ConnectSink
  1140.  
  1141.   Summary:  Connect the client's DrawPageSink to the server's
  1142.             CODrawPage source.
  1143.  
  1144.   Args:     void
  1145.  
  1146.   Returns:  HRESULT
  1147.               Standard result code. NOERROR for success.
  1148. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1149. HRESULT CGuiDraw::ConnectSink(void)
  1150. {
  1151.   HRESULT hr = E_FAIL;
  1152.   DWORD dwKey;
  1153.   IConnectionPoint* pIConnectionPoint = NULL;
  1154.  
  1155.   if (!m_dwDrawPageSink)
  1156.   {
  1157.     // Get the DrawPage Sink connection point in the server.
  1158.     pIConnectionPoint = GetConnectionPoint(IID_IDrawPageSink);
  1159.     if (NULL != pIConnectionPoint)
  1160.     {
  1161.       // Connect the server's object to the DrawPage Sink in this client.
  1162.       hr = pIConnectionPoint->Advise(m_pCODrawPageSink, &dwKey);
  1163.       if (SUCCEEDED(hr))
  1164.         m_dwDrawPageSink = dwKey;
  1165.  
  1166.       RELEASE_INTERFACE(pIConnectionPoint);
  1167.     }
  1168.   }
  1169.  
  1170.   return hr;
  1171. }
  1172.  
  1173.  
  1174. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1175.   Method:   CGuiDraw::DisconnectSink
  1176.  
  1177.   Summary:  Disconnect the client DrawPageSink from the server
  1178.             CODrawPage source.
  1179.  
  1180.   Args:     void.
  1181.  
  1182.   Returns:  HRESULT
  1183.               Standard result code. NOERROR for success.
  1184. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1185. HRESULT CGuiDraw::DisconnectSink(void)
  1186. {
  1187.   HRESULT hr = E_FAIL;
  1188.   IConnectionPoint* pIConnectionPoint;
  1189.  
  1190.   if (m_dwDrawPageSink)
  1191.   {
  1192.     // Get the DrawPage Sink connection point.
  1193.     pIConnectionPoint = GetConnectionPoint(IID_IDrawPageSink);
  1194.     if (NULL != pIConnectionPoint)
  1195.     {
  1196.       // Disconnect the object in the server from the DrawPage Sink in
  1197.       // this client.
  1198.       hr = pIConnectionPoint->Unadvise(m_dwDrawPageSink);
  1199.       if (SUCCEEDED(hr))
  1200.         m_dwDrawPageSink = 0;
  1201.  
  1202.       RELEASE_INTERFACE(pIConnectionPoint);
  1203.     }
  1204.   }
  1205.  
  1206.   return hr;
  1207. }
  1208.  
  1209.  
  1210. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1211.   Method:   CGuiDraw::DoCommand
  1212.  
  1213.   Summary:  Dispatch and handle the main menu and other commands.
  1214.  
  1215.   Args:     WPARAM wParam,
  1216.               First message parameter.
  1217.             LPARAM lParam)
  1218.               Second message parameter.
  1219.  
  1220.   Returns:  LRESULT
  1221.               Standard Windows WindowProc return value.
  1222. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1223. LRESULT CGuiDraw::DoCommand(
  1224.           WPARAM wParam,
  1225.           LPARAM lParam)
  1226. {
  1227.   LRESULT lResult = FALSE;
  1228.   HMENU hMenu = ::GetMenu(m_hWnd);
  1229.  
  1230.   switch (LOWORD(wParam))
  1231.   {
  1232.     //----------------------------------------------------------------------
  1233.     // Handle Drawing Menu Commands.
  1234.     //----------------------------------------------------------------------
  1235.     case IDM_DRAW_SAVE:
  1236.       // Ask the GuiDraw object to save the current drawing.
  1237.       Save();
  1238.       break;
  1239.     case IDM_DRAW_CLEAR:
  1240.       // Ask GuiDraw object to clear its current content and if it can,
  1241.       // clear drawing display window.
  1242.       Clear();
  1243.       break;
  1244.     case IDM_DRAW_EXIT:
  1245.       // The user commands us to exit this page so we tell the
  1246.       // window to close itself.
  1247.       ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  1248.       break;
  1249.  
  1250.     //----------------------------------------------------------------------
  1251.     // Handle Pen Menu Commands.
  1252.     //----------------------------------------------------------------------
  1253.     case IDM_PEN_COLOR:
  1254.       // Select Ink drawing color using ChooseColor common dialog.
  1255.       {
  1256.         COLORREF crNewColor = PickColor();
  1257.         InkColor(crNewColor);
  1258.       }
  1259.       break;
  1260.     case IDM_PEN_THIN:
  1261.       // Select thin Ink width. Set menu check indicator.
  1262.       {
  1263.         HRESULT hr;
  1264.         HMENU hMenu  = ::GetMenu(m_hWnd);
  1265.         BOOL bOn = ::GetMenuState(
  1266.                        hMenu,
  1267.                        IDM_PEN_THIN,
  1268.                        MF_BYCOMMAND) & MF_CHECKED;
  1269.         if (!bOn)
  1270.         {
  1271.           hr = InkWidth(INK_THIN);
  1272.           if (SUCCEEDED(hr))
  1273.           {
  1274.             ::CheckMenuItem(
  1275.                 hMenu,
  1276.                 IDM_PEN_THIN,
  1277.                 MF_BYCOMMAND | MF_CHECKED);
  1278.             ::CheckMenuItem(
  1279.                 hMenu,
  1280.                 IDM_PEN_MEDIUM,
  1281.                 MF_BYCOMMAND | MF_UNCHECKED);
  1282.             ::CheckMenuItem(
  1283.                 hMenu,
  1284.                 IDM_PEN_THICK,
  1285.                 MF_BYCOMMAND | MF_UNCHECKED);
  1286.           }
  1287.         }
  1288.       }
  1289.       break;
  1290.     case IDM_PEN_MEDIUM:
  1291.       // Select medium Ink width. Set menu check indicator.
  1292.       {
  1293.         HRESULT hr;
  1294.         HMENU hMenu  = ::GetMenu(m_hWnd);
  1295.         BOOL bOn = ::GetMenuState(
  1296.                        hMenu,
  1297.                        IDM_PEN_MEDIUM,
  1298.                        MF_BYCOMMAND) & MF_CHECKED;
  1299.         if (!bOn)
  1300.         {
  1301.           hr = InkWidth(INK_MEDIUM);
  1302.           if (SUCCEEDED(hr))
  1303.           {
  1304.             ::CheckMenuItem(
  1305.                 hMenu,
  1306.                 IDM_PEN_MEDIUM,
  1307.                 MF_BYCOMMAND | MF_CHECKED);
  1308.             ::CheckMenuItem(
  1309.                 hMenu,
  1310.                 IDM_PEN_THICK,
  1311.                 MF_BYCOMMAND | MF_UNCHECKED);
  1312.             ::CheckMenuItem(
  1313.                 hMenu,
  1314.                 IDM_PEN_THIN,
  1315.                 MF_BYCOMMAND | MF_UNCHECKED);
  1316.           }
  1317.         }
  1318.       }
  1319.       break;
  1320.     case IDM_PEN_THICK:
  1321.       // Select thick Ink width. Set menu check indicator.
  1322.       {
  1323.         HRESULT hr;
  1324.         HMENU hMenu  = ::GetMenu(m_hWnd);
  1325.         BOOL bOn = ::GetMenuState(
  1326.                        hMenu,
  1327.                        IDM_PEN_THICK,
  1328.                        MF_BYCOMMAND) & MF_CHECKED;
  1329.         if (!bOn)
  1330.         {
  1331.           hr = InkWidth(INK_THICK);
  1332.           if (SUCCEEDED(hr))
  1333.           {
  1334.             ::CheckMenuItem(
  1335.                 hMenu,
  1336.                 IDM_PEN_THICK,
  1337.                 MF_BYCOMMAND | MF_CHECKED);
  1338.             ::CheckMenuItem(
  1339.                 hMenu,
  1340.                 IDM_PEN_MEDIUM,
  1341.                 MF_BYCOMMAND | MF_UNCHECKED);
  1342.             ::CheckMenuItem(
  1343.                 hMenu,
  1344.                 IDM_PEN_THIN,
  1345.                 MF_BYCOMMAND | MF_UNCHECKED);
  1346.           }
  1347.         }
  1348.       }
  1349.       break;
  1350.  
  1351.     //----------------------------------------------------------------------
  1352.     // Handle Help Menu Commands. Pass these commands on to main app.
  1353.     //----------------------------------------------------------------------
  1354.     case IDM_DHELP_CONTENTS:
  1355.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_CONTENTS, 0);
  1356.       break;
  1357.     case IDM_DHELP_TUTORIAL:
  1358.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_TUTORIAL, 0);
  1359.       break;
  1360.     case IDM_DHELP_TUTSERVER:
  1361.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_PERDRAW, 0);
  1362.       break;
  1363.     case IDM_DHELP_READSOURCE:
  1364.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_READSOURCE, 0);
  1365.       break;
  1366.     case IDM_DHELP_ABOUT:
  1367.       PostMessage(m_hWndApp, WM_COMMAND, (WPARAM) IDM_HELP_ABOUT, 0);
  1368.       break;
  1369.  
  1370.     default:
  1371.       // Defer all messages NOT handled here to the Default Window Proc.
  1372.       lResult = ::DefWindowProc(m_hWnd, WM_COMMAND, wParam, lParam);
  1373.       break;
  1374.   }
  1375.  
  1376.   return(lResult);
  1377. }
  1378.  
  1379.  
  1380. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1381.   Method:   CGuiDraw::WindowProc
  1382.  
  1383.   Summary:  Main window procedure for this window object.  See CVirWindow
  1384.             in the APPUTIL library (APPUTIL.H and APPUTIL.CPP) for details
  1385.             on how this method gets called by the global WindowProc.
  1386.  
  1387.   Args:     UINT uMsg,
  1388.               Windows message that is "sent" to this window.
  1389.             WPARAM wParam,
  1390.               First message parameter.
  1391.             LPARAM lParam)
  1392.               Second message parameter.
  1393.  
  1394.   Returns:  LRESULT
  1395.               Standard Windows WindowProc return value.
  1396. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1397. LRESULT CGuiDraw::WindowProc(
  1398.           UINT uMsg,
  1399.           WPARAM wParam,
  1400.           LPARAM lParam)
  1401. {
  1402.   LRESULT lResult = FALSE;
  1403.  
  1404.   switch (uMsg)
  1405.   {
  1406.     case WM_CREATE:
  1407.       break;
  1408.  
  1409.     case WM_ACTIVATE:
  1410.       // If we were newly activated by a mouse click then don't just sit
  1411.       // there--re-paint. This is needed when another window was topped
  1412.       // over part of this DrawPage window and the user then topped the
  1413.       // DrawPage using a mouse click on the visible part of this window.
  1414.       // In any case also let the windows default WindowProc handle this
  1415.       // message to set keyboard focus to the newly active window.
  1416.       if (WA_CLICKACTIVE == LOWORD(wParam))
  1417.         PaintWin();
  1418.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  1419.       break;
  1420.  
  1421.     case WM_MEASUREITEM:
  1422.       // Get setup for painting text in this window. For later evolution.
  1423.       {
  1424.         LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
  1425.         lpmis->itemHeight = m_tm.tmHeight + m_tm.tmExternalLeading;
  1426.         lpmis->itemWidth = m_wWidth;
  1427.         lResult = TRUE;
  1428.       }
  1429.  
  1430.     case WM_SIZE:
  1431.       // Handle a resize of this window.
  1432.       ResizeWin(LOWORD(lParam), HIWORD(lParam));
  1433.       break;
  1434.  
  1435.     case WM_PAINT:
  1436.       // If something major happened repaint the whole window.
  1437.       {
  1438.         PAINTSTRUCT ps;
  1439.  
  1440.         if(BeginPaint(m_hWnd, &ps))
  1441.         {
  1442.           PaintWin();
  1443.           EndPaint(m_hWnd, &ps);
  1444.         }
  1445.       }
  1446.       break;
  1447.  
  1448.     case WM_LBUTTONDOWN:
  1449.       // Start sequence of ink drawing to the paper.
  1450.       InkStart(LOWORD(lParam), HIWORD(lParam));
  1451.       break;
  1452.  
  1453.     case WM_MOUSEMOVE:
  1454.       // Draw inking sequence data.
  1455.       InkDraw(LOWORD(lParam), HIWORD(lParam));
  1456.       break;
  1457.  
  1458.     case WM_LBUTTONUP:
  1459.       // Stop an ink drawing sequence.
  1460.       InkStop(LOWORD(lParam), HIWORD(lParam));
  1461.       break;
  1462.  
  1463.     case WM_COMMAND:
  1464.       // Dispatch and handle any Menu and command messages received.
  1465.       lResult = DoCommand(wParam, lParam);
  1466.       break;
  1467.  
  1468.     case WM_CHAR:
  1469.       if (wParam == 0x1b)
  1470.       {
  1471.         // Exit this app if user hits ESC key.
  1472.         ::PostMessage(m_hWnd, WM_CLOSE, 0, 0);
  1473.       }
  1474.       break;
  1475.  
  1476.     case WM_CLOSE:
  1477.       // The user selected Close on the drawing window's System menu
  1478.       // or Exit on the File menu.
  1479.       // If there is data that has not been saved then ask user
  1480.       // if it should be saved. If user cancels then cancel the exit.
  1481.       if (IDCANCEL == AskSave())
  1482.         break;
  1483.     case WM_QUIT:
  1484.       // Bring the main app window to top when this drawing window goes away.
  1485.       BringWindowToTop(m_hWndApp);
  1486.       // If the app is being quit then close any associated help windows.
  1487.       // For future evolution.
  1488.     default:
  1489.       // Defer all messages NOT handled above to the Default Window Proc.
  1490.       lResult = ::DefWindowProc(m_hWnd, uMsg, wParam, lParam);
  1491.       break;
  1492.   }
  1493.  
  1494.   return(lResult);
  1495. }
  1496.