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 / stoclien / guipaper.cpp next >
Encoding:
C/C++ Source or Header  |  1997-08-30  |  32.3 KB  |  1,128 lines

  1. /*+==========================================================================
  2.   File:      GUIPAPER.CPP
  3.  
  4.   Summary:   Implementation file for the CGuiPaper C++ class. A GuiPaper
  5.              is a C++ object that displays mouse movement as free-form
  6.              drawing in the client area of a designated window (much like
  7.              common scribble sample programs except that COM technology is
  8.              used throughout to construct this functionality). CGuiPaper
  9.              is anchored to the Windows GUI (Graphical User Interface)
  10.              environment--it retains knowledge of window handles and
  11.              device contexts on the local machine. This GuiPaper object
  12.              relies on a virtual paper object for storage of the drawing
  13.              data. This virtual Paper object (a COPaper) is instantiated
  14.              as a COM object in a separate thread-safe In-process server,
  15.              STOSERVE.
  16.  
  17.              For a comprehensive tutorial code tour of GUIPAPER's contents
  18.              and offerings see the tutorial STOCLIEN.HTM file. For more
  19.              specific technical details on the internal workings see the
  20.              comments dispersed throughout the GUIPAPER source code.
  21.  
  22.   Classes:   CGuiPaper.
  23.  
  24.   Origin:    6-10-96: atrent - Editor inheritance from GUIBALL.CPP in the
  25.              CONCLIEN source.
  26.  
  27. ----------------------------------------------------------------------------
  28.   This file is part of the Microsoft COM Tutorial Code Samples.
  29.  
  30.   Copyright (C) Microsoft Corporation, 1997.  All rights reserved.
  31.  
  32.   This source code is intended only as a supplement to Microsoft
  33.   Development Tools and/or on-line documentation.  See these other
  34.   materials for detailed information regarding Microsoft code samples.
  35.  
  36.   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  37.   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  38.   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  39.   PARTICULAR PURPOSE.
  40. ==========================================================================+*/
  41.  
  42. /*--------------------------------------------------------------------------
  43.   We include WINDOWS.H for all Win32 applications.
  44.   We include OLE2.H because we will be calling the COM/OLE Libraries.
  45.   We include OLECTL.H because it has definitions for connectable objects.
  46.   We include COMMDLG.H because we will be using the Open File,
  47.     Choose Color, and potentially other Common dialogs.
  48.   We include TCHAR.H for general Unicode/Ansi prototype of utility
  49.     functions like _tsplitpath, etc.
  50.   We include APPUTIL.H because we will be building this application using
  51.     the convenient Virtual Window and Dialog classes and other
  52.     utility functions in the APPUTIL Library (ie, APPUTIL.LIB).
  53.   We include IPAPER.H and PAPGUIDS.H for the common Paper-related Interface
  54.     class, GUID, and CLSID specifications.
  55.   We include PAPFILE.H because it has the C++ class used for compound file
  56.     storage of Drawing Paper Data.
  57.   We include GUIPAPER.H because it has the C++ class used for GUI display
  58.     of the drawing Paper.
  59.   We include SINK.H because it has the C++ class used for the sink that
  60.     receives event notifications from the COPaper object in the server.
  61.   We include STOCLIEN.H because it has class and resource definitions
  62.     specific to this STOCLIEN application.
  63. ---------------------------------------------------------------------------*/
  64. #include <windows.h>
  65. #include <ole2.h>
  66. #include <olectl.h>
  67. #include <commdlg.h>
  68. #include <tchar.h>
  69. #include <apputil.h>
  70. #include <ipaper.h>
  71. #include <papguids.h>
  72. #include "papfile.h"
  73. #include "guipaper.h"
  74. #include "sink.h"
  75. #include "stoclien.h"
  76.  
  77.  
  78. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  79.   Method:   CGuiPaper::CGuiPaper
  80.  
  81.   Summary:  Constructor.
  82.  
  83.   Args:     void
  84.  
  85.   Modifies: ...
  86.  
  87.   Returns:  void
  88. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  89. CGuiPaper::CGuiPaper(void)
  90. {
  91.   m_hWnd       = NULL;
  92.   m_hInst      = NULL;
  93.   m_hDC        = NULL;
  94.   m_pIPaper    = NULL;
  95.   m_nLockKey   = 0;
  96.   m_hPen       = NULL;
  97.   m_nInkWidth  = INK_THIN;
  98.   m_crInkColor = RGB(0,0,0);
  99.   m_bInkSaving = FALSE;
  100.   m_bInking    = FALSE;
  101.   m_bPainting  = FALSE;
  102.   m_OldPos.x   = 0;
  103.   m_OldPos.y   = 0;
  104.   m_pCOPaperSink = NULL;
  105.   m_dwPaperSink = 0;
  106.   m_bDirty     = FALSE;
  107.   m_pPapFile   = NULL;
  108.  
  109.   // Init the file name for title display to <MyModule>.<EXT>.
  110.   if (GetModuleFileName(m_hInst, m_szFileName, MAX_PATH))
  111.   {
  112.     _tsplitpath(m_szFileName, NULL, NULL, m_szFileTitle, NULL);
  113.     lstrcat(m_szFileTitle, TEXT(PAP_FILE_EXT));
  114.   }
  115.  
  116.   // Ensure the m_szFileName OFN member variable string is NULL.
  117.   m_szFileName[0] = 0;
  118. }
  119.  
  120.  
  121. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  122.   Method:   CGuiPaper::~CGuiPaper
  123.  
  124.   Summary:  Destructor.
  125.  
  126.   Args:     void
  127.  
  128.   Modifies: ...
  129.  
  130.   Returns:  void
  131. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  132. CGuiPaper::~CGuiPaper(void)
  133. {
  134.   BOOL bOk = TRUE;
  135.  
  136.   if (m_pIPaper)
  137.   {
  138.     // Make sure we unlock the paper object.
  139.     m_pIPaper->Unlock(m_nLockKey);
  140.  
  141.     // Just to make sure, turn off Ink Saving.
  142.     m_bInkSaving = FALSE;
  143.  
  144.     // Disconnect all Sinks--currently only one: PaperSink. This officially
  145.     // stops all PaperSink notifications.
  146.     DisconnectPaperSink();
  147.  
  148.     // Delete the Pen object.
  149.     if (m_hPen)
  150.       DeleteObject(m_hPen);
  151.  
  152.     // Release the reference to the PaperSink object.
  153.     RELEASE_INTERFACE(m_pCOPaperSink);
  154.  
  155.     // Release the main interface pointer copy held in CGuiPaper.
  156.     RELEASE_INTERFACE(m_pIPaper);
  157.  
  158.     // Delete the CPapFile object.
  159.     DELETE_POINTER(m_pPapFile);
  160.   }
  161. }
  162.  
  163.  
  164. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  165.   Method:   CGuiPaper::Init
  166.  
  167.   Summary:  Get CGuiPaper started. Make any subordinate objects, like
  168.             COPaper and CPapFile, and get them started.
  169.  
  170.   Args:     HINSTANCE hInst
  171.               Handle to the application instance.
  172.             HWND hWnd
  173.               Handle of the display window. Part of what makes CGuiPaper
  174.               a GUI kind of thing.
  175.             TCHAR* pszCmdLineFile)
  176.               Pointer to file name for an initial file to load that was
  177.               specified on the app command line.
  178.  
  179.   Modifies: ...
  180.  
  181.   Returns:  BOOL
  182.               TRUE for success; FALSE for fail.
  183. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  184. BOOL CGuiPaper::Init(
  185.        HINSTANCE hInst,
  186.        HWND hWnd,
  187.        TCHAR* pszCmdLineFile)
  188. {
  189.   BOOL bOk = FALSE;
  190.   HRESULT hr;
  191.   BOOL bFirst;
  192.   COPaperSink* pCob = NULL;
  193.   HCURSOR hCurPrev;
  194.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  195.  
  196.   if (hInst && hWnd)
  197.   {
  198.     m_hInst = hInst;
  199.     m_hWnd = hWnd;
  200.  
  201.     // Get and save our private display Device Context.
  202.     m_hDC = GetDC(m_hWnd);
  203.  
  204.     // Change cursor to the hour glass. Init could take awhile.
  205.     hCurPrev = SetCursor(hCurWait);
  206.  
  207.     // Fill in the Open File Name Common Dialog's OPENFILENAME structure.
  208.     m_ofnFile.lStructSize = sizeof(OPENFILENAME);
  209.     m_ofnFile.hwndOwner = hWnd;
  210.     m_ofnFile.hInstance = hInst;
  211.     m_ofnFile.lpstrCustomFilter = NULL;
  212.     m_ofnFile.nMaxCustFilter = 0;
  213.     m_ofnFile.nFilterIndex = 1;
  214.     m_ofnFile.lpstrFile = m_szFileName;
  215.     m_ofnFile.nMaxFile = MAX_PATH;
  216.     m_ofnFile.lpstrInitialDir = TEXT(".");
  217.     m_ofnFile.lpstrFileTitle = m_szFileTitle;
  218.     m_ofnFile.nMaxFileTitle = MAX_PATH;
  219.     m_ofnFile.lpstrDefExt = TEXT(PAP_EXT);
  220.  
  221.     if (LoadString(m_hInst, IDS_OFN_PAPFILES, m_szFileFilter, MAX_PATH))
  222.       m_ofnFile.lpstrFilter = m_szFileFilter;
  223.     else
  224.       m_ofnFile.lpstrFilter = TEXT(OFN_DEFAULTFILES_STR);
  225.  
  226.     // Call COM service to create a COPaper instance. We are not
  227.     // aggregating it so we ask for its IPaper interface directly.
  228.     hr = CoCreateInstance(
  229.            CLSID_DllPaper,
  230.            NULL,
  231.            CLSCTX_INPROC_SERVER,
  232.            IID_IPaper,
  233.            (PPVOID)&m_pIPaper);
  234.     if (SUCCEEDED(hr))
  235.     {
  236.       // Init the COPaper object.
  237.       GetClientRect(hWnd, &m_WinRect);
  238.       hr = m_pIPaper->InitPaper(&m_WinRect, &bFirst);
  239.       if (SUCCEEDED(hr))
  240.       {
  241.         // We can now allow ink saving.
  242.         m_bInkSaving = TRUE;
  243.  
  244.         // If this is not first init then resize this client's window to
  245.         // match what the COPaper object is using for its size.
  246.         if (!bFirst)
  247.           SetWindowPos(
  248.             m_hWnd,
  249.             HWND_TOP,
  250.             0,
  251.             0,
  252.             m_WinRect.right,
  253.             m_WinRect.bottom,
  254.             SWP_NOMOVE | SWP_NOZORDER);
  255.  
  256.         // Create the PapFile object so we can store paper data in
  257.         // compound files.
  258.         m_pPapFile = new CPapFile;
  259.         if (NULL != m_pPapFile)
  260.         {
  261.           if (FileExist(pszCmdLineFile))
  262.           {
  263.             // If the user specified a file name on the app's command
  264.             // invocation line then use that name if it actually refers
  265.             // to an existing file. If not then ignore it.
  266.             lstrcpy(m_szFileName, pszCmdLineFile);
  267.             lstrcpy(m_szFileTitle, pszCmdLineFile);
  268.           }
  269.           else
  270.           {
  271.             // Build a path to where the default application .PAP file
  272.             // should be. It should be in the same directory as the .EXE
  273.             // as the appname with an .PAP extension.
  274.             MakeFamilyPath(m_hInst, m_szFileName, TEXT(PAP_FILE_EXT));
  275.           }
  276.  
  277.           // Init the CPapFile object. Init will do the AddRef on the
  278.           // m_pIPaper copy that is passed.
  279.           hr = m_pPapFile->Init(m_szFileName, m_pIPaper);
  280.           if (SUCCEEDED(hr))
  281.           {
  282.             // Create the COPaperSink object to receive COPaper events.
  283.             pCob = new COPaperSink(NULL, this);
  284.             if (NULL != pCob)
  285.             {
  286.               // Save a pointer to the COPaperSink IUnknown interface.
  287.               // AddRef because of this saved copy.
  288.               m_pCOPaperSink = pCob;
  289.               m_pCOPaperSink->AddRef();
  290.             }
  291.             else
  292.               hr = E_OUTOFMEMORY;
  293.           }
  294.         }
  295.         else
  296.           hr = E_OUTOFMEMORY;
  297.       }
  298.  
  299.       bOk = SUCCEEDED(hr);
  300.     }
  301.  
  302.     // Set Cursor back to what it was.
  303.     SetCursor(hCurPrev);
  304.   }
  305.  
  306.   return (bOk);
  307. }
  308.  
  309.  
  310. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  311.   Method:   CGuiPaper::DrawOn
  312.  
  313.   Summary:  Lock the paper object for drawing by this client.
  314.  
  315.   Args:     void.
  316.  
  317.   Modifies: ...
  318.  
  319.   Returns:  HRESULT
  320.               Standard result code. NOERROR for success.
  321. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  322. HRESULT CGuiPaper::DrawOn(void)
  323. {
  324.   HRESULT hr;
  325.  
  326.   // If we are not already locked for drawing then do so.
  327.   if (!m_nLockKey)
  328.     hr = m_pIPaper->Lock(&m_nLockKey);
  329.  
  330.   return hr;
  331. }
  332.  
  333.  
  334. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  335.   Method:   CGuiPaper::DrawOff
  336.  
  337.   Summary:  Unlock the paper object for drawing by this client.
  338.  
  339.   Args:     void.
  340.  
  341.   Modifies: ...
  342.  
  343.   Returns:  HRESULT
  344.               Standard result code. NOERROR for success.
  345. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  346. HRESULT CGuiPaper::DrawOff(void)
  347. {
  348.   HRESULT hr = NOERROR;
  349.  
  350.   // If we are not already unlocked for drawing then do so.
  351.   if (m_nLockKey)
  352.   {
  353.     hr = m_pIPaper->Unlock(m_nLockKey);
  354.     if (SUCCEEDED(hr))
  355.       m_nLockKey = 0;
  356.   }
  357.  
  358.   return hr;
  359. }
  360.  
  361.  
  362. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  363.   Method:   CGuiPaper::ClearWin
  364.  
  365.   Summary:  Clear display window but don't erase drawn data.
  366.  
  367.   Args:     void.
  368.  
  369.   Modifies: ...
  370.  
  371.   Returns:  HRESULT
  372.               Standard result code. NOERROR for success.
  373. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  374. HRESULT CGuiPaper::ClearWin(void)
  375. {
  376.   HRESULT hr = E_FAIL;
  377.   RECT  WinRect;
  378.  
  379.   if(!m_bInking)
  380.   {
  381.     // Get our window's client area rectangle.
  382.     GetClientRect(m_hWnd, &WinRect);
  383.  
  384.     // Fill that rectangle with pixels of default white paint.
  385.     FillRect(m_hDC, &WinRect, GETCLASSBRUSH(m_hWnd));
  386.  
  387.     hr = NOERROR;
  388.   }
  389.  
  390.   return hr;
  391. }
  392.  
  393.  
  394. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  395.   Method:   CGuiPaper::PaintWin
  396.  
  397.   Summary:  Clears window background and repaints the current drawing
  398.             on the paper. Relies on connectable object technology.
  399.             The Redraw method in the COPaper object sends event
  400.             notifications to this client in order to achieve a
  401.             repaint of the drawing.  CGuiPaper does not have a copy of
  402.             the drawing data--only the COPaper component in the server
  403.             has that.
  404.  
  405.   Args:     void
  406.  
  407.   Modifies: ...
  408.  
  409.   Returns:  HRESULT
  410.               Standard result code. NOERROR for success.
  411. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  412. HRESULT CGuiPaper::PaintWin(void)
  413. {
  414.   HRESULT hr = E_FAIL;
  415.   COLORREF crInkColor;
  416.   SHORT nInkWidth;
  417.  
  418.   if (m_pIPaper && !m_bPainting && !m_bInking)
  419.   {
  420.     m_bPainting = TRUE;
  421.     // Save and restore ink color and width since redraw otherwise
  422.     // ends up changing these values in CGuiPaper.
  423.     crInkColor = m_crInkColor;
  424.     nInkWidth = m_nInkWidth;
  425.     hr = m_pIPaper->Redraw(m_nLockKey);
  426.     m_nInkWidth = nInkWidth;
  427.     m_crInkColor = crInkColor;
  428.     m_bPainting = FALSE;
  429.   }
  430.  
  431.   return hr;
  432. }
  433.  
  434.  
  435. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  436.   Method:   CGuiPaper::Erase
  437.  
  438.   Summary:  Erase content of the drawing paper and clear display window.
  439.  
  440.   Args:     void.
  441.  
  442.   Modifies: ...
  443.  
  444.   Returns:  HRESULT
  445.               Standard result code. NOERROR for success.
  446. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  447. HRESULT CGuiPaper::Erase(void)
  448. {
  449.   HRESULT hr = E_FAIL;
  450.  
  451.   if(m_nLockKey)
  452.   {
  453.     if (m_pIPaper)
  454.       hr = m_pIPaper->Erase(m_nLockKey);
  455.     if (SUCCEEDED(hr))
  456.     {
  457.       ClearWin();
  458.       m_bDirty = TRUE;
  459.     }
  460.   }
  461.  
  462.   return hr;
  463. }
  464.  
  465.  
  466. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  467.   Method:   CGuiPaper::Resize
  468.  
  469.   Summary:  Resizes current drawing paper rectangle.
  470.  
  471.   Args:     WORD wWidth
  472.               New window width. Max X coord.
  473.             WORD wHeight
  474.               New window height. Max Y coord.
  475.  
  476.   Modifies: ...
  477.  
  478.   Returns:  HRESULT
  479.               Standard result code. NOERROR for success.
  480. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  481. HRESULT CGuiPaper::Resize(
  482.                      WORD wWidth,
  483.                      WORD wHeight)
  484. {
  485.   if(m_nLockKey)
  486.   {
  487.     // Store the new window size and mark dirty.
  488.     m_WinRect.right = wWidth;
  489.     m_WinRect.bottom = wHeight;
  490.     m_bDirty = TRUE;
  491.   }
  492.  
  493.   return NOERROR;
  494. }
  495.  
  496.  
  497. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  498.   Method:   CGuiPaper::InkSaving
  499.  
  500.   Summary:  Tell CGuiPaper whether ink data is being saved in the Paper
  501.             object.
  502.  
  503.   Args:     BOOL bInkSaving
  504.               TRUE means save in COPaper object; FALSE means not.
  505.  
  506.   Modifies: ...
  507.  
  508.   Returns:  HRESULT
  509.               Standard result code. NOERROR for success.
  510. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  511. HRESULT CGuiPaper::InkSaving(
  512.                      BOOL bInkSaving)
  513. {
  514.   m_bInkSaving = bInkSaving;
  515.  
  516.   return NOERROR;
  517. }
  518.  
  519.  
  520. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  521.   Method:   CGuiPaper::InkWidth
  522.  
  523.   Summary:  Changes current ink width.
  524.  
  525.   Args:     SHORT nInkWidth
  526.               New ink width in pixels.
  527.  
  528.   Modifies: ...
  529.  
  530.   Returns:  HRESULT
  531.               Standard result code. NOERROR for success.
  532. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  533. HRESULT CGuiPaper::InkWidth(
  534.                      SHORT nInkWidth)
  535. {
  536.   if (nInkWidth)
  537.     m_nInkWidth = nInkWidth;
  538.  
  539.   return NOERROR;
  540. }
  541.  
  542.  
  543. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  544.   Method:   CGuiPaper::InkColor
  545.  
  546.   Summary:  Changes current ink color.
  547.  
  548.   Args:     COLORREF crInkColor
  549.               RGB color ref value (eg, RGB(0,0,0) is black).
  550.  
  551.   Modifies: ...
  552.  
  553.   Returns:  HRESULT
  554.               Standard result code. NOERROR for success.
  555. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  556. HRESULT CGuiPaper::InkColor(
  557.                      COLORREF crInkColor)
  558. {
  559.   m_crInkColor = crInkColor;
  560.  
  561.   return NOERROR;
  562. }
  563.  
  564.  
  565. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  566.   Method:   CGuiPaper::InkStart
  567.  
  568.   Summary:  Starts an ink drawing sequence in the current color.
  569.  
  570.   Args:     SHORT nX,
  571.               X coordinate in window rectangle of start point.
  572.             SHORT nY
  573.               Y coordinate in window rectangle of start point.
  574.  
  575.   Modifies: ...
  576.  
  577.   Returns:  HRESULT
  578.               Standard result code. NOERROR for success.
  579. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  580. HRESULT CGuiPaper::InkStart(
  581.                      SHORT nX,
  582.                      SHORT nY)
  583. {
  584.   HRESULT hr = E_FAIL;
  585.  
  586.   if (m_nLockKey || (!m_nLockKey && !m_bInkSaving))
  587.   {
  588.     // Start an ink drawing sequence only if one is not in progress.
  589.     if (!m_bInking)
  590.     {
  591.       // Remember start position.
  592.       m_OldPos.x = nX;
  593.       m_OldPos.y = nY;
  594.  
  595.       // Delete old pen.
  596.       if (m_hPen)
  597.         DeleteObject(m_hPen);
  598.  
  599.       // Create and select the new drawing pen.
  600.       m_hPen = CreatePen(PS_SOLID, m_nInkWidth, m_crInkColor);
  601.       SelectObject(m_hDC, m_hPen);
  602.  
  603.       hr = NOERROR;
  604.  
  605.       // Ask the Paper object to mark the start of the ink drawing
  606.       // sequence in the current ink color.
  607.       if (m_pIPaper && m_bInkSaving)
  608.       {
  609.         hr = m_pIPaper->InkStart(
  610.                           m_nLockKey,
  611.                           nX,
  612.                           nY,
  613.                           m_nInkWidth,
  614.                           m_crInkColor);
  615.         // Capture the Mouse.
  616.         SetCapture(m_hWnd);
  617.  
  618.         // We've modified the ink data--it is now "dirty" with
  619.         // respect to the compound file image. Set dirty flag.
  620.         m_bDirty = TRUE;
  621.       }
  622.  
  623.       // Set inking flag to TRUE.
  624.       m_bInking = TRUE;
  625.     }
  626.   }
  627.  
  628.   return hr;
  629. }
  630.  
  631.  
  632. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  633.   Method:   CGuiPaper::InkDraw
  634.  
  635.   Summary:  Draws and saves ink data during the currently active ink
  636.             drawing sequence.
  637.  
  638.   Args:     SHORT nX,
  639.               X coordinate in window rectangle of start point.
  640.             SHORT nY
  641.               Y coordinate in window rectangle of start point.
  642.  
  643.   Modifies: ...
  644.  
  645.   Returns:  HRESULT
  646.               Standard result code. NOERROR for success.
  647. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  648. HRESULT CGuiPaper::InkDraw(
  649.                      SHORT nX,
  650.                      SHORT nY)
  651. {
  652.   if (m_bInking)
  653.   {
  654.     // Start this ink line at previous old position.
  655.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  656.  
  657.     // Assign new old position and draw the new line.
  658.     LineTo(m_hDC, m_OldPos.x = nX, m_OldPos.y = nY);
  659.  
  660.     // Ask the Paper object to save this data.
  661.     if (m_bInkSaving)
  662.       m_pIPaper->InkDraw(m_nLockKey, nX, nY);
  663.   }
  664.  
  665.   return NOERROR;
  666. }
  667.  
  668.  
  669. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  670.   Method:   CGuiPaper::InkStop
  671.  
  672.   Summary:  Stops the currently active ink drawing sequence.
  673.  
  674.   Args:     SHORT nX,
  675.               X coordinate in window rectangle of start point.
  676.             SHORT nY
  677.               Y coordinate in window rectangle of start point.
  678.  
  679.   Modifies: ...
  680.  
  681.   Returns:  HRESULT
  682.               Standard result code. NOERROR for success.
  683. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  684. HRESULT CGuiPaper::InkStop(
  685.                      SHORT nX,
  686.                      SHORT nY)
  687. {
  688.   if (m_bInking)
  689.   {
  690.     // Start this ink line at previous old position.
  691.     MoveToEx(m_hDC, m_OldPos.x, m_OldPos.y, NULL);
  692.  
  693.     // Draw the last line.
  694.     LineTo(m_hDC, nX, nY);
  695.  
  696.     // Turn off inking.
  697.     m_bInking = FALSE;
  698.  
  699.     // Ask the Paper object to mark the stop of the ink drawing sequence.
  700.     if (m_bInkSaving)
  701.     {
  702.       m_pIPaper->InkStop(m_nLockKey, nX, nY);
  703.       // Free the mouse.
  704.       ReleaseCapture();
  705.     }
  706.   }
  707.  
  708.   return NOERROR;
  709. }
  710.  
  711.  
  712. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  713.   Method:   CGuiPaper::GetConnectionPoint
  714.  
  715.   Summary:  Internal private method to obtain a connection point interface.
  716.  
  717.   Args:     REFIID riid
  718.               IID of the requested connection point Interface.
  719.  
  720.   Modifies: ...
  721.  
  722.   Returns:  IConnectionPoint*
  723.               Requested IConnectionPoint interface pointer. NULL if none.
  724. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  725. IConnectionPoint* CGuiPaper::GetConnectionPoint(
  726.                     REFIID riid)
  727. {
  728.   IConnectionPoint* pConnPoint = NULL;
  729.   IConnectionPointContainer* pConnPointContainer = NULL;
  730.   IConnectionPoint* pConnPt;
  731.   HRESULT hr;
  732.  
  733.   // First query the object for its Connection Point Container. This
  734.   // essentially asks the object in the server if it is connectable.
  735.   hr = m_pIPaper->QueryInterface(
  736.          IID_IConnectionPointContainer,
  737.          (PPVOID)&pConnPointContainer);
  738.   if SUCCEEDED(hr)
  739.   {
  740.     // Find the requested Connection Point. This AddRef's the
  741.     // returned pointer.
  742.     hr = pConnPointContainer->FindConnectionPoint(riid, &pConnPt);
  743.     if (SUCCEEDED(hr))
  744.       pConnPoint = pConnPt;
  745.  
  746.     RELEASE_INTERFACE(pConnPointContainer);
  747.   }
  748.  
  749.   return pConnPoint;
  750. }
  751.  
  752.  
  753. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  754.   Method:   CGuiPaper::ConnectPaperSink
  755.  
  756.   Summary:  Connect the PaperSink to the server COPaper source.
  757.  
  758.   Args:     void
  759.  
  760.   Modifies: ...
  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 CGuiPaper::ConnectPaperSink(void)
  766. {
  767.   HRESULT hr = E_FAIL;
  768.   DWORD dwKey;
  769.   IConnectionPoint* pConnPoint;
  770.  
  771.   if (!m_dwPaperSink)
  772.   {
  773.     // Get the Paper Sink connection point.
  774.     pConnPoint = GetConnectionPoint(IID_IPaperSink);
  775.     if (NULL != pConnPoint)
  776.     {
  777.       // Connect the object in the server to the Paper Sink in this client.
  778.       hr = pConnPoint->Advise(m_pCOPaperSink, &dwKey);
  779.       if (SUCCEEDED(hr))
  780.         m_dwPaperSink = dwKey;
  781.  
  782.       RELEASE_INTERFACE(pConnPoint);
  783.     }
  784.   }
  785.  
  786.   return hr;
  787. }
  788.  
  789.  
  790. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  791.   Method:   CGuiPaper::DisconnectPaperSink
  792.  
  793.   Summary:  Disconnect the PaperSink from the server COPaper source.
  794.  
  795.   Args:     void.
  796.  
  797.   Modifies: ...
  798.  
  799.   Returns:  HRESULT
  800.               Standard result code. NOERROR for success.
  801. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  802. HRESULT CGuiPaper::DisconnectPaperSink(void)
  803. {
  804.   HRESULT hr = E_FAIL;
  805.   IConnectionPoint* pConnPoint;
  806.  
  807.   if (m_dwPaperSink)
  808.   {
  809.     // Get the Paper Sink connection point.
  810.     pConnPoint = GetConnectionPoint(IID_IPaperSink);
  811.     if (NULL != pConnPoint)
  812.     {
  813.       // Disconnect the object in the server from the Paper Sink in
  814.       // this client.
  815.       hr = pConnPoint->Unadvise(m_dwPaperSink);
  816.       if (SUCCEEDED(hr))
  817.         m_dwPaperSink = 0;
  818.  
  819.       RELEASE_INTERFACE(pConnPoint);
  820.     }
  821.   }
  822.  
  823.   return hr;
  824. }
  825.  
  826.  
  827. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  828.   Method:   CGuiPaper::Load
  829.  
  830.   Summary:  Load paper data from the current compound file.
  831.  
  832.   Args:     void.
  833.  
  834.   Modifies: ...
  835.  
  836.   Returns:  HRESULT
  837.               Standard result code. NOERROR for success.
  838. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  839. HRESULT CGuiPaper::Load(void)
  840. {
  841.   HRESULT hr = E_FAIL;
  842.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  843.   HCURSOR hCurPrev;
  844.   TCHAR szTitle[MAX_PATH + 32];
  845.  
  846.   if (NULL != m_pPapFile)
  847.   {
  848.     // Change cursor to the hour glass.
  849.     hCurPrev = SetCursor(hCurWait);
  850.  
  851.     // Ask the PapFile object to load the paper data from current compound
  852.     // file.
  853.     hr = m_pPapFile->Load(m_nLockKey, NULL);
  854.     if (SUCCEEDED(hr))
  855.     {
  856.       // Set Main Window Title.
  857.       lstrcpy(szTitle, TEXT(MAIN_APP_NAME_STR));
  858.       lstrcat(szTitle, m_szFileTitle);
  859.       SetWindowText(m_hWnd, szTitle);
  860.       m_bDirty = FALSE;
  861.     }
  862.  
  863.     // Set Cursor back to what it was.
  864.     SetCursor(hCurPrev);
  865.   }
  866.  
  867.   return hr;
  868. }
  869.  
  870.  
  871. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  872.   Method:   CGuiPaper::Save
  873.  
  874.   Summary:  Calls on CPapFile to save the current drawing's paper data in
  875.             the current compound file.
  876.  
  877.   Args:     void.
  878.  
  879.   Modifies: ...
  880.  
  881.   Returns:  HRESULT
  882.               Standard result code. NOERROR for success.
  883. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  884. HRESULT CGuiPaper::Save(void)
  885. {
  886.   HRESULT hr = E_FAIL;
  887.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  888.   HCURSOR hCurPrev;
  889.  
  890.   if (NULL != m_pPapFile)
  891.   {
  892.     // Change cursor to the hour glass.
  893.     hCurPrev = SetCursor(hCurWait);
  894.  
  895.     // Ask the PapFile object to save itself. NULL for file name means
  896.     // save current file.
  897.     hr = m_pPapFile->Save(m_nLockKey, NULL);
  898.     if (SUCCEEDED(hr))
  899.       m_bDirty = FALSE;
  900.  
  901.     // Set Cursor back to what it was.
  902.     SetCursor(hCurPrev);
  903.   }
  904.  
  905.   return hr;
  906. }
  907.  
  908.  
  909. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  910.   Method:   CGuiPaper::AskSave
  911.  
  912.   Summary:  Checks dirty flag (ie, if current displayed paper data was
  913.             modified and is out of sync with the paper data stored in a
  914.             compound file). If dirty, then ask user in simple dialog if he
  915.             wants to save new data. If he says yes, then save the current
  916.             paper data into the current compound file.
  917.  
  918.   Args:     void.
  919.  
  920.   Returns:  INT
  921.               Value returned from the Win32 MessageBox function.
  922. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  923. INT CGuiPaper::AskSave(void)
  924. {
  925.   int iAns = IDNO;
  926.   TCHAR szTitle[MAX_STRING_LENGTH];
  927.   TCHAR szAsk[MAX_STRING_LENGTH];
  928.   TCHAR szMsg[MAX_PATH + MAX_STRING_LENGTH];
  929.  
  930.   // If current data is dirty then ask user if he wants to save it.
  931.   if (m_bDirty)
  932.   {
  933.     if (LoadString(m_hInst, IDS_DRAWING_CHANGED, szTitle, MAX_STRING_LENGTH)
  934.         && LoadString(m_hInst, IDS_ASK_SAVE, szAsk, MAX_STRING_LENGTH))
  935.     {
  936.       lstrcpy(szMsg, m_szFileName);
  937.       lstrcat(szMsg, szAsk);
  938.       // Display AskSaveDlg to user. Ask if he wants to save.
  939.       iAns = MessageBox(
  940.                m_hWnd,
  941.                szMsg,
  942.                szTitle,
  943.                MB_YESNOCANCEL | MB_ICONEXCLAMATION);
  944.       if (IDYES == iAns)
  945.       {
  946.         // Tell CPapFile to save itself to the current compound file.
  947.         Save();
  948.       }
  949.     }
  950.   }
  951.  
  952.   return iAns;
  953. }
  954.  
  955.  
  956. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  957.   Method:   CGuiPaper::Open
  958.  
  959.   Summary:  If current data has been modified then ask user if he wants to
  960.             first save it to the current file. Then use Open File Name
  961.             common dialog to ask user for a new file name to load. If user
  962.             chooses a file name then load the paper data from that
  963.             compound file.
  964.  
  965.   Args:     void.
  966.  
  967.   Modifies: ...
  968.  
  969.   Returns:  HRESULT
  970.               Standard result code. NOERROR for success.
  971. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  972. HRESULT CGuiPaper::Open(void)
  973. {
  974.   HRESULT hr = NOERROR;
  975.   TCHAR szFileName[MAX_PATH];
  976.   TCHAR szTitle[MAX_PATH];
  977.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  978.   HCURSOR hCurPrev;
  979.   BOOL bOpen;
  980.  
  981.   // If current data is dirty then ask user if he wants to save it first.
  982.   if (IDCANCEL != AskSave())
  983.   {
  984.     // Use the Open File Name common dialog to get file name from user.
  985.     // Set the dialog's file filter and title.
  986.     szFileName[0] = 0;
  987.     m_ofnFile.lpstrFile = szFileName;
  988.     if (LoadString(m_hInst, IDS_OFN_PAPOPEN, szTitle, sizeof(szTitle)))
  989.       m_ofnFile.lpstrTitle = szTitle;
  990.     m_ofnFile.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST;
  991.  
  992.     // Call up the dialog to get a file name from the user.
  993.     m_bPainting = TRUE;
  994.     bOpen = GetOpenFileName(&m_ofnFile);
  995.     m_bPainting = FALSE;
  996.     if (bOpen)
  997.     {
  998.       // Change cursor to the hour glass.
  999.       hCurPrev = SetCursor(hCurWait);
  1000.  
  1001.       // Tell CPapFile object to load the paper data from the newly
  1002.       // chosen compound file.
  1003.       hr = m_pPapFile->Load(m_nLockKey, szFileName);
  1004.       if (SUCCEEDED(hr))
  1005.       {
  1006.         // Remember the new current compound file name.
  1007.         lstrcpy(m_szFileName, szFileName);
  1008.  
  1009.         // Set Main Window Title.
  1010.         lstrcpy(szFileName, TEXT(MAIN_APP_NAME_STR));
  1011.         lstrcat(szFileName, m_szFileTitle);
  1012.         SetWindowText(m_hWnd, szFileName);
  1013.  
  1014.         m_bDirty = FALSE;
  1015.       }
  1016.  
  1017.       // Set Cursor back to what it was.
  1018.       SetCursor(hCurPrev);
  1019.     }
  1020.   }
  1021.  
  1022.   return hr;
  1023. }
  1024.  
  1025.  
  1026. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1027.   Method:   CGuiPaper::SaveAs
  1028.  
  1029.   Summary:  Use the File SaveAs common dialog to ask user for a new file
  1030.             name to save to. Then save the current paper data to the
  1031.             specified compound file. That file becomes the new current
  1032.             file. The previously open file is left unchanged since the
  1033.             last save to it.
  1034.  
  1035.   Args:     void.
  1036.  
  1037.   Modifies: ...
  1038.  
  1039.   Returns:  HRESULT
  1040.               Standard result code. NOERROR for success.
  1041. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1042. HRESULT CGuiPaper::SaveAs(void)
  1043. {
  1044.   HRESULT hr = E_FAIL;
  1045.   TCHAR szFileName[MAX_PATH];
  1046.   TCHAR szTitle[MAX_PATH];
  1047.   HCURSOR hCurWait = LoadCursor(NULL, IDC_WAIT);
  1048.   HCURSOR hCurPrev;
  1049.   BOOL bOpen;
  1050.  
  1051.   // Use the File Save As common dialog to get file name from user.
  1052.   // Set the dialog's file filter and title.
  1053.   szFileName[0] = 0;
  1054.   m_ofnFile.lpstrFile = szFileName;
  1055.   if (LoadString(m_hInst, IDS_OFN_PAPSAVE, szTitle, sizeof(szTitle)))
  1056.     m_ofnFile.lpstrTitle = szTitle;
  1057.   m_ofnFile.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
  1058.  
  1059.   // Call up the dialog to get a file name from the user.
  1060.   m_bPainting = TRUE;
  1061.   bOpen = GetSaveFileName(&m_ofnFile);
  1062.   m_bPainting = FALSE;
  1063.   if (bOpen)
  1064.   {
  1065.     // Change cursor to the hour glass.
  1066.     hCurPrev = SetCursor(hCurWait);
  1067.  
  1068.     // Tell CPapFile object to save the paper data to the newly
  1069.     // chosen compound file.
  1070.     hr = m_pPapFile->Save(m_nLockKey, szFileName);
  1071.     if (SUCCEEDED(hr))
  1072.     {
  1073.       // Remember the new current compound file name.
  1074.       lstrcpy(m_szFileName, szFileName);
  1075.  
  1076.       // Set Main Window Title.
  1077.       lstrcpy(szFileName, TEXT(MAIN_APP_NAME_STR));
  1078.       lstrcat(szFileName, m_szFileTitle);
  1079.       SetWindowText(m_hWnd, szFileName);
  1080.  
  1081.       m_bDirty = FALSE;
  1082.     }
  1083.  
  1084.     // Set Cursor back to what it was.
  1085.     SetCursor(hCurPrev);
  1086.   }
  1087.  
  1088.   return hr;
  1089. }
  1090.  
  1091.  
  1092. /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M
  1093.   Method:   CGuiPaper::PickColor
  1094.  
  1095.   Summary:  Uses the Choose Color common dialog to ask user for new
  1096.             Pen color. Return that new color.
  1097.  
  1098.   Args:     void.
  1099.  
  1100.   Returns:  COLORREF
  1101.               New chosen color.
  1102. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/
  1103. COLORREF CGuiPaper::PickColor(void)
  1104. {
  1105.   COLORREF crNewColor = m_crInkColor;
  1106.   SHORT i;
  1107.  
  1108.   // Init the custom color array with gray colors.
  1109.   for (i=0; i<16; i++)
  1110.     m_acrCustColors[i] = RGB(i*16, i*16, i*16);
  1111.  
  1112.   // Init the Choose Color structure.
  1113.   m_ChooseColor.lStructSize = sizeof(CHOOSECOLOR);
  1114.   m_ChooseColor.hwndOwner = m_hWnd;
  1115.   m_ChooseColor.hInstance = (HWND) m_hInst;
  1116.   m_ChooseColor.rgbResult = m_crInkColor;
  1117.   m_ChooseColor.lpCustColors = (DWORD*) m_acrCustColors;
  1118.   m_ChooseColor.Flags = CC_PREVENTFULLOPEN | CC_RGBINIT;
  1119.   m_ChooseColor.lCustData = 0L;
  1120.   m_ChooseColor.lpfnHook = NULL;
  1121.   m_ChooseColor.lpTemplateName = NULL;
  1122.  
  1123.   if (ChooseColor(&m_ChooseColor))
  1124.     crNewColor = m_ChooseColor.rgbResult;
  1125.  
  1126.   return crNewColor;
  1127. }
  1128.